diff --git a/.gitignore b/.gitignore index 67f5b41b67df..9fded7174921 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,4 @@ /libpeerconnection.log npm-debug.log testem.log -/.chrome \ No newline at end of file +/.chrome diff --git a/CHANGELOG.md b/CHANGELOG.md index 98e5bacf9ec6..20213136d1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,166 @@ + +# [2.0.0-beta.2 flannel-papaya](https://github.com/angular/material2/compare/2.0.0-beta.1...2.0.0-beta.2) (2017-02-15) + +### Breaking changes from beta.1 +* Styling is no longer prefixed by `md-`. All styling is now prefixed by `mat-` so that apps can upgrade from AngularJS Material to Angular Material without styling conflicts between the two library components. +See ([#2790](https://github.com/angular/material2/issues/2790)) for the details on the code change and some useful regular expressions that can help migrate styles. +* Checkbox tab index @Input has been changed from `tabindex` to `tabIndex`. ([#2953](https://github.com/angular/material2/issues/2953)) +* Ripple no longer has the `mdRippleBackgroundColor` input to change the background color. ([#2859](https://github.com/angular/material2/issues/2859)) +* The deprecated use of `` and `` has been removed. Use `mdInput` on an input or textarea within a `md-input-container`. `md-prefix` and `md-suffix` are now `mdPrefix` and `mdSuffix`. ([#2788](https://github.com/angular/material2/issues/2788)) + + ```html + + + + ``` + +* The deprecated use of `` has been removed. Use `` instead. ([#2283](https://github.com/angular/material2/issues/2283)) +* Input floating placeholder @Input has changed from a boolean (`true` and `false`) to a state (`always`, `never`, and `auto`) and was renamed from `floatingPlaceholder` to `floatPlaceholder`. For details on when to use which state, see ([#2585](https://github.com/angular/material2/issues/2585)) +* The use of Module `forRoot` has been deprecated and will be removed in the next release. Instead, just simply import MaterialModule directly: + + ```ts + @NgModule({ + imports: [ + ... + MaterialModule, + ... + ] + ... + }); + ``` + +### Bug Fixes + +* **autocomplete:** add mat version of autocomplete [@Input](https://github.com/Input) ([#2928](https://github.com/angular/material2/issues/2928)) ([e5521a8](https://github.com/angular/material2/commit/e5521a8)) +* **autocomplete:** allow basic use without forms directives ([#2958](https://github.com/angular/material2/issues/2958)) ([4ee2980](https://github.com/angular/material2/commit/4ee2980)) +* **autocomplete:** close panel when options list is empty ([#2834](https://github.com/angular/material2/issues/2834)) ([8a3b6fd](https://github.com/angular/material2/commit/8a3b6fd)) +* **autocomplete:** double-clicking input shouldnt close the panel ([#2835](https://github.com/angular/material2/issues/2835)) ([18969f4](https://github.com/angular/material2/commit/18969f4)) +* **autocomplete:** hide instead of close when options empty ([#2997](https://github.com/angular/material2/issues/2997)) ([a022035](https://github.com/angular/material2/commit/a022035)) +* **autocomplete:** placeholder should float while panel is open ([#2730](https://github.com/angular/material2/issues/2730)) ([eec4dc6](https://github.com/angular/material2/commit/eec4dc6)) +* **autocomplete:** scroll options below fold into view ([#2728](https://github.com/angular/material2/issues/2728)) ([6c84603](https://github.com/angular/material2/commit/6c84603)) +* **autocomplete:** support rtl ([#2648](https://github.com/angular/material2/issues/2648)) ([4f59ad0](https://github.com/angular/material2/commit/4f59ad0)) +* **autocomplete:** up arrow should set last item active ([#2776](https://github.com/angular/material2/issues/2776)) ([fd5e4d9](https://github.com/angular/material2/commit/fd5e4d9)) +* **autosize:** export md-autosize directive ([#2432](https://github.com/angular/material2/issues/2432)) ([f2d73da](https://github.com/angular/material2/commit/f2d73da)), closes [#2419](https://github.com/angular/material2/issues/2419) +* **button:** add default color for mat-raised-button ([#3052](https://github.com/angular/material2/issues/3052)) ([6fe1d9a](https://github.com/angular/material2/commit/6fe1d9a)) +* **button:** only flat button and icon buttons should inherit the color ([#2561](https://github.com/angular/material2/issues/2561)) ([ac363df](https://github.com/angular/material2/commit/ac363df)), closes [#2539](https://github.com/angular/material2/issues/2539) +* **button:** raised buttons in dark theme ([#3070](https://github.com/angular/material2/issues/3070)) ([87ab712](https://github.com/angular/material2/commit/87ab712)) +* **button:** reuse _getHostElement() to avoid redundant elementRef.nativeElement calls ([#2625](https://github.com/angular/material2/issues/2625)) ([c7d1c17](https://github.com/angular/material2/commit/c7d1c17)) +* **button-toggle:** add the setDisabledState from ControlValueAccessor ([#2430](https://github.com/angular/material2/issues/2430)) ([fb750b4](https://github.com/angular/material2/commit/fb750b4)) +* **button-toggle:** conflict with radio component ([#2343](https://github.com/angular/material2/issues/2343)) ([9e99374](https://github.com/angular/material2/commit/9e99374)), closes [#2274](https://github.com/angular/material2/issues/2274) +* **button-toggle:** make conform with design specs ([#2570](https://github.com/angular/material2/issues/2570)) ([fed5d7b](https://github.com/angular/material2/commit/fed5d7b)) +* **card:** fix padding for md-card-actions in xs screens ([#2567](https://github.com/angular/material2/issues/2567)) ([ad0df31](https://github.com/angular/material2/commit/ad0df31)) +* **checkbox:** Emit event when checkbox's indeterminate value changes ([#2130](https://github.com/angular/material2/issues/2130)) ([f11c5eb](https://github.com/angular/material2/commit/f11c5eb)) +* **checkbox:** rename tabindex to tabIndex ([#2953](https://github.com/angular/material2/issues/2953)) ([b91964a](https://github.com/angular/material2/commit/b91964a)) +* **checkbox:** ripple color does not change ([#2857](https://github.com/angular/material2/issues/2857)) ([7ac29f8](https://github.com/angular/material2/commit/7ac29f8)) +* **checkbox, radio:** not using theme border color ([#2744](https://github.com/angular/material2/issues/2744)) ([07ec765](https://github.com/angular/material2/commit/07ec765)) +* **compatibility:** add missing mat- selectors ([#2923](https://github.com/angular/material2/issues/2923)) ([f29f7ab](https://github.com/angular/material2/commit/f29f7ab)) +* **connected-position-strategy:** wrong logic when determining whether element is on screen ([#2677](https://github.com/angular/material2/issues/2677)) ([e055d05](https://github.com/angular/material2/commit/e055d05)), closes [#2102](https://github.com/angular/material2/issues/2102) [#2658](https://github.com/angular/material2/issues/2658) +* **dialog:** escape key not working once element loses focus ([#3082](https://github.com/angular/material2/issues/3082)) ([a08dc55](https://github.com/angular/material2/commit/a08dc55)), closes [#3009](https://github.com/angular/material2/issues/3009) +* **dialog:** prevent error when restoring focus on IE ([#2771](https://github.com/angular/material2/issues/2771)) ([153fcd3](https://github.com/angular/material2/commit/153fcd3)), closes [#2760](https://github.com/angular/material2/issues/2760) +* **dialog:** prevent the close button from submitting forms ([#2659](https://github.com/angular/material2/issues/2659)) ([29f939a](https://github.com/angular/material2/commit/29f939a)), closes [#2599](https://github.com/angular/material2/issues/2599) +* **dialog:** use injector from viewContainerRef if provided ([#2655](https://github.com/angular/material2/issues/2655)) ([be0da09](https://github.com/angular/material2/commit/be0da09)) +* **docs:** properly create links in guide files ([#2770](https://github.com/angular/material2/issues/2770)) ([60f03ed](https://github.com/angular/material2/commit/60f03ed)) +* **icon:** add caching of md-icon aria-label ([#2649](https://github.com/angular/material2/issues/2649)) ([08e9d70](https://github.com/angular/material2/commit/08e9d70)), closes [#2642](https://github.com/angular/material2/issues/2642) +* **input:** add more padding so that the hint doesn't overflow the container ([#2246](https://github.com/angular/material2/issues/2246)) ([d7831d9](https://github.com/angular/material2/commit/d7831d9)) +* **input:** camel-case md-prefix and md-suffix ([#2639](https://github.com/angular/material2/issues/2639)) ([7562322](https://github.com/angular/material2/commit/7562322)), closes [#2636](https://github.com/angular/material2/issues/2636) +* **input:** disable underline with reactive forms ([#2565](https://github.com/angular/material2/issues/2565)) ([f9dd34f](https://github.com/angular/material2/commit/f9dd34f)), closes [#2558](https://github.com/angular/material2/issues/2558) +* **input:** disabled inputs should be grayed out ([#2513](https://github.com/angular/material2/issues/2513)) ([ed3ffe0](https://github.com/angular/material2/commit/ed3ffe0)) +* **input:** ensure that property bindings work ([#2431](https://github.com/angular/material2/issues/2431)) ([b4b4224](https://github.com/angular/material2/commit/b4b4224)), closes [#2428](https://github.com/angular/material2/issues/2428) +* **input:** fix chrome 56 warning ([#2906](https://github.com/angular/material2/issues/2906)) ([62189a3](https://github.com/angular/material2/commit/62189a3)) +* **input:** fix placeholder for number input with bad input. ([#2362](https://github.com/angular/material2/issues/2362)) ([52aa715](https://github.com/angular/material2/commit/52aa715)) +* **input:** hints not being read out by screen readers ([#2856](https://github.com/angular/material2/issues/2856)) ([f899b5f](https://github.com/angular/material2/commit/f899b5f)), closes [#2798](https://github.com/angular/material2/issues/2798) +* **input:** horizontal overflow in IE and Edge ([#2784](https://github.com/angular/material2/issues/2784)) ([e0fe635](https://github.com/angular/material2/commit/e0fe635)) +* **input:** properly determine input value ([#2455](https://github.com/angular/material2/issues/2455)) ([3a11927](https://github.com/angular/material2/commit/3a11927)), closes [#2441](https://github.com/angular/material2/issues/2441) [#2363](https://github.com/angular/material2/issues/2363) +* **input:** remove md-input and md-textarea in favor of md-input-container ([#2788](https://github.com/angular/material2/issues/2788)) ([7b30fdc](https://github.com/angular/material2/commit/7b30fdc)) +* **input:** vendor-prefix ::placeholder ([#2547](https://github.com/angular/material2/issues/2547)) ([3b16648](https://github.com/angular/material2/commit/3b16648)) +* **input-container:** prefix and suffix stretching together with parent ([#2496](https://github.com/angular/material2/issues/2496)) ([64f6d1b](https://github.com/angular/material2/commit/64f6d1b)), closes [#2493](https://github.com/angular/material2/issues/2493) [#1881](https://github.com/angular/material2/issues/1881) [#1421](https://github.com/angular/material2/issues/1421) +* **input-container:** reduce redundancy when forwarding the NgControl classes ([#2442](https://github.com/angular/material2/issues/2442)) ([8c0eef2](https://github.com/angular/material2/commit/8c0eef2)) +* **option:** revert duplicate prop ([#3051](https://github.com/angular/material2/issues/3051)) ([516720f](https://github.com/angular/material2/commit/516720f)) +* **overlay:** disable pointer events if overlay is detached ([#2747](https://github.com/angular/material2/issues/2747)) ([453fa7f](https://github.com/angular/material2/commit/453fa7f)), closes [#2739](https://github.com/angular/material2/issues/2739) +* **overlay:** fix pointer events for ie11 ([#3023](https://github.com/angular/material2/issues/3023)) ([597e3de](https://github.com/angular/material2/commit/597e3de)), closes [#3022](https://github.com/angular/material2/issues/3022) +* **progress-bar:** buffer animation not working in IE ([#2941](https://github.com/angular/material2/issues/2941)) ([ab8f98f](https://github.com/angular/material2/commit/ab8f98f)), closes [#2881](https://github.com/angular/material2/issues/2881) +* **progress-bar:** unable to apply visibility in indeterminate mode and reduce CSS ([#2417](https://github.com/angular/material2/issues/2417)) ([eb96b0c](https://github.com/angular/material2/commit/eb96b0c)), closes [#2413](https://github.com/angular/material2/issues/2413) +* **progress-spinner:** fix color input on md-spinner ([#2396](https://github.com/angular/material2/issues/2396)) ([6cb6576](https://github.com/angular/material2/commit/6cb6576)), closes [#2393](https://github.com/angular/material2/issues/2393) +* **radio:** change radio button trigger element to input element ([#2838](https://github.com/angular/material2/issues/2838)) ([2f10a95](https://github.com/angular/material2/commit/2f10a95)) +* **ripple:** camel-cased CSS classes ([#2340](https://github.com/angular/material2/issues/2340)) ([c67f4e5](https://github.com/angular/material2/commit/c67f4e5)) +* **ripple:** make ripples conform with specs ([#2859](https://github.com/angular/material2/issues/2859)) ([6381948](https://github.com/angular/material2/commit/6381948)) +* **select:** avoid going into infinite loop under certain conditions ([#2955](https://github.com/angular/material2/issues/2955)) ([998a583](https://github.com/angular/material2/commit/998a583)), closes [#2950](https://github.com/angular/material2/issues/2950) +* **select:** don't open menu if there are no options ([#2924](https://github.com/angular/material2/issues/2924)) ([cc77ef4](https://github.com/angular/material2/commit/cc77ef4)) +* **select:** fix select panel animation ([#2699](https://github.com/angular/material2/issues/2699)) ([15eb33a](https://github.com/angular/material2/commit/15eb33a)), closes [#2695](https://github.com/angular/material2/issues/2695) +* **select:** fix selection color ([#2697](https://github.com/angular/material2/issues/2697)) ([4e94da4](https://github.com/angular/material2/commit/4e94da4)), closes [#2696](https://github.com/angular/material2/issues/2696) +* **select:** selected option not being highlighted when options are added asynchronously ([#2499](https://github.com/angular/material2/issues/2499)) ([7fc38b9](https://github.com/angular/material2/commit/7fc38b9)), closes [#2497](https://github.com/angular/material2/issues/2497) +* **select:** set default font size ([#2976](https://github.com/angular/material2/issues/2976)) ([40bc486](https://github.com/angular/material2/commit/40bc486)) +* **select:** set select value to trigger height and center text ([#3021](https://github.com/angular/material2/issues/3021)) ([ac9c090](https://github.com/angular/material2/commit/ac9c090)) +* **select:** support use inside a custom value accessor ([#2704](https://github.com/angular/material2/issues/2704)) ([651440f](https://github.com/angular/material2/commit/651440f)), closes [#2609](https://github.com/angular/material2/issues/2609) +* **select:** transparent background when overscrolling ([#2117](https://github.com/angular/material2/issues/2117)) ([d9b2d85](https://github.com/angular/material2/commit/d9b2d85)) +* **select:** trim long labels inside md-option ([#2444](https://github.com/angular/material2/issues/2444)) ([416f56f](https://github.com/angular/material2/commit/416f56f)), closes [#2440](https://github.com/angular/material2/issues/2440) +* **select:** view not updating when using OnPush detection strategy ([#2894](https://github.com/angular/material2/issues/2894)) ([3bcb7c3](https://github.com/angular/material2/commit/3bcb7c3)), closes [#2663](https://github.com/angular/material2/issues/2663) [#2269](https://github.com/angular/material2/issues/2269) +* **select:** parent align affects placeholder ([#2572](https://github.com/angular/material2/issues/2572)) ([a1c90b3](https://github.com/angular/material2/commit/a1c90b3)) +* **sidenav:** animate content resizing for side mode. ([#2486](https://github.com/angular/material2/issues/2486)) ([4d33449](https://github.com/angular/material2/commit/4d33449)) +* **sidenav:** fix animation issue for initially open sidenav ([#3045](https://github.com/angular/material2/issues/3045)) ([37e4bad](https://github.com/angular/material2/commit/37e4bad)) +* **slide-toggle:** consistent naming of aria attributes ([#2688](https://github.com/angular/material2/issues/2688)) ([10bd6da](https://github.com/angular/material2/commit/10bd6da)) +* **slider:** fire change event on value change via keyboard. ([#2807](https://github.com/angular/material2/issues/2807)) ([7f50d11](https://github.com/angular/material2/commit/7f50d11)) +* **slider:** hide ticks when slider is disabled ([#2687](https://github.com/angular/material2/issues/2687)) ([e9ec8ab](https://github.com/angular/material2/commit/e9ec8ab)) +* apply font-family to text components ([#2821](https://github.com/angular/material2/issues/2821)) ([d11673a](https://github.com/angular/material2/commit/d11673a)) +* **slider:** make disabled state look like mocks ([#2604](https://github.com/angular/material2/issues/2604)) ([8263ffb](https://github.com/angular/material2/commit/8263ffb)) +* **slider:** make min value style match mocks ([#2641](https://github.com/angular/material2/issues/2641)) ([737b608](https://github.com/angular/material2/commit/737b608)) +* **slider:** round decimals in the thumb label ([#2527](https://github.com/angular/material2/issues/2527)) ([987897c](https://github.com/angular/material2/commit/987897c)), closes [#2511](https://github.com/angular/material2/issues/2511) +* **snack-bar:** improper button styling and improved handling of long text ([#2991](https://github.com/angular/material2/issues/2991)) ([93937e6](https://github.com/angular/material2/commit/93937e6)), closes [#2979](https://github.com/angular/material2/issues/2979) +* **snack-bar:** prevent error when opening multiple snack bars in fast succession ([#2392](https://github.com/angular/material2/issues/2392)) ([161f319](https://github.com/angular/material2/commit/161f319)), closes [#2390](https://github.com/angular/material2/issues/2390) +* **snack-bar:** SimpleSnackBar not being exported ([#3016](https://github.com/angular/material2/issues/3016)) ([a7a3967](https://github.com/angular/material2/commit/a7a3967)), closes [#3010](https://github.com/angular/material2/issues/3010) +* **tabs:** crashing on chrome under certain conditions ([#2411](https://github.com/angular/material2/issues/2411)) ([727ce53](https://github.com/angular/material2/commit/727ce53)), closes [#2151](https://github.com/angular/material2/issues/2151) +* **tabs:** fix ink not showing on chrome 57 ([#3041](https://github.com/angular/material2/issues/3041)) ([f24832c](https://github.com/angular/material2/commit/f24832c)) +* **tabs:** infinite loop when selectedIndex is set to NaN ([#2389](https://github.com/angular/material2/issues/2389)) ([f4cfc2d](https://github.com/angular/material2/commit/f4cfc2d)) +* **toolbar:** add toolbar role to host element ([#2914](https://github.com/angular/material2/issues/2914)) ([67032ca](https://github.com/angular/material2/commit/67032ca)), closes [#2909](https://github.com/angular/material2/issues/2909) +* **toolbar:** correct font-weight ([#2485](https://github.com/angular/material2/issues/2485)) ([1b44880](https://github.com/angular/material2/commit/1b44880)) +* **toolbar:** prevent content overflow and line-wrapping ([#2454](https://github.com/angular/material2/issues/2454)) ([e728771](https://github.com/angular/material2/commit/e728771)), closes [#2451](https://github.com/angular/material2/issues/2451) +* **tooltip:** better handling of multi-line text ([#2472](https://github.com/angular/material2/issues/2472)) ([7863e38](https://github.com/angular/material2/commit/7863e38)), closes [#2205](https://github.com/angular/material2/issues/2205) +* **tooltip:** not working properly with ChangeDetectionStrategy.OnPush ([#2721](https://github.com/angular/material2/issues/2721)) ([632b964](https://github.com/angular/material2/commit/632b964)), closes [#2713](https://github.com/angular/material2/issues/2713) +* **tooltip:** provide a maximum width ([#2678](https://github.com/angular/material2/issues/2678)) ([fb5e1d4](https://github.com/angular/material2/commit/fb5e1d4)), closes [#2671](https://github.com/angular/material2/issues/2671) + + +### Features + +* **autocomplete:** add autocomplete panel toggling ([#2452](https://github.com/angular/material2/issues/2452)) ([d4ab3d3](https://github.com/angular/material2/commit/d4ab3d3)) +* **autocomplete:** add fallback positions ([#2726](https://github.com/angular/material2/issues/2726)) ([8fc7706](https://github.com/angular/material2/commit/8fc7706)) +* **autocomplete:** add keyboard events to autocomplete ([#2723](https://github.com/angular/material2/issues/2723)) ([fcea9d4](https://github.com/angular/material2/commit/fcea9d4)) +* **autocomplete:** add screenreader support ([#2729](https://github.com/angular/material2/issues/2729)) ([bd7f240](https://github.com/angular/material2/commit/bd7f240)) +* **autocomplete:** add value support ([#2516](https://github.com/angular/material2/issues/2516)) ([5def001](https://github.com/angular/material2/commit/5def001)) +* **autocomplete:** allow use of obj values ([#2792](https://github.com/angular/material2/issues/2792)) ([55e1847](https://github.com/angular/material2/commit/55e1847)) +* **button-toggle:** Show selected option when md-button-toggle is disabled ([#3012](https://github.com/angular/material2/issues/3012)) ([1547440](https://github.com/angular/material2/commit/1547440)), closes [#3007](https://github.com/angular/material2/issues/3007) +* **compatibility:** remove conflicts with material1 css styling ([#2790](https://github.com/angular/material2/issues/2790)) ([210ff02](https://github.com/angular/material2/commit/210ff02)) +* **dialog:** add a config option for passing in data ([#2266](https://github.com/angular/material2/issues/2266)) ([29cbe61](https://github.com/angular/material2/commit/29cbe61)), closes [#2181](https://github.com/angular/material2/issues/2181) +* **dialog:** add events (observables) for open & closeAll ([#2522](https://github.com/angular/material2/issues/2522)) ([23ab152](https://github.com/angular/material2/commit/23ab152)) +* **dialog:** add the ability to align the content of md-dialog-actions ([#2557](https://github.com/angular/material2/issues/2557)) ([e18ab5d](https://github.com/angular/material2/commit/e18ab5d)), closes [#2483](https://github.com/angular/material2/issues/2483) +* **dialog:** support open with TemplateRef ([#2910](https://github.com/angular/material2/issues/2910)) ([bf0f625](https://github.com/angular/material2/commit/bf0f625)) +* **focus-classes:** expose focus origin changes through observable ([#2974](https://github.com/angular/material2/issues/2974)) ([d4ba648](https://github.com/angular/material2/commit/d4ba648)) +* **FocusOriginMonitor:** add support for touch events ([#3020](https://github.com/angular/material2/issues/3020)) ([ec7e2e4](https://github.com/angular/material2/commit/ec7e2e4)) +* **input:** option to imperatively float placeholder ([#2585](https://github.com/angular/material2/issues/2585)) ([fb0cf8a](https://github.com/angular/material2/commit/fb0cf8a)), closes [#2466](https://github.com/angular/material2/issues/2466) +* **list-key-manager:** active descendant support ([#2606](https://github.com/angular/material2/issues/2606)) ([e2ad3a0](https://github.com/angular/material2/commit/e2ad3a0)) +* **menu:** Added ability to show the menu overlay around the menu trigger ([#1771](https://github.com/angular/material2/issues/1771)) ([592f33f](https://github.com/angular/material2/commit/592f33f)) +* **overlay:** add fullscreen-enabled overlay class ([#1949](https://github.com/angular/material2/issues/1949)) ([0640302](https://github.com/angular/material2/commit/0640302)) +* **screenshot:** Add screenshot function to e2e test (button and checkbox) ([#2532](https://github.com/angular/material2/issues/2532)) ([8ba8deb](https://github.com/angular/material2/commit/8ba8deb)) +* **scripts:** push generated docs to material assets repo ([#2720](https://github.com/angular/material2/issues/2720)) ([ba12f44](https://github.com/angular/material2/commit/ba12f44)) +* **select:** emit change event ([#2458](https://github.com/angular/material2/issues/2458)) ([e5bd15c](https://github.com/angular/material2/commit/e5bd15c)), closes [#2248](https://github.com/angular/material2/issues/2248) +* **sidenav:** add disableClose option ([#2501](https://github.com/angular/material2/issues/2501)) ([52ade97](https://github.com/angular/material2/commit/52ade97)), closes [#2462](https://github.com/angular/material2/issues/2462) +* **slide-toggle:** add support for labelPosition ([#2836](https://github.com/angular/material2/issues/2836)) ([68a0c90](https://github.com/angular/material2/commit/68a0c90)), closes [#2820](https://github.com/angular/material2/issues/2820) +* **slider:** emit input event when slider thumb moves ([#2325](https://github.com/angular/material2/issues/2325)) ([99963c4](https://github.com/angular/material2/commit/99963c4)), closes [#2296](https://github.com/angular/material2/issues/2296) +* **snack-bar:** allow addition of extra css classes ([#2804](https://github.com/angular/material2/issues/2804)) ([e783494](https://github.com/angular/material2/commit/e783494)), closes [#2664](https://github.com/angular/material2/issues/2664) +* **style:** add directive to determine how elements were focused. ([#2646](https://github.com/angular/material2/issues/2646)) ([8a6d902](https://github.com/angular/material2/commit/8a6d902)) +* **tabs:** add the ability to invert the header ([#2391](https://github.com/angular/material2/issues/2391)) ([a494c92](https://github.com/angular/material2/commit/a494c92)), closes [#2387](https://github.com/angular/material2/issues/2387) +* **theming:** provide a content wrapper attribute ([#2170](https://github.com/angular/material2/issues/2170)) ([4bf4b87](https://github.com/angular/material2/commit/4bf4b87)), closes [#1938](https://github.com/angular/material2/issues/1938) [#2106](https://github.com/angular/material2/issues/2106) +* **tooltip:** reposition on scroll ([#2703](https://github.com/angular/material2/issues/2703)) ([bc52298](https://github.com/angular/material2/commit/bc52298)) +* **viewport-ruler:** cache document client rect ([#2538](https://github.com/angular/material2/issues/2538)) ([d0c8f18](https://github.com/angular/material2/commit/d0c8f18)) +* add a common class to be used when dealing with selection logic ([#2562](https://github.com/angular/material2/issues/2562)) ([c295fa9](https://github.com/angular/material2/commit/c295fa9)) +* add simplified checkbox component for usage in other components ([#2619](https://github.com/angular/material2/issues/2619)) ([3b6cab0](https://github.com/angular/material2/commit/3b6cab0)) +* remove the need for forRoot on material NgModules ([#2556](https://github.com/angular/material2/issues/2556)) ([b49bfce](https://github.com/angular/material2/commit/b49bfce)) + + +### Performance Improvements + +* **tabs:** reduce amount of reflows when aligning the ink bar ([#2372](https://github.com/angular/material2/issues/2372)) ([dab742f](https://github.com/angular/material2/commit/dab742f)) + + # [2.0.0-beta.1 rebar-teacup](https://github.com/angular/material2/compare/2.0.0-beta.0...2.0.0-beta.1) (2016-12-23) @@ -12,7 +175,7 @@ ### Breaking changes from alpha.11 * The `svgSrc` propert of `` has been removed. All SVG URLs must now be explicitly marked as trusted using Angular's `DomSanitizer` service. -* The `` element is deprecated in favor of ``. This new component +* The `` element is deprecated in favor of ``. This new component allows for direct access to the native input element. * All `@Input` properties have been changed to use their camelCase names for binding. The old names are still available as deprecated but will be removed in the next release. diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md index 5009fbee2b7f..25b69346a9e7 100644 --- a/CODING_STANDARDS.md +++ b/CODING_STANDARDS.md @@ -43,8 +43,8 @@ if (!$attrs['tabindex']) { For example, rather than doing this: ```html Basic button -FAB -pony +FAB +pony ``` do this: @@ -192,13 +192,13 @@ code organization.** This will allow users to much more easily override styles. For example, rather than doing this: ```scss -md-calendar { +.mat-calendar { display: block; - .md-month { + .mat-month { display: inline-block; - .md-date.md-selected { + .mat-date.mat-selected { font-weight: bold; } } @@ -207,15 +207,15 @@ md-calendar { do this: ```scss -md-calendar { +.mat-calendar { display: block; } -.md-calendar-month { +.mat-calendar-month { display: inline-block; } -.md-calendar-date.md-selected { +.mat-calendar-date.mat-selected { font-weight: bold; } ``` @@ -260,11 +260,11 @@ This is a low-effort task that makes a big difference for low-vision users. Exam When it is not super obvious, include a brief description of what a class represents. For example: ```scss // The calendar icon button used to open the calendar pane. -.md-datepicker-button { ... } +.mat-datepicker-button { ... } // Floating pane that contains the calendar at the bottom of the input. -.md-datepicker-calendar-pane { ... } +.mat-datepicker-calendar-pane { ... } // Portion of the floating panel that sits, invisibly, on top of the input. -.md-datepicker-input-mask { } +.mat-datepicker-input-mask { } ``` diff --git a/README.md b/README.md index de6da4c78b37..63b9db6cd2be 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Also see our [`Good for community contribution`](https://github.com/angular/mate label. High level items planned for January 2017: + * Initial version of md-autocomplete * Prototyping for data-table * Improvements to https://material.angular.io @@ -73,8 +74,8 @@ High level items planned for January 2017: | ripples | Available | [README][19] | [#108][0108] | | dialog | Available | [README][22] | [#114][0114] | | snackbar / toast | Available | [README][21] | [#115][0115] | -| select | Available | - | [#118][0118] | -| textarea | Available | - | - | +| select | Available | [README][23] | [#118][0118] | +| textarea | Available | [README][5] | - | | autocomplete | In-progress | - | [#117][0117] | | chips | Initial version, features evolving | - | [#120][0120] | | theming | Available, need guidance overlays | [Guide][20] | - | @@ -114,6 +115,7 @@ High level items planned for January 2017: [20]: https://github.com/angular/material2/blob/master/guides/theming.md [21]: https://github.com/angular/material2/blob/master/src/lib/snack-bar/README.md [22]: https://github.com/angular/material2/blob/master/src/lib/dialog/README.md +[23]: https://github.com/angular/material2/blob/master/src/lib/select/README.md [0107]: https://github.com/angular/material2/issues/107 [0119]: https://github.com/angular/material2/issues/119 diff --git a/e2e/components/button/button.e2e.ts b/e2e/components/button/button.e2e.ts index dd183358e118..91719f606425 100644 --- a/e2e/components/button/button.e2e.ts +++ b/e2e/components/button/button.e2e.ts @@ -1,4 +1,4 @@ -import {browser, by, element} from 'protractor'; +import {browser, by, element, ExpectedConditions} from 'protractor'; import {screenshot} from '../../screenshot'; @@ -9,12 +9,16 @@ describe('button', () => { it('should prevent click handlers from executing when disabled', () => { element(by.id('test-button')).click(); expect(element(by.id('click-counter')).getText()).toEqual('1'); - screenshot('clicked once'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('clicked once')); element(by.id('disable-toggle')).click(); element(by.id('test-button')).click(); expect(element(by.id('click-counter')).getText()).toEqual('1'); - screenshot('click disabled'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('click disabled')); }); }); }); diff --git a/e2e/components/checkbox/checkbox.e2e.ts b/e2e/components/checkbox/checkbox.e2e.ts index b6b1a255b4a1..8c81f63100b0 100644 --- a/e2e/components/checkbox/checkbox.e2e.ts +++ b/e2e/components/checkbox/checkbox.e2e.ts @@ -1,4 +1,4 @@ -import {browser, by, element, Key} from 'protractor'; +import {browser, by, element, Key, ExpectedConditions} from 'protractor'; import {screenshot} from '../../screenshot'; describe('checkbox', function () { @@ -17,14 +17,18 @@ describe('checkbox', function () { checkboxEl.click(); inputEl.getAttribute('checked').then((value: string) => { expect(value).toBeTruthy('Expect checkbox "checked" property to be true'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('checked')); }); - screenshot('checked'); checkboxEl.click(); inputEl.getAttribute('checked').then((value: string) => { expect(value).toBeFalsy('Expect checkbox "checked" property to be false'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('unchecked')); }); - screenshot('unchecked'); }); it('should toggle the checkbox when pressing space', () => { @@ -32,16 +36,13 @@ describe('checkbox', function () { inputEl.getAttribute('checked').then((value: string) => { expect(value).toBeFalsy('Expect checkbox "checked" property to be false'); - screenshot('start'); }); inputEl.sendKeys(Key.SPACE); inputEl.getAttribute('checked').then((value: string) => { expect(value).toBeTruthy('Expect checkbox "checked" property to be true'); - screenshot('pressed space'); }); }); - }); }); diff --git a/e2e/components/dialog/dialog.e2e.ts b/e2e/components/dialog/dialog.e2e.ts index 907713ceeaae..7a9d862384c0 100644 --- a/e2e/components/dialog/dialog.e2e.ts +++ b/e2e/components/dialog/dialog.e2e.ts @@ -11,6 +11,12 @@ describe('dialog', () => { expectToExist('md-dialog-container'); }); + it('should open a template dialog', () => { + expectToExist('.my-template-dialog', false); + element(by.id('template')).click(); + expectToExist('.my-template-dialog'); + }); + it('should close by clicking on the backdrop', () => { element(by.id('default')).click(); @@ -29,6 +35,16 @@ describe('dialog', () => { }); }); + it('should close by pressing escape when the first tabbable element has lost focus', () => { + element(by.id('default')).click(); + + waitForDialog().then(() => { + clickElementAtPoint('md-dialog-container', { x: 0, y: 0 }); + pressKeys(Key.ESCAPE); + expectToExist('md-dialog-container', false); + }); + }); + it('should close by clicking on the "close" button', () => { element(by.id('default')).click(); diff --git a/e2e/components/fullscreen/fullscreen.e2e.ts b/e2e/components/fullscreen/fullscreen.e2e.ts index 63a3c65d06e5..3851de017cc0 100644 --- a/e2e/components/fullscreen/fullscreen.e2e.ts +++ b/e2e/components/fullscreen/fullscreen.e2e.ts @@ -1,38 +1,38 @@ -import {browser, by, element, Key, ProtractorBy} from 'protractor'; +import {browser, by, element} from 'protractor'; describe('fullscreen', () => { - beforeEach(() => browser.get('/fullscreen')); - let overlayInBody = () => - browser.isElementPresent(by.css('body > .cdk-overlay-container') as ProtractorBy); - let overlayInFullscreen = () => - browser.isElementPresent(by.css('#fullscreenpane > .cdk-overlay-container') as ProtractorBy); + beforeEach(() => browser.get('/fullscreen')); it('should open a dialog inside a fullscreen element and move it to the document body', () => { element(by.id('fullscreen')).click(); element(by.id('dialog')).click(); - overlayInFullscreen().then((isPresent: boolean) => { - expect(isPresent).toBe(true); - element(by.id('exitfullscreenindialog')).click(); - overlayInBody().then((isPresent: boolean) => { - expect(isPresent).toBe(true); - }); - }); + expectOverlayInFullscreen(); + + element(by.id('exitfullscreenindialog')).click(); + expectOverlayInBody(); }); it('should open a dialog inside the document body and move it to a fullscreen element', () => { element(by.id('dialog')).click(); - overlayInBody().then((isPresent: boolean) => { - expect(isPresent).toBe(true); - element(by.id('fullscreenindialog')).click(); - overlayInFullscreen().then((isPresent: boolean) => { - expect(isPresent).toBe(true); - element(by.id('exitfullscreenindialog')).click(); - overlayInBody().then((isPresent: boolean) => { - expect(isPresent).toBe(true); - }); - }); - }); + expectOverlayInBody(); + + element(by.id('fullscreenindialog')).click(); + expectOverlayInFullscreen(); + + element(by.id('exitfullscreenindialog')).click(); + expectOverlayInBody(); }); + + /** Expects the overlay container to be inside of the body element. */ + function expectOverlayInBody() { + expect(browser.isElementPresent(by.css('body > .cdk-overlay-container'))).toBe(true); + } + + /** Expects the overlay container to be in fullscreen mode. */ + function expectOverlayInFullscreen() { + expect(browser.isElementPresent(by.css('#fullscreenpane > .cdk-overlay-container'))).toBe(true); + } + }); diff --git a/e2e/components/grid-list/grid-list.e2e.ts b/e2e/components/grid-list/grid-list.e2e.ts index 814e0bbbc697..848f2b04a3cb 100644 --- a/e2e/components/grid-list/grid-list.e2e.ts +++ b/e2e/components/grid-list/grid-list.e2e.ts @@ -1,11 +1,13 @@ import {browser} from 'protractor'; import {expectToExist} from '../../util/asserts'; +import {screenshot} from '../../screenshot'; describe('grid-list', () => { beforeEach(() => browser.get('/grid-list')); it('should render a grid list container', () => { expectToExist('md-grid-list'); + screenshot(); }); it('should render list items inside the grid list container', () => { diff --git a/e2e/components/icon/icon.e2e.ts b/e2e/components/icon/icon.e2e.ts index e0cb1caa6910..1407f517d1ef 100644 --- a/e2e/components/icon/icon.e2e.ts +++ b/e2e/components/icon/icon.e2e.ts @@ -1,4 +1,5 @@ import {browser, by, element} from 'protractor'; +import {screenshot} from '../../screenshot'; describe('icon', () => { describe('font icons by ligature', () => { @@ -13,6 +14,7 @@ describe('icon', () => { testIcon.getAttribute('aria-label').then((attr: string) => { expect(attr).toEqual('favorite'); }); + screenshot(); }); it('should have the correct class when used', () => { diff --git a/e2e/components/list/list.e2e.ts b/e2e/components/list/list.e2e.ts index e616f1ffcde5..86da9dba1246 100644 --- a/e2e/components/list/list.e2e.ts +++ b/e2e/components/list/list.e2e.ts @@ -1,11 +1,13 @@ import {browser} from 'protractor'; import {expectToExist} from '../../util/asserts'; +import {screenshot} from '../../screenshot'; describe('list', () => { beforeEach(() => browser.get('/list')); it('should render a list container', () => { expectToExist('md-list'); + screenshot(); }); it('should render list items inside the list container', () => { diff --git a/e2e/components/menu/menu-page.ts b/e2e/components/menu/menu-page.ts index ffda502eba4f..0d5dc6f82142 100644 --- a/e2e/components/menu/menu-page.ts +++ b/e2e/components/menu/menu-page.ts @@ -3,7 +3,7 @@ import {browser, by, element, ElementFinder} from 'protractor'; export class MenuPage { constructor() { browser.get('/menu'); } - menu(): ElementFinder { return element(by.css('.md-menu-panel')); } + menu(): ElementFinder { return element(by.css('.mat-menu-panel')); } start(): ElementFinder { return element(by.id('start')); } @@ -23,11 +23,11 @@ export class MenuPage { combinedTrigger(): ElementFinder { return element(by.id('combined-t')); } - beforeMenu(): ElementFinder { return element(by.css('.md-menu-panel.before')); } + beforeMenu(): ElementFinder { return element(by.css('.mat-menu-panel.before')); } - aboveMenu(): ElementFinder { return element(by.css('.md-menu-panel.above')); } + aboveMenu(): ElementFinder { return element(by.css('.mat-menu-panel.above')); } - combinedMenu(): ElementFinder { return element(by.css('.md-menu-panel.combined')); } + combinedMenu(): ElementFinder { return element(by.css('.mat-menu-panel.combined')); } getResultText() { return this.textArea().getText(); } } diff --git a/e2e/components/menu/menu.e2e.ts b/e2e/components/menu/menu.e2e.ts index 36e330fbf60d..26b3e4dbb431 100644 --- a/e2e/components/menu/menu.e2e.ts +++ b/e2e/components/menu/menu.e2e.ts @@ -1,10 +1,11 @@ -import {browser, Key, protractor} from 'protractor'; +import {Key, protractor} from 'protractor'; import {MenuPage} from './menu-page'; import {expectToExist, expectAlignedWith, expectFocusOn, expectLocation} from '../../util/asserts'; import {pressKeys} from '../../util/actions'; +import {screenshot} from '../../screenshot'; describe('menu', () => { - const menuSelector = '.md-menu-panel'; + const menuSelector = '.mat-menu-panel'; let page: MenuPage; beforeEach(() => page = new MenuPage()); @@ -15,28 +16,33 @@ describe('menu', () => { expectToExist(menuSelector); expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); + screenshot(); }); it('should close menu when menu item is clicked', () => { page.trigger().click(); page.items(0).click(); expectToExist(menuSelector, false); + screenshot(); }); it('should run click handlers on regular menu items', () => { page.trigger().click(); page.items(0).click(); expect(page.getResultText()).toEqual('one'); + screenshot('one'); page.trigger().click(); page.items(1).click(); expect(page.getResultText()).toEqual('two'); + screenshot('two'); }); it('should run not run click handlers on disabled menu items', () => { page.trigger().click(); page.items(2).click(); expect(page.getResultText()).toEqual(''); + screenshot(); }); it('should support multiple triggers opening the same menu', () => { @@ -60,7 +66,7 @@ describe('menu', () => { it('should mirror classes on host to menu template in overlay', () => { page.trigger().click(); page.menu().getAttribute('class').then((classes: string) => { - expect(classes).toContain('md-menu-panel custom'); + expect(classes).toContain('mat-menu-panel custom'); }); }); diff --git a/e2e/components/radio/radio.e2e.ts b/e2e/components/radio/radio.e2e.ts index 09781fbedb92..a2a5188838d2 100644 --- a/e2e/components/radio/radio.e2e.ts +++ b/e2e/components/radio/radio.e2e.ts @@ -1,4 +1,5 @@ -import {browser, by, element} from 'protractor'; +import {browser, by, element, ExpectedConditions} from 'protractor'; +import {screenshot} from '../../screenshot'; describe('radio', () => { describe('disabling behavior', () => { @@ -7,7 +8,10 @@ describe('radio', () => { it('should be checked when clicked', () => { element(by.id('water')).click(); element(by.id('water')).getAttribute('class').then((value: string) => { - expect(value).toContain('md-radio-checked'); + expect(value).toContain('mat-radio-checked'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('water')); }); element(by.css('input[id=water-input]')).getAttribute('checked').then((value: string) => { expect(value).toBeTruthy(); @@ -18,7 +22,10 @@ describe('radio', () => { element(by.id('leaf')).click(); element(by.id('leaf')).getAttribute('class').then((value: string) => { - expect(value).toContain('md-radio-checked'); + expect(value).toContain('mat-radio-checked'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('leaf')); }); element(by.css('input[id=leaf-input]')).getAttribute('checked').then((value: string) => { expect(value).toBeTruthy(); @@ -32,15 +39,20 @@ describe('radio', () => { element(by.id('toggle-disable')).click(); element(by.id('water')).click(); element(by.id('water')).getAttribute('class').then((value: string) => { - expect(value).toContain('md-radio-disabled'); + expect(value).toContain('mat-radio-disabled'); + browser.wait(ExpectedConditions.presenceOf(element(by.css('.mat-radio-disabled')))) + .then(() => screenshot('water')); }); - element(by.css('input[id=water-input]')).getAttribute('disabled').then((value: string) => { + element(by.css('input[id=water-input]')).getAttribute('disabled').then((value: string) => { expect(value).toBeTruthy(); }); element(by.id('leaf')).click(); element(by.id('leaf')).getAttribute('class').then((value: string) => { - expect(value).toContain('md-radio-disabled'); + expect(value).toContain('mat-radio-disabled'); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('leaf')); }); element(by.css('input[id=leaf-input]')).getAttribute('disabled').then((value: string) => { expect(value).toBeTruthy(); diff --git a/e2e/components/slide-toggle/slide-toggle.e2e.ts b/e2e/components/slide-toggle/slide-toggle.e2e.ts index 96f839e6e3ef..c681c093942f 100644 --- a/e2e/components/slide-toggle/slide-toggle.e2e.ts +++ b/e2e/components/slide-toggle/slide-toggle.e2e.ts @@ -1,5 +1,6 @@ import {browser, element, by, Key} from 'protractor'; import {expectToExist} from '../../util/asserts'; +import {screenshot} from '../../screenshot'; describe('slide-toggle', () => { const getInput = () => element(by.css('#normal-slide-toggle input')); @@ -9,6 +10,7 @@ describe('slide-toggle', () => { it('should render a slide-toggle', () => { expectToExist('md-slide-toggle'); + screenshot(); }); it('should change the checked state on click', () => { @@ -19,6 +21,7 @@ describe('slide-toggle', () => { getNormalToggle().click(); expect(inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); + screenshot(); }); it('should change the checked state on click', () => { @@ -29,6 +32,7 @@ describe('slide-toggle', () => { getNormalToggle().click(); expect(inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); + screenshot(); }); it('should not change the checked state on click when disabled', () => { @@ -39,11 +43,12 @@ describe('slide-toggle', () => { element(by.css('#disabled-slide-toggle')).click(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); + screenshot(); }); it('should move the thumb on state change', () => { let slideToggleEl = getNormalToggle(); - let thumbEl = element(by.css('#normal-slide-toggle .md-slide-toggle-thumb-container')); + let thumbEl = element(by.css('#normal-slide-toggle .mat-slide-toggle-thumb-container')); let previousX = thumbEl.getLocation().then(pos => pos.x); @@ -52,6 +57,7 @@ describe('slide-toggle', () => { let newX = thumbEl.getLocation().then(pos => pos.x); expect(previousX).not.toBe(newX); + screenshot(); }); it('should toggle the slide-toggle on space key', () => { diff --git a/e2e/components/tabs/tabs.e2e.ts b/e2e/components/tabs/tabs.e2e.ts index a8c7c5f72f57..4092aa2b5c4f 100644 --- a/e2e/components/tabs/tabs.e2e.ts +++ b/e2e/components/tabs/tabs.e2e.ts @@ -1,5 +1,14 @@ -import {browser, by, element, ElementArrayFinder, ElementFinder, Key} from 'protractor'; +import { + browser, + by, + element, + ElementArrayFinder, + ElementFinder, + Key, + ExpectedConditions +} from 'protractor'; import {pressKeys} from '../../util/actions'; +import {screenshot} from '../../screenshot'; describe('tabs', () => { describe('basic behavior', () => { @@ -10,7 +19,7 @@ describe('tabs', () => { beforeEach(() => { browser.get('/tabs'); tabGroup = element(by.css('md-tab-group')); - tabLabels = element.all(by.css('.md-tab-label')); + tabLabels = element.all(by.css('.mat-tab-label')); tabBodies = element.all(by.css('md-tab-body')); }); @@ -18,10 +27,16 @@ describe('tabs', () => { tabLabels.get(1).click(); expect(getLabelActiveStates(tabLabels)).toEqual([false, true, false]); expect(getBodyActiveStates(tabBodies)).toEqual([false, true, false]); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('click1')); tabLabels.get(0).click(); expect(getLabelActiveStates(tabLabels)).toEqual([true, false, false]); expect(getBodyActiveStates(tabBodies)).toEqual([true, false, false]); + browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))) + .then(() => screenshot('click0')); }); it('should change focus with keyboard interaction', () => { @@ -67,12 +82,12 @@ function getFocusStates(elements: ElementArrayFinder) { /** Returns an array of true/false that represents the active states for the provided elements. */ function getLabelActiveStates(elements: ElementArrayFinder) { - return getClassStates(elements, 'md-tab-label-active'); + return getClassStates(elements, 'mat-tab-label-active'); } /** Returns an array of true/false that represents the active states for the provided elements */ function getBodyActiveStates(elements: ElementArrayFinder) { - return getClassStates(elements, 'md-tab-body-active'); + return getClassStates(elements, 'mat-tab-body-active'); } /** diff --git a/e2e/screenshot.ts b/e2e/screenshot.ts index 28fad25225a2..e5df95c695d0 100644 --- a/e2e/screenshot.ts +++ b/e2e/screenshot.ts @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import * as gulp from 'gulp'; import * as path from 'path'; import {browser} from 'protractor'; @@ -35,8 +34,8 @@ export class Screenshot { return path.resolve(OUTPUT_DIR, this.filename); } - constructor(id: string) { - this.id = `${currentJasmineSpecName} ${id}`; + constructor(id?: string) { + this.id = id ? `${currentJasmineSpecName} ${id}` : currentJasmineSpecName; browser.takeScreenshot().then(png => this.storeScreenshot(png)); } @@ -52,7 +51,7 @@ export class Screenshot { } } -export function screenshot(id: string) { +export function screenshot(id?: string) { if (process.env['TRAVIS']) { return new Screenshot(id); } diff --git a/guides/getting-started.md b/guides/getting-started.md index b34323262ba7..02eb86b00feb 100644 --- a/guides/getting-started.md +++ b/guides/getting-started.md @@ -1,9 +1,9 @@ Get started with Angular Material using the Angular CLI. -## Install the CLI +## Install Angular CLI ```bash - npm install -g angular-cli + npm install -g @angular/cli ``` ## Create a new project @@ -27,7 +27,7 @@ npm install --save @angular/material import { MaterialModule } from '@angular/material'; // other imports @NgModule({ - imports: [MaterialModule.forRoot()], + imports: [MaterialModule], ... }) export class PizzaPartyAppModule { } diff --git a/guides/theming-your-components.md b/guides/theming-your-components.md index d413c639f037..91750546d8e8 100644 --- a/guides/theming-your-components.md +++ b/guides/theming-your-components.md @@ -24,8 +24,8 @@ All you need is to create a `@mixin` function in the custom-component-theme.scss // Use md-color to extract individual colors from a palette as necessary. .candy-carousel { - background-color: md-color($primary); - border-color: md-color($accent, A400); + background-color: mat-color($primary); + border-color: mat-color($accent, A400); } } ``` @@ -53,7 +53,7 @@ Styles that are affected by the theme should be placed in a separated theming fi ## Using colors from a palette -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: +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 `mat-color` function to extract a specific color from a palette. For example: ```scss // Import theming functions @@ -61,9 +61,9 @@ You can consume the theming functions from the `@angular/material/core/theming/t // Import your custom theme @import 'src/unicorn-app-theme.scss'; -// Use md-color to extract individual colors from a palette as necessary. +// Use mat-color to extract individual colors from a palette as necessary. .candy-carousel { - background-color: md-color($candy-app-primary); - border-color: md-color($candy-app-accent, A400); + background-color: mat-color($candy-app-primary); + border-color: mat-color($candy-app-accent, A400); } ``` diff --git a/guides/theming.md b/guides/theming.md index 81a3617a6af7..78a5e59861c6 100644 --- a/guides/theming.md +++ b/guides/theming.md @@ -41,7 +41,7 @@ The actual path will depend on your server setup. You can also concatenate the file with the rest of your application's css. Finally, if your app's content **is not** placed inside of a `md-sidenav-container` element, you -need to add the `md-app-background` class to your wrapper element (for example the `body`). This +need to add the `mat-app-background` class to your wrapper element (for example the `body`). This ensures that the proper theme background is applied to your page. ### Defining a custom theme @@ -55,19 +55,19 @@ the corresponding styles. A typical theme file will look something like this: // Include the base styles for Angular Material core. We include this here so that you only // have to load a single css file for Angular Material in your app. -@include md-core(); +@include mat-core(); // Define the palettes for your theme using the Material Design palettes available in palette.scss // (imported above). For each palette, you can optionally specify a default, lighter, and darker // hue. -$candy-app-primary: md-palette($md-indigo); -$candy-app-accent: md-palette($md-pink, A200, A100, A400); +$candy-app-primary: mat-palette($mat-indigo); +$candy-app-accent: mat-palette($mat-pink, A200, A100, A400); // The warn palette is optional (defaults to red). -$candy-app-warn: md-palette($md-red); +$candy-app-warn: mat-palette($mat-red); // Create the theme object (a Sass map containing all of the palettes). -$candy-app-theme: md-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn); +$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn); // Include theme styles for core and each component used in your app. // Alternatively, you can import and @include the theme mixins for each component @@ -96,11 +96,11 @@ some selector. For example, we could append the following to the example above t secondary dark theme: ```scss .unicorn-dark-theme { - $dark-primary: md-palette($md-blue-grey); - $dark-accent: md-palette($md-amber, A200, A100, A400); - $dark-warn: md-palette($md-deep-orange); + $dark-primary: mat-palette($mat-blue-grey); + $dark-accent: mat-palette($mat-amber, A200, A100, A400); + $dark-warn: mat-palette($mat-deep-orange); - $dark-theme: md-dark-theme($dark-primary, $dark-accent, $dark-warn); + $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); @include angular-material-theme($dark-theme); } diff --git a/package.json b/package.json index a1f88577415f..427b8a5f5ff1 100644 --- a/package.json +++ b/package.json @@ -19,35 +19,35 @@ "docs": "gulp docs", "api": "gulp api" }, - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "license": "MIT", "engines": { "node": ">= 5.4.1 < 7" }, "dependencies": { - "@angular/common": "^2.2.0", - "@angular/compiler": "^2.2.0", - "@angular/core": "^2.2.0", - "@angular/forms": "^2.2.0", - "@angular/http": "^2.2.0", - "@angular/platform-browser": "^2.2.0", + "@angular/common": "^2.3.0", + "@angular/compiler": "^2.3.0", + "@angular/core": "^2.3.0", + "@angular/forms": "^2.3.0", + "@angular/http": "^2.3.0", + "@angular/platform-browser": "^2.3.0", "core-js": "^2.4.1", - "rxjs": "5.0.0-beta.12", - "systemjs": "0.19.38", - "zone.js": "^0.6.23" + "rxjs": "^5.0.1", + "systemjs": "0.19.43", + "zone.js": "^0.7.2" }, "devDependencies": { - "@angular/compiler-cli": "^2.2.0", - "@angular/platform-browser-dynamic": "^2.2.0", - "@angular/platform-server": "^2.2.0", - "@angular/router": "^3.2.0", + "@angular/compiler-cli": "^2.3.0", + "@angular/platform-browser-dynamic": "^2.3.0", + "@angular/platform-server": "^2.3.0", + "@angular/router": "^3.3.0", "@types/glob": "^5.0.30", "@types/gulp": "^3.8.32", "@types/hammerjs": "^2.0.34", - "@types/jasmine": "^2.5.40", + "@types/jasmine": "2.5.41", "@types/merge2": "^0.3.29", "@types/minimist": "^1.2.0", - "@types/node": "^6.0.34", + "@types/node": "^7.0.4", "@types/run-sequence": "^0.0.28", "@types/rx": "2.5.33", "axe-core": "^2.1.7", @@ -55,9 +55,9 @@ "conventional-changelog": "^1.1.0", "dgeni": "^0.4.2", "dgeni-packages": "^0.16.5", - "firebase-admin": "^4.0.4", + "firebase-admin": "^4.0.6", "firebase-tools": "^2.2.1", - "fs-extra": "^1.0.0", + "fs-extra": "^2.0.0", "glob": "^7.1.1", "gulp": "^3.9.1", "gulp-autoprefixer": "^3.1.1", @@ -66,37 +66,41 @@ "gulp-clean-css": "^2.3.2", "gulp-cli": "^1.2.2", "gulp-connect": "^5.0.0", + "gulp-flatten": "^0.3.1", + "gulp-highlight-files": "0.0.4", "gulp-htmlmin": "^3.0.0", "gulp-if": "^2.0.2", "gulp-markdown": "^1.2.0", + "gulp-rename": "^1.2.2", "gulp-sass": "^3.1.0", "gulp-sourcemaps": "^2.4.0", "gulp-transform": "^1.1.0", - "gulp-typescript": "^3.1.4", "hammerjs": "^2.0.8", "highlight.js": "^9.9.0", "jasmine-core": "^2.5.2", - "karma": "^1.3.0", - "karma-browserstack-launcher": "^1.1.1", + "karma": "^1.4.1", + "karma-browserstack-launcher": "^1.2.0", "karma-chrome-launcher": "^2.0.0", "karma-firefox-launcher": "^1.0.0", "karma-jasmine": "^1.1.0", "karma-sauce-launcher": "^1.1.0", "karma-sourcemap-loader": "^0.3.7", - "madge": "^0.6.0", + "madge": "^1.5.0", "merge2": "^1.0.2", "minimist": "^1.2.0", "node-sass": "^4.3.0", - "protractor": "^5.0.0", + "protractor": "^5.1.0", "resolve-bin": "^0.4.0", "run-sequence": "^1.2.2", "sass": "^0.5.0", "selenium-webdriver": "^3.0.1", - "stylelint": "^7.7.1", + "stylelint": "^7.8.0", "travis-after-modes": "0.0.7", "ts-node": "^2.0.0", - "tslint": "^3.13.0", + "tslint": "^4.4.2", + "tslint-no-unused-var": "0.0.6", "typescript": "~2.0.10", - "uglify-js": "^2.7.5" + "uglify-js": "^2.7.5", + "web-animations-js": "^2.2.2" } } diff --git a/scripts/ci/after-success.sh b/scripts/ci/after-success.sh index 37524389d1e3..599f097f2cec 100755 --- a/scripts/ci/after-success.sh +++ b/scripts/ci/after-success.sh @@ -10,4 +10,5 @@ cd $(dirname $0)/../.. if [ "$TRAVIS_PULL_REQUEST" = "false" ] && $(npm bin)/travis-after-modes; then echo "All travis modes passed. Publishing the build artifacts..." ./scripts/release/publish-build-artifacts.sh -fi \ No newline at end of file + ./scripts/release/publish-docs-content.sh +fi diff --git a/scripts/release/copy-docs.sh b/scripts/release/copy-docs.sh index 4c9247b7c23a..a18c5acaddcc 100755 --- a/scripts/release/copy-docs.sh +++ b/scripts/release/copy-docs.sh @@ -7,7 +7,7 @@ # Use OVERVIEW.html when possible. If there's no OVERVIEW file exists, use README.html usage='Usage: copy-docs.sh $destinationFolder' -if [ $# -ne 1 ]; then +if [ $# -ne 1 ]; then echo "Missing destination folder. $usage" exit fi @@ -23,7 +23,7 @@ fi for file in $originFolder* do name=${file#$originFolder} - overviewFile=$originFolder$name/OVERVIEW.html + overviewFile=$originFolder$name/$name.html readmeFile=$originFolder$name/README.html destFile=$destFolder/$name.html if [ -f $overviewFile ]; then diff --git a/scripts/release/publish-docs-content.sh b/scripts/release/publish-docs-content.sh new file mode 100755 index 000000000000..5074ff1aa65f --- /dev/null +++ b/scripts/release/publish-docs-content.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Publish material2 docs assets to the material2-docs-content repo +# material.angular.io will pull from this assets repo to get the latest docs + +cd "$(dirname $0)/../../" + +docsPath="./dist/docs" +repoPath="/tmp/material2-docs-content" +repoUrl="https://github.com/DevVersion/material2-docs-content" +examplesSource="./dist/docs/examples" + +# If the docs directory is not present, generate docs +if [ ! -d $docsPath ]; then + $(npm bin)/gulp docs +fi + +# Get git meta info for commit +commitSha="$(git rev-parse --short HEAD)" +commitAuthorName="$(git --no-pager show -s --format='%an' HEAD)" +commitAuthorEmail="$(git --no-pager show -s --format='%ae' HEAD)" +commitMessage="$(git log --oneline -n 1)" + +# create directory and clone test repo +rm -rf $repoPath +mkdir -p $repoPath +git clone $repoUrl $repoPath + +# Clean out repo directory and copy contents of dist/docs into it +rm -rf $repoPath/* +mkdir $repoPath/overview +mkdir $repoPath/guides +mkdir $repoPath/api +mkdir $repoPath/examples + +# Move api files over to $repoPath/api +cp -r $docsPath/api/* $repoPath/api + +# Flatten the markdown docs structure and move it into $repoPath/overview +overviewFiles=$docsPath/markdown/ +for filename in $overviewFiles* +do + if [ -d $filename ]; then + for _ in $filename/* + do + markdownFile=${filename#$overviewFiles}.html + # Filename should be same as folder name with .html extension + if [ -e $filename/$markdownFile ]; then + cp -r $filename/$markdownFile $repoPath/overview/ + fi + done + fi +done + +# Move guide files over to $repoPath/guides +for filename in $overviewFiles* +do + if [ -f $filename ]; then + cp -r $filename $repoPath/guides + fi +done + +# Move highlighted examples into $repoPath +cp -r $examplesSource/* $repoPath/examples + +# Push content to repo +cd $repoPath +git config user.name "$commitAuthorName" +git config user.email "$commitAuthorEmail" +git config credential.helper "store --file=.git/credentials" + +echo "https://${MATERIAL2_BUILDS_TOKEN}:@github.com" > .git/credentials + +git add -A +git commit -m "$commitMessage" +git tag "$commitSha" +git push origin master --tags diff --git a/scripts/saucelabs/start-tunnel.sh b/scripts/saucelabs/start-tunnel.sh index 30a60bcf27ae..4110fe34aa91 100755 --- a/scripts/saucelabs/start-tunnel.sh +++ b/scripts/saucelabs/start-tunnel.sh @@ -2,7 +2,7 @@ set -e -o pipefail -TUNNEL_FILE="sc-4.4.2-linux.tar.gz" +TUNNEL_FILE="sc-4.4.3-linux.tar.gz" TUNNEL_URL="https://saucelabs.com/downloads/$TUNNEL_FILE" TUNNEL_DIR="/tmp/saucelabs-connect" diff --git a/src/demo-app/autocomplete/autocomplete-demo.html b/src/demo-app/autocomplete/autocomplete-demo.html index 95e6a345b179..1124b22a5e2f 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.html +++ b/src/demo-app/autocomplete/autocomplete-demo.html @@ -1,6 +1,9 @@ +Space above cards: +
-
Reactive value: {{ stateCtrl.value }}
+ Reactive length: {{ reactiveStates.length }} +
Reactive value: {{ stateCtrl.value | json }}
Reactive dirty: {{ stateCtrl.dirty }}
@@ -9,7 +12,7 @@ - + @@ -37,8 +40,8 @@
- - + + {{ state.name }} ({{state.code}}) @@ -47,6 +50,5 @@ {{ state.name }} - ({{state.code}}) \ No newline at end of file diff --git a/src/demo-app/autocomplete/autocomplete-demo.scss b/src/demo-app/autocomplete/autocomplete-demo.scss index 5789ae0ee434..8b4132523acf 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.scss +++ b/src/demo-app/autocomplete/autocomplete-demo.scss @@ -2,12 +2,12 @@ display: flex; flex-flow: row wrap; - md-card { - width: 350px; + .mat-card { + width: 400px; margin: 24px; } - md-input-container { + .mat-input-container { margin-top: 16px; } } diff --git a/src/demo-app/autocomplete/autocomplete-demo.ts b/src/demo-app/autocomplete/autocomplete-demo.ts index 0de67dd4cbaf..d6caba770ff8 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.ts +++ b/src/demo-app/autocomplete/autocomplete-demo.ts @@ -1,6 +1,6 @@ -import {Component, OnDestroy, ViewEncapsulation} from '@angular/core'; +import {Component, ViewEncapsulation} from '@angular/core'; import {FormControl} from '@angular/forms'; -import {Subscription} from 'rxjs/Subscription'; +import 'rxjs/add/operator/startWith'; @Component({ moduleId: module.id, @@ -9,14 +9,14 @@ import {Subscription} from 'rxjs/Subscription'; styleUrls: ['autocomplete-demo.css'], encapsulation: ViewEncapsulation.None }) -export class AutocompleteDemo implements OnDestroy { - stateCtrl = new FormControl(); +export class AutocompleteDemo { + stateCtrl: FormControl; currentState = ''; + topHeightCtrl = new FormControl(0); - reactiveStates: any[]; + reactiveStates: any; tdStates: any[]; - reactiveValueSub: Subscription; tdDisabled = false; states = [ @@ -73,19 +73,20 @@ export class AutocompleteDemo implements OnDestroy { ]; constructor() { - this.reactiveStates = this.states; this.tdStates = this.states; - this.reactiveValueSub = - this.stateCtrl.valueChanges.subscribe(val => this.reactiveStates = this.filterStates(val)); + this.stateCtrl = new FormControl({code: 'CA', name: 'California'}); + this.reactiveStates = this.stateCtrl.valueChanges + .startWith(this.stateCtrl.value) + .map(val => this.displayFn(val)) + .map(name => this.filterStates(name)); + } + displayFn(value: any): string { + return value && typeof value === 'object' ? value.name : value; } filterStates(val: string) { return val ? this.states.filter((s) => s.name.match(new RegExp(val, 'gi'))) : this.states; } - ngOnDestroy() { - this.reactiveValueSub.unsubscribe(); - } - } diff --git a/src/demo-app/baseline/baseline-demo.scss b/src/demo-app/baseline/baseline-demo.scss index 32ba259cffaa..2e25ae8f1518 100644 --- a/src/demo-app/baseline/baseline-demo.scss +++ b/src/demo-app/baseline/baseline-demo.scss @@ -2,7 +2,7 @@ .demo-basic { padding: 0; } -.demo-basic md-card-content { +.demo-basic .mat-card-content { padding: 16px; } .demo-full-width { diff --git a/src/demo-app/card/card-demo.html b/src/demo-app/card/card-demo.html index 1bb780497625..98cc322d20e3 100644 --- a/src/demo-app/card/card-demo.html +++ b/src/demo-app/card/card-demo.html @@ -51,7 +51,7 @@ - + Easily customizable diff --git a/src/demo-app/card/card-demo.scss b/src/demo-app/card/card-demo.scss index e5dfdfce005b..309b1e2d1366 100644 --- a/src/demo-app/card/card-demo.scss +++ b/src/demo-app/card/card-demo.scss @@ -2,7 +2,7 @@ display: flex; flex-flow: column nowrap; - md-card { + .mat-card { margin: 0 16px 16px 0; width: 350px; } @@ -15,7 +15,7 @@ .demo-card-blue { background-color: #b0becc; - md-card-actions { + .mat-card-actions { display: flex; flex-direction: column; } diff --git a/src/demo-app/chips/chips-demo.html b/src/demo-app/chips/chips-demo.html index f6932b772fc9..859d638695f6 100644 --- a/src/demo-app/chips/chips-demo.html +++ b/src/demo-app/chips/chips-demo.html @@ -54,7 +54,7 @@

Stacked Chips

(focus) event to run custom code.

- + {{aColor.name}} diff --git a/src/demo-app/chips/chips-demo.scss b/src/demo-app/chips/chips-demo.scss index d9392867ba1b..c44263807d75 100644 --- a/src/demo-app/chips/chips-demo.scss +++ b/src/demo-app/chips/chips-demo.scss @@ -1,23 +1,23 @@ .chips-demo { - .md-chip-list-stacked { + .mat-chip-list-stacked { display: block; max-width: 200px; } - md-card { + .mat-card { padding: 0; margin: 16px; - & md-toolbar { + & .mat-toolbar { margin: 0; } - & md-card-content { + & .mat-card-content { padding: 24px; } } - .md-basic-chip { + .mat-basic-chip { margin: auto 10px; } } \ No newline at end of file diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 47b54dbb1036..9f66cf8016ba 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -4,8 +4,7 @@ import {HttpModule} from '@angular/http'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {DemoApp, Home} from './demo-app/demo-app'; import {RouterModule} from '@angular/router'; -import {MaterialModule, OverlayContainer, - FullscreenOverlayContainer} from '@angular/material'; +import {MaterialModule, OverlayContainer, FullscreenOverlayContainer} from '@angular/material'; import {DEMO_APP_ROUTES} from './demo-app/routes'; import {ProgressBarDemo} from './progress-bar/progress-bar-demo'; import {JazzDialog, ContentElementDialog, DialogDemo, IFrameDialog} from './dialog/dialog-demo'; @@ -37,7 +36,8 @@ import {TabsDemo, SunnyTabContent, RainyTabContent, FoggyTabContent} from './tab import {ProjectionDemo, ProjectionTestComponent} from './projection/projection-demo'; import {PlatformDemo} from './platform/platform-demo'; import {AutocompleteDemo} from './autocomplete/autocomplete-demo'; -import {InputContainerDemo} from './input/input-container-demo'; +import {InputDemo} from './input/input-demo'; +import {StyleDemo} from './style/style-demo'; @NgModule({ imports: [ @@ -62,7 +62,7 @@ import {InputContainerDemo} from './input/input-container-demo'; GridListDemo, Home, IconDemo, - InputContainerDemo, + InputDemo, JazzDialog, ContentElementDialog, IFrameDialog, @@ -86,6 +86,7 @@ import {InputContainerDemo} from './input/input-container-demo'; SliderDemo, SlideToggleDemo, SpagettiPanel, + StyleDemo, ToolbarDemo, TooltipDemo, TabsDemo, diff --git a/src/demo-app/demo-app/demo-app.scss b/src/demo-app/demo-app/demo-app.scss index dacebc95c957..ee4512261ed3 100644 --- a/src/demo-app/demo-app/demo-app.scss +++ b/src/demo-app/demo-app/demo-app.scss @@ -8,10 +8,10 @@ body { -moz-osx-font-smoothing: grayscale; } - md-sidenav { - min-width: 15%; + .mat-sidenav { + min-width: 15vw; - [md-button] { + .mat-button { width: 100%; position: relative; bottom: 0; @@ -24,8 +24,8 @@ body { box-sizing: border-box; } - md-toolbar { - md-icon { + .mat-toolbar { + .mat-icon { cursor: pointer; } diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 98c029d770cc..746a18056915 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -31,7 +31,6 @@ export class DemoApp { {name: 'Grid List', route: 'grid-list'}, {name: 'Icon', route: 'icon'}, {name: 'Input', route: 'input'}, - {name: 'Input Container', route: 'input-container'}, {name: 'List', route: 'list'}, {name: 'Menu', route: 'menu'}, {name: 'Live Announcer', route: 'live-announcer'}, @@ -50,7 +49,8 @@ export class DemoApp { {name: 'Tabs', route: 'tabs'}, {name: 'Toolbar', route: 'toolbar'}, {name: 'Tooltip', route: 'tooltip'}, - {name: 'Platform', route: 'platform'} + {name: 'Platform', route: 'platform'}, + {name: 'Style', route: 'style'} ]; constructor(private _element: ElementRef) { diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index 3a1b3ba916b6..d2e2360799f6 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -31,7 +31,8 @@ import {ProjectionDemo} from '../projection/projection-demo'; import {TABS_DEMO_ROUTES} from '../tabs/routes'; import {PlatformDemo} from '../platform/platform-demo'; import {AutocompleteDemo} from '../autocomplete/autocomplete-demo'; -import {InputContainerDemo} from '../input/input-container-demo'; +import {InputDemo} from '../input/input-demo'; +import {StyleDemo} from '../style/style-demo'; export const DEMO_APP_ROUTES: Routes = [ {path: '', component: Home}, @@ -50,7 +51,7 @@ export const DEMO_APP_ROUTES: Routes = [ {path: 'projection', component: ProjectionDemo}, {path: 'overlay', component: OverlayDemo}, {path: 'checkbox', component: CheckboxDemo}, - {path: 'input-container', component: InputContainerDemo}, + {path: 'input', component: InputDemo}, {path: 'toolbar', component: ToolbarDemo}, {path: 'icon', component: IconDemo}, {path: 'list', component: ListDemo}, @@ -65,5 +66,6 @@ export const DEMO_APP_ROUTES: Routes = [ {path: 'dialog', component: DialogDemo}, {path: 'tooltip', component: TooltipDemo}, {path: 'snack-bar', component: SnackBarDemo}, - {path: 'platform', component: PlatformDemo} + {path: 'platform', component: PlatformDemo}, + {path: 'style', component: StyleDemo}, ]; diff --git a/src/demo-app/dialog/dialog-demo.html b/src/demo-app/dialog/dialog-demo.html index 53734bbbfdff..ec05d8f1ae04 100644 --- a/src/demo-app/dialog/dialog-demo.html +++ b/src/demo-app/dialog/dialog-demo.html @@ -1,7 +1,14 @@

Dialog demo

- - + + + @@ -46,8 +53,20 @@

Other options

- Disable close +

+ + + +

+ +

+ Disable close +

Last close result: {{lastCloseResult}}

+ + diff --git a/src/demo-app/dialog/dialog-demo.ts b/src/demo-app/dialog/dialog-demo.ts index 5e91c097f9aa..f82ecbfbce4c 100644 --- a/src/demo-app/dialog/dialog-demo.ts +++ b/src/demo-app/dialog/dialog-demo.ts @@ -1,6 +1,7 @@ -import {Component, Inject} from '@angular/core'; +import {Component, Inject, ViewChild, TemplateRef} from '@angular/core'; import {DOCUMENT} from '@angular/platform-browser'; -import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material'; +import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/material'; + @Component({ moduleId: module.id, @@ -21,8 +22,14 @@ export class DialogDemo { bottom: '', left: '', right: '' + }, + data: { + message: 'Jazzy jazz jazz' } }; + numTemplateOpens = 0; + + @ViewChild(TemplateRef) template: TemplateRef; constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) { // Possible useful example for the open and closeAll events. @@ -41,7 +48,7 @@ export class DialogDemo { openJazz() { this.dialogRef = this.dialog.open(JazzDialog, this.config); - this.dialogRef.afterClosed().subscribe(result => { + this.dialogRef.afterClosed().subscribe((result: string) => { this.lastCloseResult = result; this.dialogRef = null; }); @@ -51,6 +58,11 @@ export class DialogDemo { let dialogRef = this.dialog.open(ContentElementDialog, this.config); dialogRef.componentInstance.actionsAlignment = this.actionsAlignment; } + + openTemplate() { + this.numTemplateOpens++; + this.dialog.open(this.template, this.config); + } } @@ -59,13 +71,13 @@ export class DialogDemo { template: `

It's Jazz!

-

{{ jazzMessage }}

+

{{ data.message }}

` }) export class JazzDialog { - jazzMessage = 'Jazzy jazz jazz'; - - constructor(public dialogRef: MdDialogRef) { } + constructor( + public dialogRef: MdDialogRef, + @Inject(MD_DIALOG_DATA) public data: any) { } } @@ -104,7 +116,7 @@ export class JazzDialog { color="primary" href="https://en.wikipedia.org/wiki/Neptune" target="_blank">Read more on Wikipedia - + + + +
- + +
-Value: {{determinateProgressValue}} - - +

Buffer

+ +
+ Value: {{bufferProgressValue}} + + + + Buffer Value: {{bufferBufferValue}} + + +
+
- +
-Value: {{bufferProgressValue}} - - - -Buffer Value: {{bufferBufferValue}} - -

Indeterminate

- +

Query

- +
diff --git a/src/demo-app/progress-bar/progress-bar-demo.scss b/src/demo-app/progress-bar/progress-bar-demo.scss index b6de28f90cb5..8f038cad031f 100644 --- a/src/demo-app/progress-bar/progress-bar-demo.scss +++ b/src/demo-app/progress-bar/progress-bar-demo.scss @@ -1,12 +1,16 @@ .demo-progress-bar-container { width: 100%; -} -.demo-progress-bar-margins { - margin: 20px 0; + md-progress-bar { + margin: 20px 0; + } } .demo-progress-bar-spacer { display: inline-block; width: 50px; +} + +.demo-progress-bar-controls { + margin: 10px 0; } \ No newline at end of file diff --git a/src/demo-app/progress-bar/progress-bar-demo.ts b/src/demo-app/progress-bar/progress-bar-demo.ts index 5e1501651309..a3eb4f1ca715 100644 --- a/src/demo-app/progress-bar/progress-bar-demo.ts +++ b/src/demo-app/progress-bar/progress-bar-demo.ts @@ -10,19 +10,24 @@ import {Component} from '@angular/core'; styleUrls: ['progress-bar-demo.css'], }) export class ProgressBarDemo { + color: string = 'primary'; determinateProgressValue: number = 30; bufferProgressValue: number = 30; bufferBufferValue: number = 40; stepDeterminateProgressVal(val: number) { - this.determinateProgressValue += val; + this.determinateProgressValue = this.clampValue(val + this.determinateProgressValue); } stepBufferProgressVal(val: number) { - this.bufferProgressValue += val; + this.bufferProgressValue = this.clampValue(val + this.bufferProgressValue); } stepBufferBufferVal(val: number) { - this.bufferBufferValue += val; + this.bufferBufferValue = this.clampValue(val + this.bufferBufferValue); + } + + private clampValue(value: number) { + return Math.max(0, Math.min(100, value)); } } diff --git a/src/demo-app/progress-spinner/progress-spinner-demo.html b/src/demo-app/progress-spinner/progress-spinner-demo.html index 91d632210889..3abea5c81d1a 100644 --- a/src/demo-app/progress-spinner/progress-spinner-demo.html +++ b/src/demo-app/progress-spinner/progress-spinner-demo.html @@ -1,21 +1,26 @@

Determinate

+ +
+

Value: {{progressValue}}

+ + +
+
- - + +
-Value: {{progressValue}} - - +

Indeterminate

+ + Primary Color + Accent Color + Warn Color + -

Indeterminate

- - - + + +
diff --git a/src/demo-app/progress-spinner/progress-spinner-demo.scss b/src/demo-app/progress-spinner/progress-spinner-demo.scss index 9c2d89b96171..8bb5ac7b3465 100644 --- a/src/demo-app/progress-spinner/progress-spinner-demo.scss +++ b/src/demo-app/progress-spinner/progress-spinner-demo.scss @@ -1,9 +1,13 @@ .demo-progress-spinner { width: 100%; - md-progress-spinner, - md-spinner { + .mat-progress-spinner, + .mat-spinner { display: inline-block; } } + +.demo-progress-spinner-controls { + margin: 10px 0; +} \ No newline at end of file diff --git a/src/demo-app/progress-spinner/progress-spinner-demo.ts b/src/demo-app/progress-spinner/progress-spinner-demo.ts index 03f68a3206fc..c11cedc5f57a 100644 --- a/src/demo-app/progress-spinner/progress-spinner-demo.ts +++ b/src/demo-app/progress-spinner/progress-spinner-demo.ts @@ -9,9 +9,10 @@ import {Component} from '@angular/core'; }) export class ProgressSpinnerDemo { progressValue: number = 40; + color: string = 'primary'; step(val: number) { - this.progressValue += val; + this.progressValue = Math.max(0, Math.min(100, val + this.progressValue)); } } diff --git a/src/demo-app/radio/radio-demo.scss b/src/demo-app/radio/radio-demo.scss index af41a95719cd..9a37d4da430a 100644 --- a/src/demo-app/radio/radio-demo.scss +++ b/src/demo-app/radio/radio-demo.scss @@ -8,7 +8,7 @@ margin: 8px; padding: 16px; - md-radio-button { + .mat-radio-button { margin: 8px; } } diff --git a/src/demo-app/ripple/ripple-demo.html b/src/demo-app/ripple/ripple-demo.html index 5c9cf2bd8004..0cec0791fbe3 100644 --- a/src/demo-app/ripple/ripple-demo.html +++ b/src/demo-app/ripple/ripple-demo.html @@ -21,24 +21,18 @@
Speed - Slow + Slow Normal - Fast + Fast
- + - - -
@@ -51,9 +45,8 @@ [mdRippleCentered]="centered" [mdRippleDisabled]="disabled" [mdRippleUnbounded]="unbounded" - [mdRippleMaxRadius]="maxRadius" + [mdRippleRadius]="radius" [mdRippleColor]="rippleColor" - [mdRippleBackgroundColor]="rippleBackgroundColor" [mdRippleSpeedFactor]="rippleSpeed"> Click me diff --git a/src/demo-app/ripple/ripple-demo.ts b/src/demo-app/ripple/ripple-demo.ts index c1fc3675b423..2f1d43bec680 100644 --- a/src/demo-app/ripple/ripple-demo.ts +++ b/src/demo-app/ripple/ripple-demo.ts @@ -9,23 +9,22 @@ import {MdRipple} from '@angular/material'; styleUrls: ['ripple-demo.css'], }) export class RippleDemo { - @ViewChild(MdRipple) manualRipple: MdRipple; + @ViewChild(MdRipple) ripple: MdRipple; centered = false; disabled = false; unbounded = false; rounded = false; - maxRadius: number = null; + radius: number = null; rippleSpeed = 1; rippleColor = ''; - rippleBackgroundColor = ''; disableButtonRipples = false; doManualRipple() { - if (this.manualRipple) { - window.setTimeout(() => this.manualRipple.start(), 10); - window.setTimeout(() => this.manualRipple.end(0, 0), 500); + if (this.ripple) { + this.ripple.launch(0, 0, { centered: true }); } } + } diff --git a/src/demo-app/select/select-demo.html b/src/demo-app/select/select-demo.html index f1e903f4824b..4471ebc92c54 100644 --- a/src/demo-app/select/select-demo.html +++ b/src/demo-app/select/select-demo.html @@ -27,7 +27,7 @@

Touched: {{ drinkControl.touched }}

Dirty: {{ drinkControl.dirty }}

Status: {{ drinkControl.control?.status }}

- + diff --git a/src/demo-app/select/select-demo.scss b/src/demo-app/select/select-demo.scss index 3594a998b107..e7e0649c3a24 100644 --- a/src/demo-app/select/select-demo.scss +++ b/src/demo-app/select/select-demo.scss @@ -2,7 +2,7 @@ display: flex; flex-flow: row wrap; - md-card { + .mat-card { width: 450px; margin: 24px; } diff --git a/src/demo-app/sidenav/sidenav-demo.html b/src/demo-app/sidenav/sidenav-demo.html index aaeba9c88258..3330798e1ce6 100644 --- a/src/demo-app/sidenav/sidenav-demo.html +++ b/src/demo-app/sidenav/sidenav-demo.html @@ -31,8 +31,8 @@

My Content

- - + + diff --git a/src/demo-app/sidenav/sidenav-demo.scss b/src/demo-app/sidenav/sidenav-demo.scss index 9805892bd0db..a04b3bb58124 100644 --- a/src/demo-app/sidenav/sidenav-demo.scss +++ b/src/demo-app/sidenav/sidenav-demo.scss @@ -1,7 +1,7 @@ .demo-sidenav-container { border: 3px solid black; - .md-sidenav-focus-trap > .cdk-focus-trap-content { + .mat-sidenav-focus-trap > .cdk-focus-trap-content { padding: 10px; } } diff --git a/src/demo-app/snack-bar/snack-bar-demo.html b/src/demo-app/snack-bar/snack-bar-demo.html index 314064e9b67a..9fdc08ac9d21 100644 --- a/src/demo-app/snack-bar/snack-bar-demo.html +++ b/src/demo-app/snack-bar/snack-bar-demo.html @@ -27,6 +27,10 @@

SnackBar demo

+ +

+ Add extra class to container +

diff --git a/src/demo-app/snack-bar/snack-bar-demo.scss b/src/demo-app/snack-bar/snack-bar-demo.scss index 8d737ab226f6..b0b1052eae2e 100644 --- a/src/demo-app/snack-bar/snack-bar-demo.scss +++ b/src/demo-app/snack-bar/snack-bar-demo.scss @@ -1,3 +1,17 @@ -.demo-button-label-input { - display: inline-block; -} \ No newline at end of file +.party { + animation: party 5000ms infinite; +} + +@keyframes party { + 0% { background: #00f; } + 10% { background: #8e44ad; } + 20% { background: #1abc9c; } + 30% { background: #d35400; } + 40% { background: #00f; } + 50% { background: #34495e; } + 60% { background: #00f; } + 70% { background: #2980b9; } + 80% { background: #f1c40f; } + 90% { background: #2980b9; } + 100% { background: #0ff; } +} diff --git a/src/demo-app/snack-bar/snack-bar-demo.ts b/src/demo-app/snack-bar/snack-bar-demo.ts index 344d3ed11eb1..baad2beac03d 100644 --- a/src/demo-app/snack-bar/snack-bar-demo.ts +++ b/src/demo-app/snack-bar/snack-bar-demo.ts @@ -1,24 +1,27 @@ -import {Component} from '@angular/core'; +import {Component, ViewEncapsulation} from '@angular/core'; import {MdSnackBar, MdSnackBarConfig} from '@angular/material'; @Component({ moduleId: module.id, selector: 'snack-bar-demo', + styleUrls: ['snack-bar-demo.css'], templateUrl: 'snack-bar-demo.html', + encapsulation: ViewEncapsulation.None, }) export class SnackBarDemo { message: string = 'Snack Bar opened.'; actionButtonLabel: string = 'Retry'; action: boolean = false; setAutoHide: boolean = true; - autoHide: number = 0; + autoHide: number = 10000; + addExtraClass: boolean = false; - constructor( - public snackBar: MdSnackBar) { } + constructor(public snackBar: MdSnackBar) { } open() { let config = new MdSnackBarConfig(); config.duration = this.autoHide; + config.extraClasses = this.addExtraClass ? ['party'] : null; this.snackBar.open(this.message, this.action && this.actionButtonLabel, config); } } diff --git a/src/demo-app/style/style-demo.html b/src/demo-app/style/style-demo.html new file mode 100644 index 000000000000..81786735f85c --- /dev/null +++ b/src/demo-app/style/style-demo.html @@ -0,0 +1,21 @@ + + + + + + + + +
Active classes: {{b.classList}}
+ +
+ +
+

div with element focus monitored

+ +
+ +
+

div with subtree focus monitored

+ +
diff --git a/src/demo-app/style/style-demo.scss b/src/demo-app/style/style-demo.scss new file mode 100644 index 000000000000..ddfd64de6bb3 --- /dev/null +++ b/src/demo-app/style/style-demo.scss @@ -0,0 +1,19 @@ +.demo-focusable.cdk-focused { + border: 2px solid red; +} + +.demo-focusable.cdk-mouse-focused { + background: green; +} + +.demo-focusable.cdk-keyboard-focused { + background: yellow; +} + +.demo-focusable.cdk-program-focused { + background: blue; +} + +.demo-focusable.cdk-touch-focused { + background: purple; +} diff --git a/src/demo-app/style/style-demo.ts b/src/demo-app/style/style-demo.ts new file mode 100644 index 000000000000..e1151da131f8 --- /dev/null +++ b/src/demo-app/style/style-demo.ts @@ -0,0 +1,13 @@ +import {Component, Renderer} from '@angular/core'; +import {FocusOriginMonitor} from '@angular/material'; + + +@Component({ + moduleId: module.id, + selector: 'style-demo', + templateUrl: 'style-demo.html', + styleUrls: ['style-demo.css'], +}) +export class StyleDemo { + constructor(public renderer: Renderer, public fom: FocusOriginMonitor) {} +} diff --git a/src/demo-app/tabs/tabs-demo.scss b/src/demo-app/tabs/tabs-demo.scss index e35db93a5045..d10e259e7e5e 100644 --- a/src/demo-app/tabs/tabs-demo.scss +++ b/src/demo-app/tabs/tabs-demo.scss @@ -1,7 +1,7 @@ .demo-nav-bar { border: 1px solid #e0e0e0; margin-bottom: 40px; - [md-tab-nav-bar] { + .mat-tab-nav-bar { background: #f9f9f9; } sunny-routed-content, @@ -15,15 +15,15 @@ .demo-tab-group { border: 1px solid #e0e0e0; margin-bottom: 40px; - .md-tab-header { + .mat-tab-header { background: #f9f9f9; } - .md-tab-body-content { + .mat-tab-body-content { padding: 12px; } } -tabs-demo md-card { +tabs-demo .mat-card { width: 160px; } @@ -32,22 +32,22 @@ tabs-demo md-card { display: flex; width: 100%; - md-tab-group { + .mat-tab-group { min-width: 200px; margin-bottom: 0; flex-grow: 1; } - md-card { + .mat-card { min-width: 160px; margin-right: 16px; - md-checkbox { + .mat-checkbox { display: block; margin-top: 8px; } - md-input-container { + .mat-input-container { width: 100px; } diff --git a/src/demo-app/tooltip/tooltip-demo.html b/src/demo-app/tooltip/tooltip-demo.html index 79691037bea0..15025d7a1df3 100644 --- a/src/demo-app/tooltip/tooltip-demo.html +++ b/src/demo-app/tooltip/tooltip-demo.html @@ -1,7 +1,7 @@

Tooltip Demo

-

+

-

+
Scroll down while tooltip is open to see it hide automatically
+
+

diff --git a/src/demo-app/tooltip/tooltip-demo.scss b/src/demo-app/tooltip/tooltip-demo.scss index 39c9602a9e72..c2483c856273 100644 --- a/src/demo-app/tooltip/tooltip-demo.scss +++ b/src/demo-app/tooltip/tooltip-demo.scss @@ -1,8 +1,14 @@ .demo-tooltip { .centered { text-align: center; + height: 200px; + overflow: auto; + + button { + margin: 16px; + } } - md-radio-button { + .mat-radio-button { display: block; } } diff --git a/src/demo-app/tooltip/tooltip-demo.ts b/src/demo-app/tooltip/tooltip-demo.ts index a18c5605a24d..96a59b7e53cb 100644 --- a/src/demo-app/tooltip/tooltip-demo.ts +++ b/src/demo-app/tooltip/tooltip-demo.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, ChangeDetectionStrategy} from '@angular/core'; import {TooltipPosition} from '@angular/material'; @@ -7,6 +7,7 @@ import {TooltipPosition} from '@angular/material'; selector: 'tooltip-demo', templateUrl: 'tooltip-demo.html', styleUrls: ['tooltip-demo.css'], + changeDetection: ChangeDetectionStrategy.OnPush // make sure tooltip also works OnPush }) export class TooltipDemo { position: TooltipPosition = 'below'; diff --git a/src/demo-app/tsconfig.json b/src/demo-app/tsconfig.json index 7d14befdc59b..21b70e287a20 100644 --- a/src/demo-app/tsconfig.json +++ b/src/demo-app/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "declaration": true, + "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "es2015", "dom"], @@ -22,8 +22,11 @@ ] } }, - "exclude": [ - "*-aot.ts" + "files": [ + "./demo-app-types.d.ts", + "./demo-app-module.ts", + "./system-config.ts", + "./main.ts" ], "angularCompilerOptions": { "genDir": "../../dist", diff --git a/src/e2e-app/dialog/dialog-e2e.html b/src/e2e-app/dialog/dialog-e2e.html index 827bfee99ca0..b66e8dfd8c51 100644 --- a/src/e2e-app/dialog/dialog-e2e.html +++ b/src/e2e-app/dialog/dialog-e2e.html @@ -1,2 +1,5 @@ + + + diff --git a/src/e2e-app/dialog/dialog-e2e.ts b/src/e2e-app/dialog/dialog-e2e.ts index 068406bbc5cb..09f833d96d0b 100644 --- a/src/e2e-app/dialog/dialog-e2e.ts +++ b/src/e2e-app/dialog/dialog-e2e.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, ViewChild, TemplateRef} from '@angular/core'; import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material'; @Component({ @@ -9,6 +9,8 @@ import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material'; export class DialogE2E { dialogRef: MdDialogRef; + @ViewChild(TemplateRef) templateRef: TemplateRef; + constructor (private _dialog: MdDialog) { } private _openDialog(config?: MdDialogConfig) { @@ -28,6 +30,10 @@ export class DialogE2E { disableClose: true }); } + + openTemplate() { + this.dialogRef = this._dialog.open(this.templateRef); + } } @Component({ diff --git a/src/e2e-app/index.html b/src/e2e-app/index.html index e5f49fcb8937..e9ce94a3d3f7 100644 --- a/src/e2e-app/index.html +++ b/src/e2e-app/index.html @@ -17,6 +17,7 @@ Loading... + diff --git a/src/examples/button-overview/button-overview-example.css b/src/examples/button-overview/button-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/button-overview/button-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/button-overview/button-overview-example.html b/src/examples/button-overview/button-overview-example.html new file mode 100644 index 000000000000..56f6fa44fb6b --- /dev/null +++ b/src/examples/button-overview/button-overview-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/button-overview/button-overview-example.ts b/src/examples/button-overview/button-overview-example.ts new file mode 100644 index 000000000000..41a52022f263 --- /dev/null +++ b/src/examples/button-overview/button-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'button-overview-example', + templateUrl: './button-overview-example.html', +}) +export class ButtonOverviewExample {} diff --git a/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.css b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.css new file mode 100644 index 000000000000..d21ca3ce57fb --- /dev/null +++ b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.css @@ -0,0 +1,3 @@ +.example-selected-value { + margin: 15px 0; +} diff --git a/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.html b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.html new file mode 100644 index 000000000000..dd5cd2771788 --- /dev/null +++ b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.html @@ -0,0 +1,15 @@ + + + format_align_left + + + format_align_center + + + format_align_right + + + format_align_justify + + +

Selected value: {{group.value}}
diff --git a/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.ts b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.ts new file mode 100644 index 000000000000..d014b8a2846d --- /dev/null +++ b/src/examples/button-toggle-exclusive/button-toggle-exclusive-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'button-toggle-exclusive-example', + templateUrl: './button-toggle-exclusive-example.html', + styleUrls: ['./button-toggle-exclusive-example.css'], +}) +export class ButtonToggleExclusiveExample {} diff --git a/src/examples/button-toggle-overview/button-toggle-overview-example.css b/src/examples/button-toggle-overview/button-toggle-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/button-toggle-overview/button-toggle-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/button-toggle-overview/button-toggle-overview-example.html b/src/examples/button-toggle-overview/button-toggle-overview-example.html new file mode 100644 index 000000000000..bb215d5b5425 --- /dev/null +++ b/src/examples/button-toggle-overview/button-toggle-overview-example.html @@ -0,0 +1 @@ +Toggle me! diff --git a/src/examples/button-toggle-overview/button-toggle-overview-example.ts b/src/examples/button-toggle-overview/button-toggle-overview-example.ts new file mode 100644 index 000000000000..38a4afd0561d --- /dev/null +++ b/src/examples/button-toggle-overview/button-toggle-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'button-toggle-overview-example', + templateUrl: './button-toggle-overview-example.html', +}) +export class ButtonToggleOverviewExample {} diff --git a/src/examples/button-types/button-types-example.css b/src/examples/button-types/button-types-example.css new file mode 100644 index 000000000000..b0b34952fcc1 --- /dev/null +++ b/src/examples/button-types/button-types-example.css @@ -0,0 +1,9 @@ +.example-container { + width: 500px; +} + +.example-button-row { + display: flex; + align-items: center; + justify-content: space-around; +} diff --git a/src/examples/button-types/button-types-example.html b/src/examples/button-types/button-types-example.html new file mode 100644 index 000000000000..4c5783de9c7a --- /dev/null +++ b/src/examples/button-types/button-types-example.html @@ -0,0 +1,17 @@ +
+

Normal Buttons

+
+ + + + +
+ +

Link Buttons

+ +
diff --git a/src/examples/button-types/button-types-example.ts b/src/examples/button-types/button-types-example.ts new file mode 100644 index 000000000000..9dbb329f1e0d --- /dev/null +++ b/src/examples/button-types/button-types-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'button-types-example', + templateUrl: './button-types-example.html', + styleUrls: ['./button-types-example.css'], +}) +export class ButtonTypesExample {} diff --git a/src/examples/card-fancy/card-fancy-example.css b/src/examples/card-fancy/card-fancy-example.css new file mode 100644 index 000000000000..daef28f16152 --- /dev/null +++ b/src/examples/card-fancy/card-fancy-example.css @@ -0,0 +1,8 @@ +.example-card { + width: 400px; +} + +.example-header-image { + background-image: url('assets/img/examples/shiba1.jpg'); + background-size: cover; +} diff --git a/src/examples/card-fancy/card-fancy-example.html b/src/examples/card-fancy/card-fancy-example.html new file mode 100644 index 000000000000..07d25dfb8d8e --- /dev/null +++ b/src/examples/card-fancy/card-fancy-example.html @@ -0,0 +1,19 @@ + + +
+ Shiba Inu + Dog Breed +
+ + +

+ The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from Japan. + A small, agile dog that copes very well with mountainous terrain, the Shiba Inu was originally + bred for hunting. +

+
+ + + + +
diff --git a/src/examples/card-fancy/card-fancy-example.ts b/src/examples/card-fancy/card-fancy-example.ts new file mode 100644 index 000000000000..8e19840d7cd4 --- /dev/null +++ b/src/examples/card-fancy/card-fancy-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'card-fancy-example', + templateUrl: './card-fancy-example.html', + styleUrls: ['./card-fancy-example.css'], +}) +export class CardFancyExample {} diff --git a/src/examples/card-overview/card-overview-example.css b/src/examples/card-overview/card-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/card-overview/card-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/card-overview/card-overview-example.html b/src/examples/card-overview/card-overview-example.html new file mode 100644 index 000000000000..5e0c2b6dfe13 --- /dev/null +++ b/src/examples/card-overview/card-overview-example.html @@ -0,0 +1 @@ +Simple card diff --git a/src/examples/card-overview/card-overview-example.ts b/src/examples/card-overview/card-overview-example.ts new file mode 100644 index 000000000000..55fc9a690d54 --- /dev/null +++ b/src/examples/card-overview/card-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'card-overview-example', + templateUrl: './card-overview-example.html', +}) +export class CardOverviewExample {} diff --git a/src/examples/checkbox-configurable/checkbox-configurable-example.css b/src/examples/checkbox-configurable/checkbox-configurable-example.css new file mode 100644 index 000000000000..558293fbb5b8 --- /dev/null +++ b/src/examples/checkbox-configurable/checkbox-configurable-example.css @@ -0,0 +1,14 @@ +.example-h2 { + margin: 10px; +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; +} + +.example-margin { + margin: 0 10px; +} diff --git a/src/examples/checkbox-configurable/checkbox-configurable-example.html b/src/examples/checkbox-configurable/checkbox-configurable-example.html new file mode 100644 index 000000000000..5876c05c6c46 --- /dev/null +++ b/src/examples/checkbox-configurable/checkbox-configurable-example.html @@ -0,0 +1,40 @@ + + +

Checkbox configuration

+ +
+ Checked + Indeterminate +
+ +
+ + + Start + End + +
+ +
+ Disabled +
+
+
+ + + +

Result

+ +
+ + I'm a checkbox + +
+
+
+ diff --git a/src/examples/checkbox-configurable/checkbox-configurable-example.ts b/src/examples/checkbox-configurable/checkbox-configurable-example.ts new file mode 100644 index 000000000000..2c0149199cc5 --- /dev/null +++ b/src/examples/checkbox-configurable/checkbox-configurable-example.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'checkbox-configurable-example', + templateUrl: './checkbox-configurable-example.html', + styleUrls: ['./checkbox-configurable-example.css'], +}) +export class CheckboxConfigurableExample { + checked = false; + indeterminate = false; + align = 'start'; + disabled = false; +} diff --git a/src/examples/checkbox-overview/checkbox-overview-example.css b/src/examples/checkbox-overview/checkbox-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/checkbox-overview/checkbox-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/checkbox-overview/checkbox-overview-example.html b/src/examples/checkbox-overview/checkbox-overview-example.html new file mode 100644 index 000000000000..c301bbe5ccc2 --- /dev/null +++ b/src/examples/checkbox-overview/checkbox-overview-example.html @@ -0,0 +1 @@ +Check me! diff --git a/src/examples/checkbox-overview/checkbox-overview-example.ts b/src/examples/checkbox-overview/checkbox-overview-example.ts new file mode 100644 index 000000000000..be9aefc592f0 --- /dev/null +++ b/src/examples/checkbox-overview/checkbox-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'checkbox-overview-example', + templateUrl: './checkbox-overview-example.html', +}) +export class CheckboxOverviewExample {} diff --git a/src/examples/chips-overview/chips-overview-example.css b/src/examples/chips-overview/chips-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/chips-overview/chips-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/chips-overview/chips-overview-example.html b/src/examples/chips-overview/chips-overview-example.html new file mode 100644 index 000000000000..bb0410569251 --- /dev/null +++ b/src/examples/chips-overview/chips-overview-example.html @@ -0,0 +1,6 @@ + + One fish + Two fish + Primary fish + Accent fish + diff --git a/src/examples/chips-overview/chips-overview-example.ts b/src/examples/chips-overview/chips-overview-example.ts new file mode 100644 index 000000000000..fa1bbf7c12c4 --- /dev/null +++ b/src/examples/chips-overview/chips-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'chips-overview-example', + templateUrl: './chips-overview-example.html', +}) +export class ChipsOverviewExample {} diff --git a/src/examples/chips-stacked/chips-stacked-example.css b/src/examples/chips-stacked/chips-stacked-example.css new file mode 100644 index 000000000000..dfb2f8c00aa2 --- /dev/null +++ b/src/examples/chips-stacked/chips-stacked-example.css @@ -0,0 +1,3 @@ +md-chip { + max-width: 200px; +} diff --git a/src/examples/chips-stacked/chips-stacked-example.html b/src/examples/chips-stacked/chips-stacked-example.html new file mode 100644 index 000000000000..27677b1c6e6d --- /dev/null +++ b/src/examples/chips-stacked/chips-stacked-example.html @@ -0,0 +1,7 @@ + + + {{chipColor.name}} + + diff --git a/src/examples/chips-stacked/chips-stacked-example.ts b/src/examples/chips-stacked/chips-stacked-example.ts new file mode 100644 index 000000000000..8c48ccc929e0 --- /dev/null +++ b/src/examples/chips-stacked/chips-stacked-example.ts @@ -0,0 +1,18 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'chips-stacked-example', + templateUrl: './chips-stacked-example.html', + styleUrls: ['./chips-stacked-example.css'], +}) +export class ChipsStackedExample { + color: string; + + availableColors = [ + { name: 'none', color: '' }, + { name: 'Primary', color: 'primary' }, + { name: 'Accent', color: 'accent' }, + { name: 'Warn', color: 'warn' } + ]; +} diff --git a/src/examples/dialog-elements/dialog-elements-example-dialog.html b/src/examples/dialog-elements/dialog-elements-example-dialog.html new file mode 100644 index 000000000000..5238b0eb6c19 --- /dev/null +++ b/src/examples/dialog-elements/dialog-elements-example-dialog.html @@ -0,0 +1,5 @@ +

Dialog with elements

+
This dialog showcases the title, close, content and actions elements.
+
+ +
diff --git a/src/examples/dialog-elements/dialog-elements-example.css b/src/examples/dialog-elements/dialog-elements-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/dialog-elements/dialog-elements-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/dialog-elements/dialog-elements-example.html b/src/examples/dialog-elements/dialog-elements-example.html new file mode 100644 index 000000000000..40637c4bd638 --- /dev/null +++ b/src/examples/dialog-elements/dialog-elements-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/dialog-elements/dialog-elements-example.ts b/src/examples/dialog-elements/dialog-elements-example.ts new file mode 100644 index 000000000000..aba941a571b0 --- /dev/null +++ b/src/examples/dialog-elements/dialog-elements-example.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; +import {MdDialog} from '@angular/material'; + + +@Component({ + selector: 'dialog-elements-example', + templateUrl: './dialog-elements-example.html', +}) +export class DialogElementsExample { + constructor(public dialog: MdDialog) { } + + openDialog() { + this.dialog.open(DialogElementsExampleDialog); + } +} + + +@Component({ + selector: 'dialog-elements-example-dialog', + templateUrl: './dialog-elements-example-dialog.html', +}) +export class DialogElementsExampleDialog { } diff --git a/src/examples/dialog-overview/dialog-overview-example-dialog.html b/src/examples/dialog-overview/dialog-overview-example-dialog.html new file mode 100644 index 000000000000..751e1339b43c --- /dev/null +++ b/src/examples/dialog-overview/dialog-overview-example-dialog.html @@ -0,0 +1 @@ +

Hi, I'm a dialog!

diff --git a/src/examples/dialog-overview/dialog-overview-example.css b/src/examples/dialog-overview/dialog-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/dialog-overview/dialog-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/dialog-overview/dialog-overview-example.html b/src/examples/dialog-overview/dialog-overview-example.html new file mode 100644 index 000000000000..176a3c411e7f --- /dev/null +++ b/src/examples/dialog-overview/dialog-overview-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/dialog-overview/dialog-overview-example.ts b/src/examples/dialog-overview/dialog-overview-example.ts new file mode 100644 index 000000000000..9bc9882ac034 --- /dev/null +++ b/src/examples/dialog-overview/dialog-overview-example.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; +import {MdDialog} from '@angular/material'; + + +@Component({ + selector: 'dialog-overview-example', + templateUrl: './dialog-overview-example.html', +}) +export class DialogOverviewExample { + constructor(public dialog: MdDialog) {} + + openDialog() { + this.dialog.open(DialogOverviewExampleDialog); + } +} + + +@Component({ + selector: 'dialog-overview-example-dialog', + templateUrl: './dialog-overview-example-dialog.html', +}) +export class DialogOverviewExampleDialog {} diff --git a/src/examples/dialog-result/dialog-result-example-dialog.html b/src/examples/dialog-result/dialog-result-example-dialog.html new file mode 100644 index 000000000000..0e4dd6339a94 --- /dev/null +++ b/src/examples/dialog-result/dialog-result-example-dialog.html @@ -0,0 +1,6 @@ +

Dialog

+
What would you like to do?
+
+ + +
diff --git a/src/examples/dialog-result/dialog-result-example.css b/src/examples/dialog-result/dialog-result-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/dialog-result/dialog-result-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/dialog-result/dialog-result-example.html b/src/examples/dialog-result/dialog-result-example.html new file mode 100644 index 000000000000..04850b42d247 --- /dev/null +++ b/src/examples/dialog-result/dialog-result-example.html @@ -0,0 +1,2 @@ + +You chose: {{selectedOption}} diff --git a/src/examples/dialog-result/dialog-result-example.ts b/src/examples/dialog-result/dialog-result-example.ts new file mode 100644 index 000000000000..ec25f92f1341 --- /dev/null +++ b/src/examples/dialog-result/dialog-result-example.ts @@ -0,0 +1,29 @@ +import {Component} from '@angular/core'; +import {MdDialog, MdDialogRef} from '@angular/material'; + + +@Component({ + selector: 'dialog-result-example', + templateUrl: './dialog-result-example.html', +}) +export class DialogResultExample { + selectedOption: string; + + constructor(public dialog: MdDialog) {} + + openDialog() { + let dialogRef = this.dialog.open(DialogResultExampleDialog); + dialogRef.afterClosed().subscribe(result => { + this.selectedOption = result; + }); + } +} + + +@Component({ + selector: 'dialog-result-example-dialog', + templateUrl: './dialog-result-example-dialog.html', +}) +export class DialogResultExampleDialog { + constructor(public dialogRef: MdDialogRef) {} +} diff --git a/src/examples/example-data.ts b/src/examples/example-data.ts new file mode 100644 index 000000000000..a1fc1c8838a6 --- /dev/null +++ b/src/examples/example-data.ts @@ -0,0 +1,48 @@ +import {EXAMPLE_COMPONENTS} from './example-module'; + + +/** + * Example data + * with information about Component name, selector, files used in example, and path to examples + */ +export class ExampleData { + // TODO: figure out how do we get these variables. + description: string = 'Some description for material'; + // TODO: use real example and delete the example/ folder. + examplePath = '/assets/example/'; + exampleFiles = ['button-demo.html', 'button-demo.scss', 'button-demo.ts']; + + // TODO: extract these variables from example code. + selectorName = 'button-demo'; + indexFilename = 'button-demo'; + componentName = 'ButtonDemo'; + + constructor(example: string) { + if (example && EXAMPLE_COMPONENTS[example]) { + this.examplePath = `/assets/plunker/examples/${example}/`; + // TODO(tinayuangao): Do not hard-code extensions + this.exampleFiles = ['html', 'ts', 'css'] + .map((extension) => `${example}-example.${extension}`); + if (EXAMPLE_COMPONENTS[example].additionalFiles) { + this.exampleFiles = this.exampleFiles.concat(EXAMPLE_COMPONENTS[example].additionalFiles); + } + this.selectorName = this.indexFilename = `${example}-example`; + + var exampleName = example.replace(/(?:^\w|\b\w)/g, function(letter) { + return letter.toUpperCase(); + }); + + if (EXAMPLE_COMPONENTS[example].title) { + this.description = EXAMPLE_COMPONENTS[example].title; + } else { + this.description = exampleName.replace(/[\-]+/g, ' ') + ' Example'; + } + + if (EXAMPLE_COMPONENTS[example].selectorName) { + this.componentName = EXAMPLE_COMPONENTS[example].selectorName; + } else { + this.componentName = exampleName.replace(/[\-]+/g, '') + 'Example'; + } + } + } +} diff --git a/src/examples/example-module.ts b/src/examples/example-module.ts new file mode 100644 index 000000000000..60293aeb56ff --- /dev/null +++ b/src/examples/example-module.ts @@ -0,0 +1,232 @@ +import {NgModule} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {CommonModule} from '@angular/common'; +import {MaterialModule} from '@angular/material'; +import {ButtonOverviewExample} from './button-overview/button-overview-example'; +import {ButtonTypesExample} from './button-types/button-types-example'; +import {CheckboxOverviewExample} from './checkbox-overview/checkbox-overview-example'; +import {SliderConfigurableExample} from './slider-configurable/slider-configurable-example'; +import {TabsOverviewExample} from './tabs-overview/tabs-overview-example'; +import { + PizzaPartyComponent, + SnackBarComponentExample +} from './snack-bar-component/snack-bar-component-example'; +import { + ProgressBarConfigurableExample +} from './progress-bar-configurable/progress-bar-configurable-example'; +import { + DialogOverviewExampleDialog, + DialogOverviewExample +} from './dialog-overview/dialog-overview-example'; +import {RadioNgModelExample} from './radio-ng-model/radio-ng-model-example'; +import {CardFancyExample} from './card-fancy/card-fancy-example'; +import {ToolbarOverviewExample} from './toolbar-overview/toolbar-overview-example'; +import {ToolbarMultirowExample} from './toolbar-multirow/toolbar-multirow-example'; +import {MenuIconsExample} from './menu-icons/menu-icons-example'; +import {GridListDynamicExample} from './grid-list-dynamic/grid-list-dynamic-example'; +import {IconOverviewExample} from './icon-overview/icon-overview-example'; +import {ProgressBarOverviewExample} from './progress-bar-overview/progress-bar-overview-example'; +import {SlideToggleOverviewExample} from './slide-toggle-overview/slide-toggle-overview-example'; +import {InputOverviewExample} from './input-overview/input-overview-example'; +import {MenuOverviewExample} from './menu-overview/menu-overview-example'; +import {CheckboxConfigurableExample} from './checkbox-configurable/checkbox-configurable-example'; +import { + ButtonToggleExclusiveExample +} from './button-toggle-exclusive/button-toggle-exclusive-example'; +import {ListSectionsExample} from './list-sections/list-sections-example'; +import {SnackBarOverviewExample} from './snack-bar-overview/snack-bar-overview-example'; +import { + DialogResultExampleDialog, + DialogResultExample +} from './dialog-result/dialog-result-example'; +import { + DialogElementsExampleDialog, + DialogElementsExample +} from './dialog-elements/dialog-elements-example'; +import {TooltipOverviewExample} from './tooltip-overview/tooltip-overview-example'; +import {ButtonToggleOverviewExample} from './button-toggle-overview/button-toggle-overview-example'; +import {GridListOverviewExample} from './grid-list-overview/grid-list-overview-example'; +import {TooltipPositionExample} from './tooltip-position/tooltip-position-example'; +import { + ProgressSpinnerConfigurableExample +} from './progress-spinner-configurable/progress-spinner-configurable-example'; +import {InputFormExample} from './input-form/input-form-example'; +import {ListOverviewExample} from './list-overview/list-overview-example'; +import {SliderOverviewExample} from './slider-overview/slider-overview-example'; +import { + SlideToggleConfigurableExample +} from './slide-toggle-configurable/slide-toggle-configurable-example'; +import {IconSvgExample} from './icon-svg-example/icon-svg-example'; +import {SidenavFabExample} from './sidenav-fab/sidenav-fab-example'; +import {CardOverviewExample} from './card-overview/card-overview-example'; +import { + ProgressSpinnerOverviewExample +} from './progress-spinner-overview/progress-spinner-overview-example'; +import {TabsTemplateLabelExample} from './tabs-template-label/tabs-template-label-example'; +import {RadioOverviewExample} from './radio-overview/radio-overview-example'; +import {SidenavOverviewExample} from './sidenav-overview/sidenav-overview-example'; +import {SelectOverviewExample} from './select-overview/select-overview-example'; +import {ChipsOverviewExample} from './chips-overview/chips-overview-example'; +import {ChipsStackedExample} from './chips-stacked/chips-stacked-example'; +import {SelectFormExample} from './select-form/select-form-example'; + + +export interface LiveExample { + title: string; + component: any; + additionalFiles: string[]; + selectorName: string; +} + +/** + * The list of example components. + * Key is the example name which will be used in `material-docs-example="key"`. + * Value is the component. + */ +export const EXAMPLE_COMPONENTS = { + 'button-overview': {title: 'Basic buttons', component: ButtonOverviewExample}, + 'button-types': {title: 'Button varieties', component: ButtonTypesExample}, + 'button-toggle-exclusive': { + title: 'Exclusive selection', + component: ButtonToggleExclusiveExample + }, + 'button-toggle-overview': {title: 'Basic button-toggles', component: ButtonToggleOverviewExample}, + 'chips-overview': {title: 'Basic chips', component: ChipsOverviewExample}, + 'chips-stacked': {title: 'Stacked chips', component: ChipsStackedExample}, + 'card-fancy': {title: 'Card with multiple sections', component: CardFancyExample}, + 'card-overview': {title: 'Basic cards', component: CardOverviewExample}, + 'checkbox-configurable': {title: 'Configurable checkbox', component: CheckboxConfigurableExample}, + 'checkbox-overview': {title: 'Basic checkboxes', component: CheckboxOverviewExample}, + 'dialog-overview': { + title: 'Basic dialog', + component: DialogOverviewExample, + additionalFiles: ['dialog-overview-example-dialog.html'], + selectorName: 'DialogOverviewExample, DialogOverviewExampleDialog', + }, + 'dialog-result': { + title: 'Dialog with a result', + component: DialogResultExample, + additionalFiles: ['dialog-result-example-dialog.html'], + selectorName: 'DialogResultExample, DialogResultExampleDialog', + }, + 'dialog-elements': { + title: 'Dialog elements', + component: DialogElementsExample, + additionalFiles: ['dialog-elements-example-dialog.html'], + selectorName: 'DialogElementsExample, DialogElementsExampleDialog', + }, + 'grid-list-dynamic': {title: 'Dynamic grid-list', component: GridListDynamicExample}, + 'grid-list-overview': {title: 'Basic grid-list', component: GridListOverviewExample}, + 'icon-overview': {title: 'Basic icons', component: IconOverviewExample}, + 'icon-svg': {title: 'SVG icons', component: IconSvgExample}, + 'input-form': {title: 'Inputs in a form', component: InputFormExample}, + 'input-overview': {title: 'Basic inputs', component: InputOverviewExample}, + 'list-overview': {title: 'Basic list', component: ListOverviewExample}, + 'list-sections': {title: 'List with sections', component: ListSectionsExample}, + 'menu-icons': {title: 'Menu with icons', component: MenuIconsExample}, + 'menu-overview': {title: 'Basic menu', component: MenuOverviewExample}, + 'progress-bar-configurable': { + title: 'Configurable progress-bar', + component: ProgressBarConfigurableExample + }, + 'progress-bar-overview': {title: 'Basic progress-bar', component: ProgressBarOverviewExample}, + 'progress-spinner-configurable': { + title: 'Configurable progress-bar', + component: ProgressSpinnerConfigurableExample + }, + 'progress-spinner-overview': { + title: 'Basic progress-spinner', + component: ProgressSpinnerOverviewExample + }, + 'radio-ng-model': {title: 'Radios with ngModel', component: RadioNgModelExample}, + 'radio-overview': {title: 'Basic radios', component: RadioOverviewExample}, + 'select-overview': {title: 'Basic select', component: SelectOverviewExample}, + 'select-form': {title: 'Select in a form', component: SelectFormExample}, + 'sidenav-fab': {title: 'Sidenav with a FAB', component: SidenavFabExample}, + 'sidenav-overview': {title: 'Basic sidenav', component: SidenavOverviewExample}, + 'slider-configurable': {title: 'Configurable slider', component: SliderConfigurableExample}, + 'slider-overview': {title: 'Basic slider', component: SliderOverviewExample}, + 'slide-toggle-configurable': { + title: 'Configurable slide-toggle', + component: SlideToggleConfigurableExample + }, + 'slide-toggle-overview': {title: 'Basic slide-toggles', component: SlideToggleOverviewExample}, + 'snack-bar-component': { + title: 'Snack-bar with a custom component', + component: SnackBarComponentExample + }, + 'snack-bar-overview': {title: 'Basic snack-bar', component: SnackBarOverviewExample}, + 'tabs-overview': {title: 'Basic tabs', component: TabsOverviewExample}, + 'tabs-template-label': {title: 'Coming soon!', component: TabsTemplateLabelExample}, + 'toolbar-multirow': {title: 'Multi-row toolbar', component: ToolbarMultirowExample}, + 'toolbar-overview': {title: 'basic toolbar', component: ToolbarOverviewExample}, + 'tooltip-overview': {title: 'Basic tooltip', component: TooltipOverviewExample}, + 'tooltip-position': {title: 'Tooltip with custom position', component: TooltipPositionExample}, +}; + +/** + * The list of all example components. + * We need to put them in both `declarations` and `entryComponents` to make them work. + */ +export const EXAMPLE_LIST = [ + ButtonOverviewExample, + ButtonToggleExclusiveExample, + ButtonToggleOverviewExample, + ButtonTypesExample, + CardFancyExample, + CardOverviewExample, + ChipsOverviewExample, + ChipsStackedExample, + CheckboxConfigurableExample, + CheckboxOverviewExample, + DialogOverviewExample, + DialogOverviewExampleDialog, + DialogResultExample, + DialogResultExampleDialog, + DialogElementsExample, + DialogElementsExampleDialog, + GridListDynamicExample, + GridListOverviewExample, + IconOverviewExample, + IconSvgExample, + InputFormExample, + InputOverviewExample, + ListOverviewExample, + ListSectionsExample, + MenuIconsExample, + MenuOverviewExample, + ProgressBarConfigurableExample, + ProgressBarOverviewExample, + ProgressSpinnerConfigurableExample, + ProgressSpinnerOverviewExample, + RadioNgModelExample, + RadioOverviewExample, + SidenavFabExample, + SelectOverviewExample, + SelectFormExample, + SidenavOverviewExample, + SliderConfigurableExample, + SliderOverviewExample, + SlideToggleConfigurableExample, + SlideToggleOverviewExample, + SnackBarComponentExample, + PizzaPartyComponent, + SnackBarOverviewExample, + TabsOverviewExample, + TabsTemplateLabelExample, + ToolbarMultirowExample, + ToolbarOverviewExample, + TooltipOverviewExample, + TooltipPositionExample, +]; + +@NgModule({ + declarations: EXAMPLE_LIST, + entryComponents: EXAMPLE_LIST, + imports: [ + MaterialModule, + FormsModule, + CommonModule, + ] +}) +export class ExampleModule { } diff --git a/src/examples/grid-list-dynamic/grid-list-dynamic-example.css b/src/examples/grid-list-dynamic/grid-list-dynamic-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/grid-list-dynamic/grid-list-dynamic-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/grid-list-dynamic/grid-list-dynamic-example.html b/src/examples/grid-list-dynamic/grid-list-dynamic-example.html new file mode 100644 index 000000000000..19c84dd5b853 --- /dev/null +++ b/src/examples/grid-list-dynamic/grid-list-dynamic-example.html @@ -0,0 +1,9 @@ + + + {{tile.text}} + + diff --git a/src/examples/grid-list-dynamic/grid-list-dynamic-example.ts b/src/examples/grid-list-dynamic/grid-list-dynamic-example.ts new file mode 100644 index 000000000000..8f0834fa0aeb --- /dev/null +++ b/src/examples/grid-list-dynamic/grid-list-dynamic-example.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'grid-list-dynamic-example', + templateUrl: './grid-list-dynamic-example.html', +}) +export class GridListDynamicExample { + tiles = [ + {text: 'One', cols: 3, rows: 1, color: 'lightblue'}, + {text: 'Two', cols: 1, rows: 2, color: 'lightgreen'}, + {text: 'Three', cols: 1, rows: 1, color: 'lightpink'}, + {text: 'Four', cols: 2, rows: 1, color: '#DDBDF1'}, + ]; +} diff --git a/src/examples/grid-list-overview/grid-list-overview-example.css b/src/examples/grid-list-overview/grid-list-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/grid-list-overview/grid-list-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/grid-list-overview/grid-list-overview-example.html b/src/examples/grid-list-overview/grid-list-overview-example.html new file mode 100644 index 000000000000..bbcdb140f98b --- /dev/null +++ b/src/examples/grid-list-overview/grid-list-overview-example.html @@ -0,0 +1,6 @@ + + 1 + 2 + 3 + 4 + diff --git a/src/examples/grid-list-overview/grid-list-overview-example.ts b/src/examples/grid-list-overview/grid-list-overview-example.ts new file mode 100644 index 000000000000..2485bb516fa8 --- /dev/null +++ b/src/examples/grid-list-overview/grid-list-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'grid-list-overview-example', + templateUrl: './grid-list-overview-example.html', +}) +export class GridListOverviewExample {} diff --git a/src/examples/icon-overview/icon-overview-example.css b/src/examples/icon-overview/icon-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/icon-overview/icon-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/icon-overview/icon-overview-example.html b/src/examples/icon-overview/icon-overview-example.html new file mode 100644 index 000000000000..dd46af80f6cb --- /dev/null +++ b/src/examples/icon-overview/icon-overview-example.html @@ -0,0 +1 @@ +home diff --git a/src/examples/icon-overview/icon-overview-example.ts b/src/examples/icon-overview/icon-overview-example.ts new file mode 100644 index 000000000000..90ed28a61266 --- /dev/null +++ b/src/examples/icon-overview/icon-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'icon-overview-example', + templateUrl: './icon-overview-example.html', +}) +export class IconOverviewExample {} diff --git a/src/examples/icon-svg-example/icon-svg-example.css b/src/examples/icon-svg-example/icon-svg-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/icon-svg-example/icon-svg-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/icon-svg-example/icon-svg-example.html b/src/examples/icon-svg-example/icon-svg-example.html new file mode 100644 index 000000000000..ac74cf7908dd --- /dev/null +++ b/src/examples/icon-svg-example/icon-svg-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/icon-svg-example/icon-svg-example.ts b/src/examples/icon-svg-example/icon-svg-example.ts new file mode 100644 index 000000000000..b9689b4208a1 --- /dev/null +++ b/src/examples/icon-svg-example/icon-svg-example.ts @@ -0,0 +1,16 @@ +import {Component} from '@angular/core'; +import {DomSanitizer} from '@angular/platform-browser'; +import {MdIconRegistry} from '@angular/material'; + + +@Component({ + selector: 'icon-svg-example', + templateUrl: './icon-svg-example.html', +}) +export class IconSvgExample { + constructor(iconRegistry: MdIconRegistry, sanitizer: DomSanitizer) { + iconRegistry.addSvgIcon( + 'thumbs-up', + sanitizer.bypassSecurityTrustResourceUrl('assets/img/examples/thumbup-icon.svg')); + } +} diff --git a/src/examples/input-form/input-form-example.css b/src/examples/input-form/input-form-example.css new file mode 100644 index 000000000000..269cb291ad0c --- /dev/null +++ b/src/examples/input-form/input-form-example.css @@ -0,0 +1,7 @@ +.example-form { + width: 500px; +} + +.example-full-width { + width: 100%; +} diff --git a/src/examples/input-form/input-form-example.html b/src/examples/input-form/input-form-example.html new file mode 100644 index 000000000000..04c3bb5ae756 --- /dev/null +++ b/src/examples/input-form/input-form-example.html @@ -0,0 +1,36 @@ +
+ + + + + + + +
+ + + +
+ +

+ + + + + + +

+ + + + + +
+ + + + + + {{postalCode.value.length}} / 5 +
+
diff --git a/src/examples/input-form/input-form-example.ts b/src/examples/input-form/input-form-example.ts new file mode 100644 index 000000000000..2e209d9f50ad --- /dev/null +++ b/src/examples/input-form/input-form-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'input-form-example', + templateUrl: './input-form-example.html', + styleUrls: ['./input-form-example.css'], +}) +export class InputFormExample {} diff --git a/src/examples/input-overview/input-overview-example.css b/src/examples/input-overview/input-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/input-overview/input-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/input-overview/input-overview-example.html b/src/examples/input-overview/input-overview-example.html new file mode 100644 index 000000000000..c864c5d3622d --- /dev/null +++ b/src/examples/input-overview/input-overview-example.html @@ -0,0 +1,3 @@ + + + diff --git a/src/examples/input-overview/input-overview-example.ts b/src/examples/input-overview/input-overview-example.ts new file mode 100644 index 000000000000..2bdc3fd6b206 --- /dev/null +++ b/src/examples/input-overview/input-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'input-overview-example', + templateUrl: './input-overview-example.html', +}) +export class InputOverviewExample {} diff --git a/src/examples/list-overview/list-overview-example.css b/src/examples/list-overview/list-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/list-overview/list-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/list-overview/list-overview-example.html b/src/examples/list-overview/list-overview-example.html new file mode 100644 index 000000000000..ada715ee0acf --- /dev/null +++ b/src/examples/list-overview/list-overview-example.html @@ -0,0 +1,5 @@ + + Item 1 + Item 2 + Item 3 + diff --git a/src/examples/list-overview/list-overview-example.ts b/src/examples/list-overview/list-overview-example.ts new file mode 100644 index 000000000000..f38e186020d8 --- /dev/null +++ b/src/examples/list-overview/list-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'list-overview-example', + templateUrl: './list-overview-example.html', +}) +export class ListOverviewExample {} diff --git a/src/examples/list-sections/list-sections-example.css b/src/examples/list-sections/list-sections-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/list-sections/list-sections-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/list-sections/list-sections-example.html b/src/examples/list-sections/list-sections-example.html new file mode 100644 index 000000000000..fe81facd961f --- /dev/null +++ b/src/examples/list-sections/list-sections-example.html @@ -0,0 +1,15 @@ + +

Folders

+ + folder +

{{folder.name}}

+

{{folder.updated | date}}

+
+ +

Notes

+ + note +

{{note.name}}

+

{{note.updated | date}}

+
+
diff --git a/src/examples/list-sections/list-sections-example.ts b/src/examples/list-sections/list-sections-example.ts new file mode 100644 index 000000000000..d6fd703390aa --- /dev/null +++ b/src/examples/list-sections/list-sections-example.ts @@ -0,0 +1,33 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'list-sections-example', + templateUrl: './list-sections-example.html', +}) +export class ListSectionsExample { + folders = [ + { + name: 'Photos', + updated: new Date('1/1/16'), + }, + { + name: 'Recipes', + updated: new Date('1/17/16'), + }, + { + name: 'Work', + updated: new Date('1/28/16'), + } + ]; + notes = [ + { + name: 'Vacation Itinerary', + updated: new Date('2/20/16'), + }, + { + name: 'Kitchen Remodel', + updated: new Date('1/18/16'), + } + ]; +} diff --git a/src/examples/menu-icons/menu-icons-example.css b/src/examples/menu-icons/menu-icons-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/menu-icons/menu-icons-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/menu-icons/menu-icons-example.html b/src/examples/menu-icons/menu-icons-example.html new file mode 100644 index 000000000000..e14942451644 --- /dev/null +++ b/src/examples/menu-icons/menu-icons-example.html @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/examples/menu-icons/menu-icons-example.ts b/src/examples/menu-icons/menu-icons-example.ts new file mode 100644 index 000000000000..070bc434bdb7 --- /dev/null +++ b/src/examples/menu-icons/menu-icons-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'menu-icons-example', + templateUrl: './menu-icons-example.html', +}) +export class MenuIconsExample {} diff --git a/src/examples/menu-overview/menu-overview-example.css b/src/examples/menu-overview/menu-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/menu-overview/menu-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/menu-overview/menu-overview-example.html b/src/examples/menu-overview/menu-overview-example.html new file mode 100644 index 000000000000..2b5d246c298e --- /dev/null +++ b/src/examples/menu-overview/menu-overview-example.html @@ -0,0 +1,5 @@ + + + + + diff --git a/src/examples/menu-overview/menu-overview-example.ts b/src/examples/menu-overview/menu-overview-example.ts new file mode 100644 index 000000000000..b5b8c8a8df47 --- /dev/null +++ b/src/examples/menu-overview/menu-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'menu-overview-example', + templateUrl: './menu-overview-example.html', +}) +export class MenuOverviewExample {} diff --git a/src/examples/progress-bar-configurable/progress-bar-configurable-example.css b/src/examples/progress-bar-configurable/progress-bar-configurable-example.css new file mode 100644 index 000000000000..558293fbb5b8 --- /dev/null +++ b/src/examples/progress-bar-configurable/progress-bar-configurable-example.css @@ -0,0 +1,14 @@ +.example-h2 { + margin: 10px; +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; +} + +.example-margin { + margin: 0 10px; +} diff --git a/src/examples/progress-bar-configurable/progress-bar-configurable-example.html b/src/examples/progress-bar-configurable/progress-bar-configurable-example.html new file mode 100644 index 000000000000..93937b77a8b8 --- /dev/null +++ b/src/examples/progress-bar-configurable/progress-bar-configurable-example.html @@ -0,0 +1,63 @@ + + +

Progress bar configuration

+ +
+ + + + Primary + + + Accent + + + Warn + + +
+ +
+ + + + Determinate + + + Indeterminate + + + Buffer + + + Query + + +
+ +
+ + +
+
+ + +
+
+
+ + + +

Result

+ +
+ + +
+
+
diff --git a/src/examples/progress-bar-configurable/progress-bar-configurable-example.ts b/src/examples/progress-bar-configurable/progress-bar-configurable-example.ts new file mode 100644 index 000000000000..f3fa937826bd --- /dev/null +++ b/src/examples/progress-bar-configurable/progress-bar-configurable-example.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'progress-bar-configurable-example', + templateUrl: './progress-bar-configurable-example.html', + styleUrls: ['./progress-bar-configurable-example.css'], +}) +export class ProgressBarConfigurableExample { + color = 'primary'; + mode = 'determinate'; + value = 50; + bufferValue = 75; +} diff --git a/src/examples/progress-bar-overview/progress-bar-overview-example.css b/src/examples/progress-bar-overview/progress-bar-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/progress-bar-overview/progress-bar-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/progress-bar-overview/progress-bar-overview-example.html b/src/examples/progress-bar-overview/progress-bar-overview-example.html new file mode 100644 index 000000000000..8ab16e9cfc13 --- /dev/null +++ b/src/examples/progress-bar-overview/progress-bar-overview-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/progress-bar-overview/progress-bar-overview-example.ts b/src/examples/progress-bar-overview/progress-bar-overview-example.ts new file mode 100644 index 000000000000..cf11f1cf2578 --- /dev/null +++ b/src/examples/progress-bar-overview/progress-bar-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'progress-bar-overview-example', + templateUrl: './progress-bar-overview-example.html', +}) +export class ProgressBarOverviewExample {} diff --git a/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.css b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.css new file mode 100644 index 000000000000..558293fbb5b8 --- /dev/null +++ b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.css @@ -0,0 +1,14 @@ +.example-h2 { + margin: 10px; +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; +} + +.example-margin { + margin: 0 10px; +} diff --git a/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.html b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.html new file mode 100644 index 000000000000..a034e2f85150 --- /dev/null +++ b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.html @@ -0,0 +1,49 @@ + + +

Progress circle configuration

+ +
+ + + + Primary + + + Accent + + + Warn + + +
+ +
+ + + + Determinate + + + Indeterminate + + +
+ +
+ + +
+
+
+ + +

Result

+ + + +
+
diff --git a/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.ts b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.ts new file mode 100644 index 000000000000..e8538a0f8ed1 --- /dev/null +++ b/src/examples/progress-spinner-configurable/progress-spinner-configurable-example.ts @@ -0,0 +1,13 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'progress-spinner-configurable-example', + templateUrl: './progress-spinner-configurable-example.html', + styleUrls: ['./progress-spinner-configurable-example.css'], +}) +export class ProgressSpinnerConfigurableExample { + color = 'praimry'; + mode = 'determinate'; + value = 50; +} diff --git a/src/examples/progress-spinner-overview/progress-spinner-overview-example.css b/src/examples/progress-spinner-overview/progress-spinner-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/progress-spinner-overview/progress-spinner-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/progress-spinner-overview/progress-spinner-overview-example.html b/src/examples/progress-spinner-overview/progress-spinner-overview-example.html new file mode 100644 index 000000000000..cd768c67385c --- /dev/null +++ b/src/examples/progress-spinner-overview/progress-spinner-overview-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/progress-spinner-overview/progress-spinner-overview-example.ts b/src/examples/progress-spinner-overview/progress-spinner-overview-example.ts new file mode 100644 index 000000000000..89cce852a1f1 --- /dev/null +++ b/src/examples/progress-spinner-overview/progress-spinner-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'progress-spinner-overview-example', + templateUrl: './progress-spinner-overview-example.html', +}) +export class ProgressSpinnerOverviewExample {} diff --git a/src/examples/radio-ng-model/radio-ng-model-example.css b/src/examples/radio-ng-model/radio-ng-model-example.css new file mode 100644 index 000000000000..f70c1b3b53dd --- /dev/null +++ b/src/examples/radio-ng-model/radio-ng-model-example.css @@ -0,0 +1,12 @@ +.example-radio-group { + display: inline-flex; + flex-direction: column; +} + +.example-radio-button { + margin: 5px; +} + +.example-selected-value { + margin: 15px 0; +} diff --git a/src/examples/radio-ng-model/radio-ng-model-example.html b/src/examples/radio-ng-model/radio-ng-model-example.html new file mode 100644 index 000000000000..108971b5739a --- /dev/null +++ b/src/examples/radio-ng-model/radio-ng-model-example.html @@ -0,0 +1,6 @@ + + + {{season}} + + +
Your favorite season is: {{favoriteSeason}}
diff --git a/src/examples/radio-ng-model/radio-ng-model-example.ts b/src/examples/radio-ng-model/radio-ng-model-example.ts new file mode 100644 index 000000000000..42e93bc5d1ba --- /dev/null +++ b/src/examples/radio-ng-model/radio-ng-model-example.ts @@ -0,0 +1,18 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'radio-ng-model-example', + templateUrl: './radio-ng-model-example.html', + styleUrls: ['./radio-ng-model-example.css'], +}) +export class RadioNgModelExample { + favoriteSeason: string; + + seasons = [ + 'Winter', + 'Spring', + 'Summer', + 'Autumn', + ]; +} diff --git a/src/examples/radio-overview/radio-overview-example.css b/src/examples/radio-overview/radio-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/radio-overview/radio-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/radio-overview/radio-overview-example.html b/src/examples/radio-overview/radio-overview-example.html new file mode 100644 index 000000000000..4038381cc49e --- /dev/null +++ b/src/examples/radio-overview/radio-overview-example.html @@ -0,0 +1,4 @@ + + Option 1 + Option 2 + diff --git a/src/examples/radio-overview/radio-overview-example.ts b/src/examples/radio-overview/radio-overview-example.ts new file mode 100644 index 000000000000..a845835e41f4 --- /dev/null +++ b/src/examples/radio-overview/radio-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'radio-overview-example', + templateUrl: './radio-overview-example.html', +}) +export class RadioOverviewExample {} diff --git a/src/examples/select-form/select-form-example.css b/src/examples/select-form/select-form-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/select-form/select-form-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/select-form/select-form-example.html b/src/examples/select-form/select-form-example.html new file mode 100644 index 000000000000..6dea33cc5963 --- /dev/null +++ b/src/examples/select-form/select-form-example.html @@ -0,0 +1,9 @@ +
+ + + {{food.viewValue}} + + + +

Selected value: {{selectedValue}}

+
diff --git a/src/examples/select-form/select-form-example.ts b/src/examples/select-form/select-form-example.ts new file mode 100644 index 000000000000..684bf016e5a9 --- /dev/null +++ b/src/examples/select-form/select-form-example.ts @@ -0,0 +1,16 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'select-form-example', + templateUrl: './select-form-example.html', +}) +export class SelectFormExample { + selectedValue: string; + + foods = [ + {value: 'steak-0', viewValue: 'Steak'}, + {value: 'pizza-1', viewValue: 'Pizza'}, + {value: 'tacos-2', viewValue: 'Tacos'} + ]; +} diff --git a/src/examples/select-overview/select-overview-example.css b/src/examples/select-overview/select-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/select-overview/select-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/select-overview/select-overview-example.html b/src/examples/select-overview/select-overview-example.html new file mode 100644 index 000000000000..a4017ca5c242 --- /dev/null +++ b/src/examples/select-overview/select-overview-example.html @@ -0,0 +1,5 @@ + + + {{ food.viewValue }} + + diff --git a/src/examples/select-overview/select-overview-example.ts b/src/examples/select-overview/select-overview-example.ts new file mode 100644 index 000000000000..8162dc7f616d --- /dev/null +++ b/src/examples/select-overview/select-overview-example.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'select-overview-example', + templateUrl: './select-overview-example.html', +}) +export class SelectOverviewExample { + foods = [ + {value: 'steak-0', viewValue: 'Steak'}, + {value: 'pizza-1', viewValue: 'Pizza'}, + {value: 'tacos-2', viewValue: 'Tacos'} + ]; +} diff --git a/src/examples/sidenav-fab/sidenav-fab-example.css b/src/examples/sidenav-fab/sidenav-fab-example.css new file mode 100644 index 000000000000..98d497acfe3d --- /dev/null +++ b/src/examples/sidenav-fab/sidenav-fab-example.css @@ -0,0 +1,25 @@ +.example-sidenav-fab-container { + width: 500px; + height: 300px; + border: 1px solid rgba(0, 0, 0, 0.5); +} + +.example-sidenav-fab-container md-sidenav { + max-width: 200px; +} + +.example-sidenav-fab-container .md-sidenav-content, +.example-sidenav-fab-container md-sidenav { + display: flex; + overflow: visible; +} + +.example-scrolling-content { + overflow: auto; +} + +.example-fab { + position: absolute; + right: 20px; + bottom: 10px; +} diff --git a/src/examples/sidenav-fab/sidenav-fab-example.html b/src/examples/sidenav-fab/sidenav-fab-example.html new file mode 100644 index 000000000000..f194c09852e2 --- /dev/null +++ b/src/examples/sidenav-fab/sidenav-fab-example.html @@ -0,0 +1,32 @@ + + + +
+ Lorem ipsum dolor sit amet, pede a libero aenean phasellus, lectus metus sint ut risus, + fusce vel in pellentesque. Nisl rutrum etiam morbi consectetuer tempor magna, aenean nullam + nunc id, neque vivamus interdum sociis nulla scelerisque sem, dolor id wisi turpis magna + aliquam magna. Risus accumsan hac eget etiam donec sed, senectus erat mattis quam, tempor + vel urna occaecat cras, metus urna augue nec at. Et morbi amet dui praesent, nec eu at, + ligula ipsum dui sollicitudin, quis nisl massa viverra ligula, mauris fermentum orci arcu + enim fringilla. Arcu erat nulla in aenean lacinia ullamcorper, urna ante nam et sagittis, + tristique vehicula nibh ipsum vivamus, proin proin. Porta commodo nibh quis libero amet. + Taciti dui, sapien consectetuer. +
+
+ +
+ Lorem ipsum dolor sit amet, pede a libero aenean phasellus, lectus metus sint ut risus, fusce + vel in pellentesque. Nisl rutrum etiam morbi consectetuer tempor magna, aenean nullam nunc id, + neque vivamus interdum sociis nulla scelerisque sem, dolor id wisi turpis magna aliquam magna. + Risus accumsan hac eget etiam donec sed, senectus erat mattis quam, tempor vel urna occaecat + cras, metus urna augue nec at. Et morbi amet dui praesent, nec eu at, ligula ipsum dui + sollicitudin, quis nisl massa viverra ligula, mauris fermentum orci arcu enim fringilla. Arcu + erat nulla in aenean lacinia ullamcorper, urna ante nam et sagittis, tristique vehicula nibh + ipsum vivamus, proin proin. Porta commodo nibh quis libero amet. Taciti dui, sapien + consectetuer. +
+
diff --git a/src/examples/sidenav-fab/sidenav-fab-example.ts b/src/examples/sidenav-fab/sidenav-fab-example.ts new file mode 100644 index 000000000000..a86050b2957d --- /dev/null +++ b/src/examples/sidenav-fab/sidenav-fab-example.ts @@ -0,0 +1,10 @@ +import {Component, ViewEncapsulation} from '@angular/core'; + + +@Component({ + selector: 'sidenav-fab-example', + templateUrl: './sidenav-fab-example.html', + styleUrls: ['./sidenav-fab-example.css'], + encapsulation: ViewEncapsulation.None, +}) +export class SidenavFabExample {} diff --git a/src/examples/sidenav-overview/sidenav-overview-example.css b/src/examples/sidenav-overview/sidenav-overview-example.css new file mode 100644 index 000000000000..8ab00588bb34 --- /dev/null +++ b/src/examples/sidenav-overview/sidenav-overview-example.css @@ -0,0 +1,16 @@ +.example-container { + width: 500px; + height: 300px; + border: 1px solid rgba(0, 0, 0, 0.5); +} + +.example-sidenav-content { + display: flex; + height: 100%; + align-items: center; + justify-content: center; +} + +.example-sidenav { + padding: 20px; +} diff --git a/src/examples/sidenav-overview/sidenav-overview-example.html b/src/examples/sidenav-overview/sidenav-overview-example.html new file mode 100644 index 000000000000..83f34793b0a4 --- /dev/null +++ b/src/examples/sidenav-overview/sidenav-overview-example.html @@ -0,0 +1,12 @@ + + + Jolly good! + + +
+ +
+ +
diff --git a/src/examples/sidenav-overview/sidenav-overview-example.ts b/src/examples/sidenav-overview/sidenav-overview-example.ts new file mode 100644 index 000000000000..533802a9099f --- /dev/null +++ b/src/examples/sidenav-overview/sidenav-overview-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'sidenav-overview-example', + templateUrl: './sidenav-overview-example.html', + styleUrls: ['./sidenav-overview-example.css'], +}) +export class SidenavOverviewExample {} diff --git a/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.css b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.css new file mode 100644 index 000000000000..060532ee7d9f --- /dev/null +++ b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.css @@ -0,0 +1,14 @@ +.example-h2 { + margin: 10px; +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; +} + +.example-margin { + margin: 10px; +} diff --git a/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.html b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.html new file mode 100644 index 000000000000..88099f4b0254 --- /dev/null +++ b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.html @@ -0,0 +1,44 @@ + + +

Slider configuration

+ +
+ + + + Primary + + + Accent + + + Warn + + +
+ +
+ Checked +
+ +
+ Disabled +
+
+
+ + + +

Result

+ +
+ + Slide me! + +
+
+
diff --git a/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.ts b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.ts new file mode 100644 index 000000000000..cfa28ceddd4d --- /dev/null +++ b/src/examples/slide-toggle-configurable/slide-toggle-configurable-example.ts @@ -0,0 +1,13 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'slide-toggle-configurable-example', + templateUrl: './slide-toggle-configurable-example.html', + styleUrls: ['./slide-toggle-configurable-example.css'], +}) +export class SlideToggleConfigurableExample { + color = 'accent'; + checked = false; + disabled = false; +} diff --git a/src/examples/slide-toggle-overview/slide-toggle-overview-example.css b/src/examples/slide-toggle-overview/slide-toggle-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/slide-toggle-overview/slide-toggle-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/slide-toggle-overview/slide-toggle-overview-example.html b/src/examples/slide-toggle-overview/slide-toggle-overview-example.html new file mode 100644 index 000000000000..6b9586e148fc --- /dev/null +++ b/src/examples/slide-toggle-overview/slide-toggle-overview-example.html @@ -0,0 +1 @@ +Slide me! diff --git a/src/examples/slide-toggle-overview/slide-toggle-overview-example.ts b/src/examples/slide-toggle-overview/slide-toggle-overview-example.ts new file mode 100644 index 000000000000..0f35c9d5e9b1 --- /dev/null +++ b/src/examples/slide-toggle-overview/slide-toggle-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'slide-toggle-overview-example', + templateUrl: './slide-toggle-overview-example.html', +}) +export class SlideToggleOverviewExample {} diff --git a/src/examples/slider-configurable/slider-configurable-example.css b/src/examples/slider-configurable/slider-configurable-example.css new file mode 100644 index 000000000000..389e65bd7ac0 --- /dev/null +++ b/src/examples/slider-configurable/slider-configurable-example.css @@ -0,0 +1,22 @@ +.example-h2 { + margin: 10px; +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; +} + +.example-margin { + margin: 10px; +} + +.md-slider-horizontal { + width: 300px; +} + +.md-slider-vertical { + height: 300px; +} diff --git a/src/examples/slider-configurable/slider-configurable-example.html b/src/examples/slider-configurable/slider-configurable-example.html new file mode 100644 index 000000000000..ba886e760363 --- /dev/null +++ b/src/examples/slider-configurable/slider-configurable-example.html @@ -0,0 +1,63 @@ + + +

Slider configuration

+ +
+ + + + + + + + + + + + +
+ +
+ Show ticks + + Auto ticks + + + + +
+ +
+ Show thumb label +
+ +
+ Vertical + Inverted +
+ +
+ Disabled +
+ +
+
+ + + +

Result

+ + + +
+
diff --git a/src/examples/slider-configurable/slider-configurable-example.ts b/src/examples/slider-configurable/slider-configurable-example.ts new file mode 100644 index 000000000000..933a9dee7311 --- /dev/null +++ b/src/examples/slider-configurable/slider-configurable-example.ts @@ -0,0 +1,29 @@ +import {Component, ViewEncapsulation} from '@angular/core'; + + +@Component({ + selector: 'slider-configurable-example', + templateUrl: './slider-configurable-example.html', + styleUrls: ['./slider-configurable-example.css'], + encapsulation: ViewEncapsulation.None, +}) +export class SliderConfigurableExample { + autoTicks = false; + disabled = false; + invert = false; + max = 100; + min = 0; + showTicks = false; + step = 1; + thumbLabel = false; + value = 0; + vertical = false; + + get tickInterval(): number | 'auto' { + return this.showTicks ? (this.autoTicks ? 'auto' : this._tickInterval) : null; + } + set tickInterval(v) { + this._tickInterval = Number(v); + } + private _tickInterval = 1; +} diff --git a/src/examples/slider-overview/slider-overview-example.css b/src/examples/slider-overview/slider-overview-example.css new file mode 100644 index 000000000000..8dfe08ccc21d --- /dev/null +++ b/src/examples/slider-overview/slider-overview-example.css @@ -0,0 +1,4 @@ +/** No CSS for this example */ +md-slider { + width: 300px; +} diff --git a/src/examples/slider-overview/slider-overview-example.html b/src/examples/slider-overview/slider-overview-example.html new file mode 100644 index 000000000000..9a92c8f867dd --- /dev/null +++ b/src/examples/slider-overview/slider-overview-example.html @@ -0,0 +1 @@ + diff --git a/src/examples/slider-overview/slider-overview-example.ts b/src/examples/slider-overview/slider-overview-example.ts new file mode 100644 index 000000000000..1f020fd662ff --- /dev/null +++ b/src/examples/slider-overview/slider-overview-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'slider-overview-example', + templateUrl: './slider-overview-example.html', + styleUrls: ['./slider-overview-example.css'], +}) +export class SliderOverviewExample {} diff --git a/src/examples/snack-bar-component/snack-bar-component-example-snack.css b/src/examples/snack-bar-component/snack-bar-component-example-snack.css new file mode 100644 index 000000000000..fa7dc6749acf --- /dev/null +++ b/src/examples/snack-bar-component/snack-bar-component-example-snack.css @@ -0,0 +1,3 @@ +.example-pizza-party { + color: hotpink; +} diff --git a/src/examples/snack-bar-component/snack-bar-component-example-snack.html b/src/examples/snack-bar-component/snack-bar-component-example-snack.html new file mode 100644 index 000000000000..d43b91e9a961 --- /dev/null +++ b/src/examples/snack-bar-component/snack-bar-component-example-snack.html @@ -0,0 +1,3 @@ + + 🍕🍕🍕🍕🍕 Pizza party!!! 🍕🍕🍕🍕🍕 + diff --git a/src/examples/snack-bar-component/snack-bar-component-example.html b/src/examples/snack-bar-component/snack-bar-component-example.html new file mode 100644 index 000000000000..69fe01bc3dcb --- /dev/null +++ b/src/examples/snack-bar-component/snack-bar-component-example.html @@ -0,0 +1,3 @@ + diff --git a/src/examples/snack-bar-component/snack-bar-component-example.ts b/src/examples/snack-bar-component/snack-bar-component-example.ts new file mode 100644 index 000000000000..03ef19e8a70d --- /dev/null +++ b/src/examples/snack-bar-component/snack-bar-component-example.ts @@ -0,0 +1,25 @@ +import {Component} from '@angular/core'; +import {MdSnackBar} from '@angular/material'; + + +@Component({ + selector: 'snack-bar-component-example', + templateUrl: './snack-bar-component-example.html', +}) +export class SnackBarComponentExample { + constructor(public snackBar: MdSnackBar) {} + + openSnackBar() { + this.snackBar.openFromComponent(PizzaPartyComponent, { + duration: 500, + }); + } +} + + +@Component({ + selector: 'snack-bar-component-example-snack', + templateUrl: './snack-bar-component-example-snack.html', + styleUrls: ['./snack-bar-component-example-snack.css'], +}) +export class PizzaPartyComponent {} diff --git a/src/examples/snack-bar-overview/snack-bar-overview-example.css b/src/examples/snack-bar-overview/snack-bar-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/snack-bar-overview/snack-bar-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/snack-bar-overview/snack-bar-overview-example.html b/src/examples/snack-bar-overview/snack-bar-overview-example.html new file mode 100644 index 000000000000..91cb38c8b317 --- /dev/null +++ b/src/examples/snack-bar-overview/snack-bar-overview-example.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/examples/snack-bar-overview/snack-bar-overview-example.ts b/src/examples/snack-bar-overview/snack-bar-overview-example.ts new file mode 100644 index 000000000000..312de83ea671 --- /dev/null +++ b/src/examples/snack-bar-overview/snack-bar-overview-example.ts @@ -0,0 +1,17 @@ +import {Component} from '@angular/core'; +import {MdSnackBar} from '@angular/material'; + + +@Component({ + selector: 'snack-bar-overview-example', + templateUrl: './snack-bar-overview-example.html', +}) +export class SnackBarOverviewExample { + constructor(public snackBar: MdSnackBar) {} + + openSnackBar(message: string, action: string) { + this.snackBar.open(message, action, { + duration: 2000, + }); + } +} diff --git a/src/examples/tabs-overview/tabs-overview-example.css b/src/examples/tabs-overview/tabs-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/tabs-overview/tabs-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/tabs-overview/tabs-overview-example.html b/src/examples/tabs-overview/tabs-overview-example.html new file mode 100644 index 000000000000..173ece5dc477 --- /dev/null +++ b/src/examples/tabs-overview/tabs-overview-example.html @@ -0,0 +1,4 @@ + + Content 1 + Content 2 + diff --git a/src/examples/tabs-overview/tabs-overview-example.ts b/src/examples/tabs-overview/tabs-overview-example.ts new file mode 100644 index 000000000000..4f222c112621 --- /dev/null +++ b/src/examples/tabs-overview/tabs-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'tabs-overview-example', + templateUrl: './tabs-overview-example.html', +}) +export class TabsOverviewExample {} diff --git a/src/examples/tabs-template-label/tabs-template-label-example.css b/src/examples/tabs-template-label/tabs-template-label-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/tabs-template-label/tabs-template-label-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/tabs-template-label/tabs-template-label-example.html b/src/examples/tabs-template-label/tabs-template-label-example.html new file mode 100644 index 000000000000..bde67b8e9138 --- /dev/null +++ b/src/examples/tabs-template-label/tabs-template-label-example.html @@ -0,0 +1 @@ +More examples coming soon! diff --git a/src/examples/tabs-template-label/tabs-template-label-example.ts b/src/examples/tabs-template-label/tabs-template-label-example.ts new file mode 100644 index 000000000000..7015b1f74182 --- /dev/null +++ b/src/examples/tabs-template-label/tabs-template-label-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'tabs-template-label-example', + templateUrl: './tabs-template-label-example.html', +}) +export class TabsTemplateLabelExample {} diff --git a/src/examples/toolbar-multirow/toolbar-multirow-example.css b/src/examples/toolbar-multirow/toolbar-multirow-example.css new file mode 100644 index 000000000000..d7c40156ba97 --- /dev/null +++ b/src/examples/toolbar-multirow/toolbar-multirow-example.css @@ -0,0 +1,7 @@ +.example-icon { + padding: 0 14px; +} + +.example-spacer { + flex: 1 1 auto; +} diff --git a/src/examples/toolbar-multirow/toolbar-multirow-example.html b/src/examples/toolbar-multirow/toolbar-multirow-example.html new file mode 100644 index 000000000000..9b178a21249f --- /dev/null +++ b/src/examples/toolbar-multirow/toolbar-multirow-example.html @@ -0,0 +1,16 @@ + + Custom Toolbar + + + Second Line + + verified_user + + + + Third Line + + favorite + delete + + diff --git a/src/examples/toolbar-multirow/toolbar-multirow-example.ts b/src/examples/toolbar-multirow/toolbar-multirow-example.ts new file mode 100644 index 000000000000..132b98174f67 --- /dev/null +++ b/src/examples/toolbar-multirow/toolbar-multirow-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'toolbar-multirow-example', + templateUrl: './toolbar-multirow-example.html', + styleUrls: ['./toolbar-multirow-example.css'], +}) +export class ToolbarMultirowExample {} diff --git a/src/examples/toolbar-overview/toolbar-overview-example.css b/src/examples/toolbar-overview/toolbar-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/toolbar-overview/toolbar-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/toolbar-overview/toolbar-overview-example.html b/src/examples/toolbar-overview/toolbar-overview-example.html new file mode 100644 index 000000000000..bf8cc3998630 --- /dev/null +++ b/src/examples/toolbar-overview/toolbar-overview-example.html @@ -0,0 +1 @@ +My App diff --git a/src/examples/toolbar-overview/toolbar-overview-example.ts b/src/examples/toolbar-overview/toolbar-overview-example.ts new file mode 100644 index 000000000000..6919aca4f3fc --- /dev/null +++ b/src/examples/toolbar-overview/toolbar-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'toolbar-overview-example', + templateUrl: './toolbar-overview-example.html', +}) +export class ToolbarOverviewExample {} diff --git a/src/examples/tooltip-overview/tooltip-overview-example.css b/src/examples/tooltip-overview/tooltip-overview-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/tooltip-overview/tooltip-overview-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/tooltip-overview/tooltip-overview-example.html b/src/examples/tooltip-overview/tooltip-overview-example.html new file mode 100644 index 000000000000..8319a8545846 --- /dev/null +++ b/src/examples/tooltip-overview/tooltip-overview-example.html @@ -0,0 +1 @@ +I have a tooltip diff --git a/src/examples/tooltip-overview/tooltip-overview-example.ts b/src/examples/tooltip-overview/tooltip-overview-example.ts new file mode 100644 index 000000000000..cf9eefc94557 --- /dev/null +++ b/src/examples/tooltip-overview/tooltip-overview-example.ts @@ -0,0 +1,8 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'tooltip-overview-example', + templateUrl: './tooltip-overview-example.html', +}) +export class TooltipOverviewExample {} diff --git a/src/examples/tooltip-position/tooltip-position-example.css b/src/examples/tooltip-position/tooltip-position-example.css new file mode 100644 index 000000000000..04ec59dd1fc7 --- /dev/null +++ b/src/examples/tooltip-position/tooltip-position-example.css @@ -0,0 +1,9 @@ +.example-tooltip-host { + display: inline-flex; + align-items: center; + margin: 50px; +} + +.example-select { + margin: 0 10px; +} diff --git a/src/examples/tooltip-position/tooltip-position-example.html b/src/examples/tooltip-position/tooltip-position-example.html new file mode 100644 index 000000000000..156a5bf68492 --- /dev/null +++ b/src/examples/tooltip-position/tooltip-position-example.html @@ -0,0 +1,11 @@ +
+ Show tooltip + + Before + After + Above + Below + Left + Right + +
diff --git a/src/examples/tooltip-position/tooltip-position-example.ts b/src/examples/tooltip-position/tooltip-position-example.ts new file mode 100644 index 000000000000..0b580c722bfc --- /dev/null +++ b/src/examples/tooltip-position/tooltip-position-example.ts @@ -0,0 +1,11 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'tooltip-position-example', + templateUrl: './tooltip-position-example.html', + styleUrls: ['./tooltip-position-example.css'], +}) +export class TooltipPositionExample { + position = 'before'; +} diff --git a/src/lib/autocomplete/README.md b/src/lib/autocomplete/README.md index 1bc0814e9eec..73bddeb68ee6 100644 --- a/src/lib/autocomplete/README.md +++ b/src/lib/autocomplete/README.md @@ -1,5 +1 @@ - -## Not yet implemented! - -The autocomplete is not yet implemented. This is only a scaffold to make -subsequent PRs easier to read. Please do not try to use yet :) \ No newline at end of file +See documentation on [material.angular.io](https://material.angular.io/). \ No newline at end of file diff --git a/src/lib/autocomplete/_autocomplete-theme.scss b/src/lib/autocomplete/_autocomplete-theme.scss index 3e231a63148d..7cce6dfe1c56 100644 --- a/src/lib/autocomplete/_autocomplete-theme.scss +++ b/src/lib/autocomplete/_autocomplete-theme.scss @@ -1,17 +1,17 @@ @import '../core/theming/theming'; -@mixin md-autocomplete-theme($theme) { +@mixin mat-autocomplete-theme($theme) { $foreground: map-get($theme, foreground); $background: map-get($theme, background); - .md-autocomplete-panel { - background: md-color($background, card); - color: md-color($foreground, text); + .mat-autocomplete-panel { + background: mat-color($background, card); + color: mat-color($foreground, text); - md-option { - &.md-selected { - background: md-color($background, card); - color: md-color($foreground, text); + .mat-option { + &.mat-selected:not(.mat-active) { + background: mat-color($background, card); + color: mat-color($foreground, text); } } } diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 42d623b88da1..b6e841046605 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -1,40 +1,121 @@ import { - Directive, ElementRef, Input, ViewContainerRef, Optional, OnDestroy + AfterContentInit, + Directive, + ElementRef, + forwardRef, + Host, + Input, + NgZone, + Optional, + OnDestroy, + ViewContainerRef, } from '@angular/core'; -import {NgControl} from '@angular/forms'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core'; import {MdAutocomplete} from './autocomplete'; import {PositionStrategy} from '../core/overlay/position/position-strategy'; +import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy'; import {Observable} from 'rxjs/Observable'; -import {MdOptionSelectEvent} from '../core/option/option'; -import 'rxjs/add/observable/merge'; +import {MdOptionSelectEvent, MdOption} from '../core/option/option'; +import {ActiveDescendantKeyManager} from '../core/a11y/activedescendant-key-manager'; +import {ENTER, UP_ARROW, DOWN_ARROW} from '../core/keyboard/keycodes'; import {Dir} from '../core/rtl/dir'; +import {Subscription} from 'rxjs/Subscription'; +import {Subject} from 'rxjs/Subject'; +import 'rxjs/add/observable/merge'; import 'rxjs/add/operator/startWith'; import 'rxjs/add/operator/switchMap'; +import {MdInputContainer, FloatPlaceholderType} from '../input/input-container'; + +/** + * The following style constants are necessary to save here in order + * to properly calculate the scrollTop of the panel. Because we are not + * actually focusing the active item, scroll must be handled manually. + */ + +/** The height of each autocomplete option. */ +export const AUTOCOMPLETE_OPTION_HEIGHT = 48; +/** The total height of the autocomplete panel. */ +export const AUTOCOMPLETE_PANEL_HEIGHT = 256; -/** The panel needs a slight y-offset to ensure the input underline displays. */ -export const MD_AUTOCOMPLETE_PANEL_OFFSET = 6; +/** + * Provider that allows the autocomplete to register as a ControlValueAccessor. + * @docs-private + */ +export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MdAutocompleteTrigger), + multi: true +}; @Directive({ selector: 'input[mdAutocomplete], input[matAutocomplete]', host: { - '(focus)': 'openPanel()' - } + 'role': 'combobox', + 'autocomplete': 'off', + 'aria-autocomplete': 'list', + 'aria-multiline': 'false', + '[attr.aria-activedescendant]': 'activeOption?.id', + '[attr.aria-expanded]': 'panelOpen.toString()', + '[attr.aria-owns]': 'autocomplete?.id', + '(focus)': 'openPanel()', + '(blur)': '_handleBlur($event.relatedTarget?.tagName)', + '(input)': '_handleInput($event.target.value)', + '(keydown)': '_handleKeydown($event)', + }, + providers: [MD_AUTOCOMPLETE_VALUE_ACCESSOR] }) -export class MdAutocompleteTrigger implements OnDestroy { +export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAccessor, OnDestroy { private _overlayRef: OverlayRef; private _portal: TemplatePortal; private _panelOpen: boolean = false; + /** The subscription to positioning changes in the autocomplete panel. */ + private _panelPositionSubscription: Subscription; + + /** Manages active item in option list based on key events. */ + private _keyManager: ActiveDescendantKeyManager; + private _positionStrategy: ConnectedPositionStrategy; + + /** Stream of blur events that should close the panel. */ + private _blurStream = new Subject(); + + /** View -> model callback called when value changes */ + _onChange = (value: any) => {}; + + /** View -> model callback called when autocomplete has been touched */ + _onTouched = () => {}; + /* The autocomplete panel to be attached to this trigger. */ @Input('mdAutocomplete') autocomplete: MdAutocomplete; + /** Property with mat- prefix for no-conflict mode. */ + @Input('matAutocomplete') + get _matAutocomplete(): MdAutocomplete { + return this.autocomplete; + } + + set _matAutocomplete(autocomplete: MdAutocomplete) { + this.autocomplete = autocomplete; + } + constructor(private _element: ElementRef, private _overlay: Overlay, private _viewContainerRef: ViewContainerRef, - @Optional() private _controlDir: NgControl, @Optional() private _dir: Dir) {} + @Optional() private _dir: Dir, private _zone: NgZone, + @Optional() @Host() private _inputContainer: MdInputContainer) {} + + ngAfterContentInit() { + this._keyManager = new ActiveDescendantKeyManager(this.autocomplete.options).withWrap(); + } - ngOnDestroy() { this._destroyPanel(); } + ngOnDestroy() { + if (this._panelPositionSubscription) { + this._panelPositionSubscription.unsubscribe(); + } + + this._destroyPanel(); + } /* Whether or not the autocomplete panel is open. */ get panelOpen(): boolean { @@ -53,6 +134,7 @@ export class MdAutocompleteTrigger implements OnDestroy { } this._panelOpen = true; + this._floatPlaceholder('always'); } /** Closes the autocomplete suggestion panel. */ @@ -62,35 +144,126 @@ export class MdAutocompleteTrigger implements OnDestroy { } this._panelOpen = false; + this._floatPlaceholder('auto'); } /** * A stream of actions that should close the autocomplete panel, including - * when an option is selected and when the backdrop is clicked. + * when an option is selected, on blur, and when TAB is pressed. */ - get panelClosingActions(): Observable { - // TODO(kara): add tab event observable with keyboard event PR - return Observable.merge(...this.optionSelections, this._overlayRef.backdropClick()); + get panelClosingActions(): Observable { + return Observable.merge( + ...this.optionSelections, + this._blurStream.asObservable(), + this._keyManager.tabOut + ); } /** Stream of autocomplete option selections. */ - get optionSelections(): Observable[] { + get optionSelections(): Observable[] { return this.autocomplete.options.map(option => option.onSelect); } + /** The currently active option, coerced to MdOption type. */ + get activeOption(): MdOption { + return this._keyManager.activeItem as MdOption; + } + + /** + * Sets the autocomplete's value. Part of the ControlValueAccessor interface + * required to integrate with Angular's core forms API. + * + * @param value New value to be written to the model. + */ + writeValue(value: any): void { + Promise.resolve(null).then(() => this._setTriggerValue(value)); + } + + /** + * Saves a callback function to be invoked when the autocomplete's value + * changes from user input. Part of the ControlValueAccessor interface + * required to integrate with Angular's core forms API. + * + * @param fn Callback to be triggered when the value changes. + */ + registerOnChange(fn: (value: any) => {}): void { + this._onChange = fn; + } + + /** + * Saves a callback function to be invoked when the autocomplete is blurred + * by the user. Part of the ControlValueAccessor interface required + * to integrate with Angular's core forms API. + * + * @param fn Callback to be triggered when the component has been touched. + */ + registerOnTouched(fn: () => {}) { + this._onTouched = fn; + } + + _handleKeydown(event: KeyboardEvent): void { + if (this.activeOption && event.keyCode === ENTER) { + this.activeOption._selectViaInteraction(); + } else { + this._keyManager.onKeydown(event); + if (event.keyCode === UP_ARROW || event.keyCode === DOWN_ARROW) { + this.openPanel(); + this._scrollToOption(); + } + } + } + + _handleInput(value: string): void { + this._onChange(value); + this.openPanel(); + } + + _handleBlur(newlyFocusedTag: string): void { + this._onTouched(); + + // Only emit blur event if the new focus is *not* on an option. + if (newlyFocusedTag !== 'MD-OPTION') { + this._blurStream.next(null); + } + } + + /** + * In "auto" mode, the placeholder will animate down as soon as focus is lost. + * This causes the value to jump when selecting an option with the mouse. + * This method manually floats the placeholder until the panel can be closed. + */ + private _floatPlaceholder(state: FloatPlaceholderType): void { + if (this._inputContainer) { + this._inputContainer.floatPlaceholder = state; + } + } + + /** + * Given that we are not actually focusing active options, we must manually adjust scroll + * to reveal options below the fold. First, we find the offset of the option from the top + * of the panel. The new scrollTop will be that offset - the panel height + the option + * height, so the active option will be just visible at the bottom of the panel. + */ + private _scrollToOption(): void { + const optionOffset = this._keyManager.activeItemIndex * AUTOCOMPLETE_OPTION_HEIGHT; + const newScrollTop = + Math.max(0, optionOffset - AUTOCOMPLETE_PANEL_HEIGHT + AUTOCOMPLETE_OPTION_HEIGHT); + this.autocomplete._setScrollTop(newScrollTop); + } /** * This method listens to a stream of panel closing actions and resets the * stream every time the option list changes. */ private _subscribeToClosingActions(): void { - // Every time the option list changes... - this.autocomplete.options.changes - // and also at initialization, before there are any option changes... - .startWith(null) + // When the zone is stable initially, and when the option list changes... + Observable.merge(this._zone.onStable.first(), this.autocomplete.options.changes) // create a new stream of panelClosingActions, replacing any previous streams // that were created, and flatten it so our stream only emits closing events... - .switchMap(() => this.panelClosingActions) + .switchMap(() => { + this._resetPanel(); + return this.panelClosingActions; + }) // when the first closing event occurs... .first() // set the value, close the panel, and complete. @@ -106,6 +279,11 @@ export class MdAutocompleteTrigger implements OnDestroy { } } + private _setTriggerValue(value: any): void { + this._element.nativeElement.value = + this.autocomplete.displayWith ? this.autocomplete.displayWith(value) : value; + } + /** * This method closes the panel, and if a value is specified, also sets the associated * control to that value. It will also mark the control as dirty if this interaction @@ -113,10 +291,8 @@ export class MdAutocompleteTrigger implements OnDestroy { */ private _setValueAndClose(event: MdOptionSelectEvent | null): void { if (event) { - this._controlDir.control.setValue(event.source.value); - if (event.isUserInput) { - this._controlDir.control.markAsDirty(); - } + this._setTriggerValue(event.source.value); + this._onChange(event.source.value); } this.closePanel(); @@ -131,17 +307,29 @@ export class MdAutocompleteTrigger implements OnDestroy { const overlayState = new OverlayState(); overlayState.positionStrategy = this._getOverlayPosition(); overlayState.width = this._getHostWidth(); - overlayState.hasBackdrop = true; - overlayState.backdropClass = 'md-overlay-transparent-backdrop'; overlayState.direction = this._dir ? this._dir.value : 'ltr'; return overlayState; } private _getOverlayPosition(): PositionStrategy { - return this._overlay.position().connectedTo( + this._positionStrategy = this._overlay.position().connectedTo( this._element, {originX: 'start', originY: 'bottom'}, {overlayX: 'start', overlayY: 'top'}) - .withOffsetY(MD_AUTOCOMPLETE_PANEL_OFFSET); + .withFallbackPosition( + {originX: 'start', originY: 'top'}, {overlayX: 'start', overlayY: 'bottom'} + ); + this._subscribeToPositionChanges(this._positionStrategy); + return this._positionStrategy; + } + + /** + * This method subscribes to position changes in the autocomplete panel, so the panel's + * y-offset can be adjusted to match the new position. + */ + private _subscribeToPositionChanges(strategy: ConnectedPositionStrategy) { + this._panelPositionSubscription = strategy.onPositionChange.subscribe(change => { + this.autocomplete.positionY = change.connectionPair.originY === 'top' ? 'above' : 'below'; + }); } /** Returns the width of the input element, so the panel width can match it. */ @@ -149,5 +337,20 @@ export class MdAutocompleteTrigger implements OnDestroy { return this._element.nativeElement.getBoundingClientRect().width; } + /** Reset active item to null so arrow events will activate the correct options.*/ + private _resetActiveItem(): void { + this._keyManager.setActiveItem(null); + } + + /** + * Resets the active item and re-calculates alignment of the panel in case its size + * has changed due to fewer or greater number of options. + */ + private _resetPanel() { + this._resetActiveItem(); + this._positionStrategy.recalculateLastPosition(); + this.autocomplete._setVisibility(); + } + } diff --git a/src/lib/autocomplete/autocomplete.html b/src/lib/autocomplete/autocomplete.html index 97727158af0c..cd94ceeb7340 100644 --- a/src/lib/autocomplete/autocomplete.html +++ b/src/lib/autocomplete/autocomplete.html @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/src/lib/autocomplete/autocomplete.md b/src/lib/autocomplete/autocomplete.md new file mode 100644 index 000000000000..19f4acbededa --- /dev/null +++ b/src/lib/autocomplete/autocomplete.md @@ -0,0 +1,150 @@ + +The autocomplete is a normal text input enhanced by a panel of suggested options. You can read more about +autocompletes in the [Material Design spec](https://material.io/guidelines/components/text-fields.html#text-fields-auto-complete-text-field). + +### Simple autocomplete + +Start by adding a regular `mdInput` to the page. Let's assume you're using the `formControl` directive from the +`@angular/forms` module to track the value of the input. + +*my-comp.html* +```html + + + +``` + +Next, create the autocomplete panel and the options displayed inside it. Each option should be defined by an +`md-option` tag. Set each option's value property to whatever you'd like the value of the text input to be +upon that option's selection. + +*my-comp.html* +```html + + + {{ option }} + + +``` + +Now we'll need to link the text input to its panel. We can do this by exporting the autocomplete panel instance into a +local template variable (here we called it "auto"), and binding that variable to the input's `mdAutocomplete` property. + +*my-comp.html* +```html + + + + + + + {{ option }} + + +``` + +### Adding a custom filter + +At this point, the autocomplete panel should be toggleable on focus and options should be selectable. But if we want +our options to filter when we type, we need to add a custom filter. + +You can filter the options in any way you want based on the text input. Here we will do a simple string test on the +input value to see if it matches the option value. We already have access to the built-in `valueChanges` observable on +the `FormControl`, so we can simply map the text input's values to the suggested options by passing them through this +filter. The resulting observable (`filteredOptions`) can be added to the template in place of the `options` property +using the `async` pipe. + +Below we are also priming our value change stream with `null` so that the options are filtered by that value on init +(before there are any value changes). + +*my-comp.ts* +```ts +class MyComp { + myControl = new FormControl(); + options = [ + 'One', + 'Two', + 'Three' + ]; + filteredOptions: Observable; + + ngOnInit() { + this.filteredOptions = this.myControl.valueChanges + .startWith(null) + .map(val => val ? this.filter(val) : this.options.slice()); + } + + filter(val: string): string[] { + return this.options.filter(option => new RegExp(val, 'gi').test(option)); + } +} +``` + +*my-comp.html* +```html + + + + + + + {{ option }} + + +``` + +### Setting separate control and display values + +If you want the option's control value (what is saved in the form) to be different than the option's display value +(what is displayed in the actual text field), you'll need to set the `displayWith` property on your autocomplete +element. A common use case for this might be if you want to save your data as an object, but display just one of +the option's string properties. + +To make this work, create a function on your component class that maps the control value to the desired display value. +Then bind it to the autocomplete's `displayWith` property. + +```html + + + + + + + {{ option }} + + +``` + +*my-comp.ts* +```ts +class MyComp { + myControl = new FormControl(); + options = [ + new User('Mary'), + new User('Shelley'), + new User('Igor') + ]; + filteredOptions: Observable; + + ngOnInit() { + this.filteredOptions = this.myControl.valueChanges + .startWith(null) + .map(user => user && typeof user === 'object' ? user.name : user) + .map(name => name ? this.filter(name) : this.options.slice()); + } + + filter(name: string): User[] { + return this.options.filter(option => new RegExp(name, 'gi').test(option)); + } + + displayFn(user: User): string { + return user ? user.name : user; + } +} +``` + + +#### Keyboard interaction: +- DOWN_ARROW: Next option becomes active. +- UP_ARROW: Previous option becomes active. +- ENTER: Select currently active item. diff --git a/src/lib/autocomplete/autocomplete.scss b/src/lib/autocomplete/autocomplete.scss index d6c9b0162d35..80fbcc56d6d2 100644 --- a/src/lib/autocomplete/autocomplete.scss +++ b/src/lib/autocomplete/autocomplete.scss @@ -1,5 +1,37 @@ @import '../core/style/menu-common'; -.md-autocomplete-panel { - @include md-menu-base(); +/** + * The max-height of the panel, currently matching md-select value. + * TODO: Check value with MD team. + */ +$mat-autocomplete-panel-max-height: 256px !default; + +/** When in "below" position, the panel needs a slight y-offset to ensure the input underline displays. */ +$mat-autocomplete-panel-below-offset: 6px !default; + +/** When in "above" position, the panel needs a larger y-offset to ensure the label has room to display. */ +$mat-autocomplete-panel-above-offset: -24px !default; + +.mat-autocomplete-panel { + @include mat-menu-base(); + visibility: hidden; + + max-height: $mat-autocomplete-panel-max-height; + position: relative; + + &.mat-autocomplete-panel-below { + top: $mat-autocomplete-panel-below-offset; + } + + &.mat-autocomplete-panel-above { + top: $mat-autocomplete-panel-above-offset; + } + + &.mat-autocomplete-visible { + visibility: visible; + } + + &.mat-autocomplete-hidden { + visibility: hidden; + } } \ No newline at end of file diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index bc02469a4d43..e533ae34704c 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1,5 +1,5 @@ import {TestBed, async, ComponentFixture} from '@angular/core/testing'; -import {Component, OnDestroy, ViewChild} from '@angular/core'; +import {Component, OnDestroy, QueryList, ViewChild, ViewChildren} from '@angular/core'; import {By} from '@angular/platform-browser'; import {MdAutocompleteModule, MdAutocompleteTrigger} from './index'; import {OverlayContainer} from '../core/overlay/overlay-container'; @@ -7,6 +7,12 @@ import {MdInputModule} from '../input/index'; import {Dir, LayoutDirection} from '../core/rtl/dir'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; import {Subscription} from 'rxjs/Subscription'; +import {ENTER, DOWN_ARROW, SPACE, UP_ARROW} from '../core/keyboard/keycodes'; +import {MdOption} from '../core/option/option'; +import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; +import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; +import {MdAutocomplete} from './autocomplete'; +import {MdInputContainer} from '../input/input-container'; describe('MdAutocomplete', () => { let overlayContainerElement: HTMLElement; @@ -18,7 +24,7 @@ describe('MdAutocomplete', () => { imports: [ MdAutocompleteModule.forRoot(), MdInputModule.forRoot(), ReactiveFormsModule ], - declarations: [SimpleAutocomplete], + declarations: [SimpleAutocomplete, AutocompleteWithoutForms], providers: [ {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); @@ -33,6 +39,7 @@ describe('MdAutocomplete', () => { {provide: Dir, useFactory: () => { return {value: dir}; }}, + {provide: ViewportRuler, useClass: FakeViewportRuler} ] }); @@ -51,7 +58,9 @@ describe('MdAutocomplete', () => { }); it('should open the panel when the input is focused', () => { - expect(fixture.componentInstance.trigger.panelOpen).toBe(false); + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(false, `Expected panel state to start out closed.`); + dispatchEvent('focus', input); fixture.detectChanges(); @@ -64,7 +73,9 @@ describe('MdAutocomplete', () => { }); it('should open the panel programmatically', () => { - expect(fixture.componentInstance.trigger.panelOpen).toBe(false); + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(false, `Expected panel state to start out closed.`); + fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -76,16 +87,14 @@ describe('MdAutocomplete', () => { .toContain('California', `Expected panel to display when opened programmatically.`); }); - it('should close the panel when a click occurs outside it', async(() => { + it('should close the panel when blurred', async(() => { dispatchEvent('focus', input); fixture.detectChanges(); - const backdrop = - overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - backdrop.click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { + dispatchEvent('blur', input); + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.panelOpen) .toBe(false, `Expected clicking outside the panel to set its state to closed.`); expect(overlayContainerElement.textContent) @@ -97,11 +106,11 @@ describe('MdAutocomplete', () => { dispatchEvent('focus', input); fixture.detectChanges(); - const option = overlayContainerElement.querySelector('md-option') as HTMLElement; - option.click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { + const option = overlayContainerElement.querySelector('md-option') as HTMLElement; + option.click(); + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.panelOpen) .toBe(false, `Expected clicking an option to set the panel state to closed.`); expect(overlayContainerElement.textContent) @@ -109,62 +118,95 @@ describe('MdAutocomplete', () => { }); })); - it('should close the panel when a newly filtered option is clicked', async(() => { + it('should close the panel when a newly created option is clicked', async(() => { dispatchEvent('focus', input); fixture.detectChanges(); - // Filter down the option list to a subset of original options ('Alabama', 'California') - input.value = 'al'; - dispatchEvent('input', input); - fixture.detectChanges(); - - let options = - overlayContainerElement.querySelectorAll('md-option') as NodeListOf; - options[0].click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(fixture.componentInstance.trigger.panelOpen) - .toBe(false, `Expected clicking a filtered option to set the panel state to closed.`); - expect(overlayContainerElement.textContent) - .toEqual('', `Expected clicking a filtered option to close the panel.`); - - dispatchEvent('focus', input); + // Filter down the option list to a subset of original options ('Alabama', 'California') + input.value = 'al'; + dispatchEvent('input', input); fixture.detectChanges(); + let options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[0].click(); + // Changing value from 'Alabama' to 'al' to re-populate the option list, // ensuring that 'California' is created new. input.value = 'al'; dispatchEvent('input', input); fixture.detectChanges(); - options = - overlayContainerElement.querySelectorAll('md-option') as NodeListOf; - options[1].click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { + options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.panelOpen) .toBe(false, `Expected clicking a new option to set the panel state to closed.`); expect(overlayContainerElement.textContent) .toEqual('', `Expected clicking a new option to close the panel.`); }); - }); })); - it('should close the panel programmatically', async(() => { + it('should close the panel programmatically', () => { fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); fixture.componentInstance.trigger.closePanel(); fixture.detectChanges(); + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(false, `Expected closing programmatically to set the panel state to closed.`); + expect(overlayContainerElement.textContent) + .toEqual('', `Expected closing programmatically to close the panel.`); + }); + + it('should hide the panel when the options list is empty', async(() => { + dispatchEvent('focus', input); + fixture.whenStable().then(() => { - expect(fixture.componentInstance.trigger.panelOpen) - .toBe(false, `Expected closing programmatically to set the panel state to closed.`); - expect(overlayContainerElement.textContent) - .toEqual('', `Expected closing programmatically to close the panel.`); + fixture.detectChanges(); + + const panel = + overlayContainerElement.querySelector('.mat-autocomplete-panel') as HTMLElement; + expect(panel.classList) + .toContain('mat-autocomplete-visible', `Expected panel to start out visible.`); + + // Filter down the option list such that no options match the value + input.value = 'af'; + dispatchEvent('input', input); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(true, `Expected panel to stay open when options list is empty.`); + expect(panel.classList) + .toContain('mat-autocomplete-hidden', `Expected panel to hide itself when empty.`); + }); + }); + })); + + it('should keep the label floating until the panel closes', async(() => { + fixture.componentInstance.trigger.openPanel(); + expect(fixture.componentInstance.inputContainer.floatPlaceholder) + .toEqual('always', 'Expected placeholder to float as soon as panel opens.'); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.inputContainer.floatPlaceholder) + .toEqual('auto', 'Expected placeholder to return to auto state after panel closes.'); }); })); @@ -173,11 +215,11 @@ describe('MdAutocomplete', () => { it('should have the correct text direction in RTL', () => { dir = 'rtl'; - const fixture = TestBed.createComponent(SimpleAutocomplete); - fixture.detectChanges(); + const rtlFixture = TestBed.createComponent(SimpleAutocomplete); + rtlFixture.detectChanges(); - fixture.componentInstance.trigger.openPanel(); - fixture.detectChanges(); + rtlFixture.componentInstance.trigger.openPanel(); + rtlFixture.detectChanges(); const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane'); expect(overlayPane.getAttribute('dir')).toEqual('rtl'); @@ -195,34 +237,171 @@ describe('MdAutocomplete', () => { input = fixture.debugElement.query(By.css('input')).nativeElement; }); - it('should fill the text field when an option is selected', () => { + it('should update control value as user types with input value', () => { fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); - const options = - overlayContainerElement.querySelectorAll('md-option') as NodeListOf; - options[1].click(); + input.value = 'a'; + dispatchEvent('input', input); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.value) + .toEqual('a', 'Expected control value to be updated as user types.'); + + input.value = 'al'; + dispatchEvent('input', input); fixture.detectChanges(); - expect(input.value) - .toContain('California', `Expected text field to be filled with selected value.`); + expect(fixture.componentInstance.stateCtrl.value) + .toEqual('al', 'Expected control value to be updated as user types.'); }); - it('should mark the autocomplete control as dirty when an option is selected', () => { + it('should update control value when option is selected with option value', async(() => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.value) + .toEqual({code: 'CA', name: 'California'}, + 'Expected control value to equal the selected option value.'); + }); + })); + + it('should update control back to string if user types after option is selected', async(() => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + input.value = 'Californi'; + dispatchEvent('input', input); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.value) + .toEqual('Californi', 'Expected control value to revert back to string.'); + }); + })); + + it('should fill the text field with display value when an option is selected', async(() => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(input.value) + .toContain('California', `Expected text field to fill with selected value.`); + }); + })); + + it('should fill the text field with value if displayWith is not set', async(() => { fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.componentInstance.panel.displayWith = null; + fixture.componentInstance.options.toArray()[1].value = 'test value'; + fixture.detectChanges(); + + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + + fixture.detectChanges(); + expect(input.value) + .toContain('test value', `Expected input to fall back to selected option's value.`); + }); + })); + + it('should fill the text field correctly if value is set to obj programmatically', async(() => { + fixture.whenStable().then(() => { + fixture.componentInstance.stateCtrl.setValue({code: 'AL', name: 'Alabama'}); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(input.value) + .toContain('Alabama', `Expected input to fill with matching option's viewValue.`); + }); + }); + })); + + it('should clear the text field if value is reset programmatically', async(() => { + input.value = 'Alabama'; + dispatchEvent('input', input); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.componentInstance.stateCtrl.reset(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(input.value).toEqual('', `Expected input value to be empty after reset.`); + }); + }); + })); + + it('should disable input in view when disabled programmatically', () => { + const inputUnderline = + fixture.debugElement.query(By.css('.mat-input-underline')).nativeElement; + + expect(input.disabled) + .toBe(false, `Expected input to start out enabled in view.`); + expect(inputUnderline.classList.contains('mat-disabled')) + .toBe(false, `Expected input underline to start out with normal styles.`); + + fixture.componentInstance.stateCtrl.disable(); + fixture.detectChanges(); + + expect(input.disabled) + .toBe(true, `Expected input to be disabled in view when disabled programmatically.`); + expect(inputUnderline.classList.contains('mat-disabled')) + .toBe(true, `Expected input underline to display disabled styles.`); + }); + + + it('should mark the autocomplete control as dirty as user types', () => { expect(fixture.componentInstance.stateCtrl.dirty) .toBe(false, `Expected control to start out pristine.`); - const options = - overlayContainerElement.querySelectorAll('md-option') as NodeListOf; - options[1].click(); + input.value = 'a'; + dispatchEvent('input', input); fixture.detectChanges(); expect(fixture.componentInstance.stateCtrl.dirty) - .toBe(true, `Expected control to become dirty when an option was selected.`); + .toBe(true, `Expected control to become dirty when the user types into the input.`); }); + it('should mark the autocomplete control as dirty when an option is selected', async(() => { + expect(fixture.componentInstance.stateCtrl.dirty) + .toBe(false, `Expected control to start out pristine.`); + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.dirty) + .toBe(true, `Expected control to become dirty when an option was selected.`); + }); + })); + it('should not mark the control dirty when the value is set programmatically', () => { expect(fixture.componentInstance.stateCtrl.dirty) .toBe(false, `Expected control to start out pristine.`); @@ -234,8 +413,412 @@ describe('MdAutocomplete', () => { .toBe(false, `Expected control to stay pristine if value is set programmatically.`); }); + it('should mark the autocomplete control as touched on blur', () => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + expect(fixture.componentInstance.stateCtrl.touched) + .toBe(false, `Expected control to start out untouched.`); + + dispatchEvent('blur', input); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.touched) + .toBe(true, `Expected control to become touched on blur.`); + }); + + }); + + describe('keyboard events', () => { + let fixture: ComponentFixture; + let input: HTMLInputElement; + let DOWN_ARROW_EVENT: KeyboardEvent; + let ENTER_EVENT: KeyboardEvent; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + input = fixture.debugElement.query(By.css('input')).nativeElement; + DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent; + ENTER_EVENT = new FakeKeyboardEvent(ENTER) as KeyboardEvent; + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + }); + + it('should not focus the option when DOWN key is pressed', async(() => { + fixture.whenStable().then(() => { + spyOn(fixture.componentInstance.options.first, 'focus'); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + expect(fixture.componentInstance.options.first.focus).not.toHaveBeenCalled(); + }); + })); + + it('should not close the panel when DOWN key is pressed', async(() => { + fixture.whenStable().then(() => { + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(true, `Expected panel state to stay open when DOWN key is pressed.`); + expect(overlayContainerElement.textContent) + .toContain('Alabama', `Expected panel to keep displaying when DOWN key is pressed.`); + expect(overlayContainerElement.textContent) + .toContain('California', `Expected panel to keep displaying when DOWN key is pressed.`); + }); + })); + + it('should set the active item to the first option when DOWN key is pressed', async(() => { + fixture.whenStable().then(() => { + const optionEls = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.activeOption) + .toBe(fixture.componentInstance.options.first, 'Expected first option to be active.'); + expect(optionEls[0].classList).toContain('mat-active'); + expect(optionEls[1].classList).not.toContain('mat-active'); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.activeOption) + .toBe(fixture.componentInstance.options.toArray()[1], + 'Expected second option to be active.'); + expect(optionEls[0].classList).not.toContain('mat-active'); + expect(optionEls[1].classList).toContain('mat-active'); + }); + }); + }); + })); + + it('should set the active item to the last option when UP key is pressed', async(() => { + fixture.whenStable().then(() => { + const optionEls = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + + const UP_ARROW_EVENT = new FakeKeyboardEvent(UP_ARROW) as KeyboardEvent; + fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.activeOption) + .toBe(fixture.componentInstance.options.last, 'Expected last option to be active.'); + expect(optionEls[10].classList).toContain('mat-active'); + expect(optionEls[0].classList).not.toContain('mat-active'); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.componentInstance.trigger.activeOption) + .toBe(fixture.componentInstance.options.first, + 'Expected first option to be active.'); + expect(optionEls[0].classList).toContain('mat-active'); + expect(optionEls[10].classList).not.toContain('mat-active'); + }); + }); + }); + })); + + it('should set the active item properly after filtering', async(() => { + fixture.whenStable().then(() => { + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + input.value = 'o'; + dispatchEvent('input', input); + fixture.detectChanges(); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + const optionEls = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + + expect(fixture.componentInstance.trigger.activeOption) + .toBe(fixture.componentInstance.options.first, + 'Expected first option to be active.'); + expect(optionEls[0].classList).toContain('mat-active'); + expect(optionEls[1].classList).not.toContain('mat-active'); + }); + }); + }); + })); + + it('should fill the text field when an option is selected with ENTER', async(() => { + fixture.whenStable().then(() => { + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.componentInstance.trigger._handleKeydown(ENTER_EVENT); + + fixture.detectChanges(); + expect(input.value) + .toContain('Alabama', `Expected text field to fill with selected value on ENTER.`); + }); + })); + + it('should fill the text field, not select an option, when SPACE is entered', async(() => { + fixture.whenStable().then(() => { + input.value = 'New'; + dispatchEvent('input', input); + fixture.detectChanges(); + + const SPACE_EVENT = new FakeKeyboardEvent(SPACE) as KeyboardEvent; + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.componentInstance.trigger._handleKeydown(SPACE_EVENT); + fixture.detectChanges(); + + expect(input.value) + .not.toContain('New York', `Expected option not to be selected on SPACE.`); + }); + })); + + it('should mark the control dirty when selecting an option from the keyboard', async(() => { + fixture.whenStable().then(() => { + expect(fixture.componentInstance.stateCtrl.dirty) + .toBe(false, `Expected control to start out pristine.`); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.componentInstance.trigger._handleKeydown(ENTER_EVENT); + fixture.detectChanges(); + + expect(fixture.componentInstance.stateCtrl.dirty) + .toBe(true, `Expected control to become dirty when option was selected by ENTER.`); + }); + })); + + it('should open the panel again when typing after making a selection', async(() => { + fixture.whenStable().then(() => { + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.componentInstance.trigger._handleKeydown(ENTER_EVENT); + fixture.detectChanges(); + + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(false, `Expected panel state to read closed after ENTER key.`); + expect(overlayContainerElement.textContent) + .toEqual('', `Expected panel to close after ENTER key.`); + + input.value = 'Alabam'; + dispatchEvent('input', input); + fixture.detectChanges(); + + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(true, `Expected panel state to read open when typing in input.`); + expect(overlayContainerElement.textContent) + .toContain('Alabama', `Expected panel to display when typing in input.`); + }); + })); + + it('should scroll to active options below the fold', async(() => { + fixture.whenStable().then(() => { + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel'); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.detectChanges(); + expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to scroll.`); + + // These down arrows will set the 6th option active, below the fold. + [1, 2, 3, 4, 5].forEach(() => { + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + }); + fixture.detectChanges(); + + // Expect option bottom minus the panel height (288 - 256 = 32) + expect(scrollContainer.scrollTop).toEqual(32, `Expected panel to reveal the sixth option.`); + }); + + })); + }); + + describe('aria', () => { + let fixture: ComponentFixture; + let input: HTMLInputElement; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + input = fixture.debugElement.query(By.css('input')).nativeElement; + }); + + it('should set role of input to combobox', () => { + expect(input.getAttribute('role')) + .toEqual('combobox', 'Expected role of input to be combobox.'); + }); + + it('should set role of autocomplete panel to listbox', () => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel')).nativeElement; + + expect(panel.getAttribute('role')) + .toEqual('listbox', 'Expected role of the panel to be listbox.'); + }); + + it('should set aria-autocomplete to list', () => { + expect(input.getAttribute('aria-autocomplete')) + .toEqual('list', 'Expected aria-autocomplete attribute to equal list.'); + }); + + it('should set aria-multiline to false', () => { + expect(input.getAttribute('aria-multiline')) + .toEqual('false', 'Expected aria-multiline attribute to equal false.'); + }); + + it('should set aria-activedescendant based on the active option', async(() => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(input.hasAttribute('aria-activedescendant')) + .toBe(false, 'Expected aria-activedescendant to be absent if no active item.'); + + const DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent; + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.detectChanges(); + + expect(input.getAttribute('aria-activedescendant')) + .toEqual(fixture.componentInstance.options.first.id, + 'Expected aria-activedescendant to match the active item after 1 down arrow.'); + + fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); + fixture.detectChanges(); + + expect(input.getAttribute('aria-activedescendant')) + .toEqual(fixture.componentInstance.options.toArray()[1].id, + 'Expected aria-activedescendant to match the active item after 2 down arrows.'); + }); + })); + + it('should set aria-expanded based on whether the panel is open', async(() => { + expect(input.getAttribute('aria-expanded')) + .toBe('false', 'Expected aria-expanded to be false while panel is closed.'); + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + expect(input.getAttribute('aria-expanded')) + .toBe('true', 'Expected aria-expanded to be true while panel is open.'); + + fixture.componentInstance.trigger.closePanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(input.getAttribute('aria-expanded')) + .toBe('false', 'Expected aria-expanded to be false when panel closes again.'); + }); + })); + + it('should set aria-owns based on the attached autocomplete', () => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + const panel = fixture.debugElement.query(By.css('.mat-autocomplete-panel')).nativeElement; + + expect(input.getAttribute('aria-owns')) + .toEqual(panel.getAttribute('id'), 'Expected aria-owns to match attached autocomplete.'); + + }); + }); + describe('Fallback positions', () => { + let fixture: ComponentFixture; + let input: HTMLInputElement; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + input = fixture.debugElement.query(By.css('input')).nativeElement; + }); + + it('should use below positioning by default', () => { + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + const inputBottom = input.getBoundingClientRect().bottom; + const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel'); + const panelTop = panel.getBoundingClientRect().top; + + // Panel is offset by 6px in styles so that the underline has room to display. + expect((inputBottom + 6).toFixed(1)) + .toEqual(panelTop.toFixed(1), `Expected panel top to match input bottom by default.`); + expect(fixture.componentInstance.trigger.autocomplete.positionY) + .toEqual('below', `Expected autocomplete positionY to default to below.`); + }); + + it('should fall back to above position if panel cannot fit below', () => { + // Push the autocomplete trigger down so it won't have room to open "below" + input.style.top = '600px'; + input.style.position = 'relative'; + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + const inputTop = input.getBoundingClientRect().top; + const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel'); + const panelBottom = panel.getBoundingClientRect().bottom; + + // Panel is offset by 24px in styles so that the label has room to display. + expect((inputTop - 24).toFixed(1)) + .toEqual(panelBottom.toFixed(1), `Expected panel to fall back to above position.`); + expect(fixture.componentInstance.trigger.autocomplete.positionY) + .toEqual('above', `Expected autocomplete positionY to be "above" if panel won't fit.`); + }); + + it('should align panel properly when filtering in "above" position', async(() => { + // Push the autocomplete trigger down so it won't have room to open "below" + input.style.top = '600px'; + input.style.position = 'relative'; + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + input.value = 'f'; + dispatchEvent('input', input); + fixture.detectChanges(); + + const inputTop = input.getBoundingClientRect().top; + const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel'); + const panelBottom = panel.getBoundingClientRect().bottom; + + // Panel is offset by 24px in styles so that the label has room to display. + expect((inputTop - 24).toFixed(1)) + .toEqual(panelBottom.toFixed(1), `Expected panel to stay aligned after filtering.`); + expect(fixture.componentInstance.trigger.autocomplete.positionY) + .toEqual('above', `Expected autocomplete positionY to be "above" if panel won't fit.`); + }); + })); + + }); + + describe('misc', () => { + + it('should allow basic use without any forms directives', () => { + expect(() => { + const fixture = TestBed.createComponent(AutocompleteWithoutForms); + fixture.detectChanges(); + + const input = fixture.debugElement.query(By.css('input')).nativeElement; + input.value = 'd'; + dispatchEvent('input', input); + fixture.detectChanges(); + + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + expect(options.length).toBe(1); + }).not.toThrowError(); + }); + + }); }); @Component({ @@ -244,9 +827,9 @@ describe('MdAutocomplete', () => { - - - {{ state.name }} ({{ state.code }}) + + + {{ state.code }}: {{ state.name }} ` @@ -257,6 +840,9 @@ class SimpleAutocomplete implements OnDestroy { valueSub: Subscription; @ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger; + @ViewChild(MdAutocomplete) panel: MdAutocomplete; + @ViewChild(MdInputContainer) inputContainer: MdInputContainer; + @ViewChildren(MdOption) options: QueryList; states = [ {code: 'AL', name: 'Alabama'}, @@ -272,6 +858,7 @@ class SimpleAutocomplete implements OnDestroy { {code: 'WY', name: 'Wyoming'}, ]; + constructor() { this.filteredStates = this.states; this.valueSub = this.stateCtrl.valueChanges.subscribe(val => { @@ -280,6 +867,10 @@ class SimpleAutocomplete implements OnDestroy { }); } + displayFn(value: any): string { + return value ? value.name : value; + } + ngOnDestroy() { this.valueSub.unsubscribe(); } @@ -287,6 +878,34 @@ class SimpleAutocomplete implements OnDestroy { } +@Component({ + template: ` + + + + + + + {{ state }} + + + ` +}) +class AutocompleteWithoutForms { + filteredStates: any[]; + states = ['Alabama', 'California', 'Florida']; + + constructor() { + this.filteredStates = this.states.slice(); + } + + onInput(value: any) { + this.filteredStates = this.states.filter(s => new RegExp(value, 'gi').test(s)); + } + +} + /** * TODO: Move this to core testing utility until Angular has event faking * support. @@ -301,4 +920,8 @@ function dispatchEvent(eventName: string, element: HTMLElement): void { element.dispatchEvent(event); } - +/** This is a mock keyboard event to test keyboard events in the autocomplete. */ +class FakeKeyboardEvent { + constructor(public keyCode: number) {} + preventDefault() {} +} diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index bb2abbca20b4..bbed800589cc 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -1,6 +1,8 @@ import { Component, ContentChildren, + ElementRef, + Input, QueryList, TemplateRef, ViewChild, @@ -8,17 +10,67 @@ import { } from '@angular/core'; import {MdOption} from '../core'; +/** + * Autocomplete IDs need to be unique across components, so this counter exists outside of + * the component definition. + */ +let _uniqueAutocompleteIdCounter = 0; + +export type AutocompletePositionY = 'above' | 'below'; + @Component({ moduleId: module.id, selector: 'md-autocomplete, mat-autocomplete', templateUrl: 'autocomplete.html', styleUrls: ['autocomplete.css'], encapsulation: ViewEncapsulation.None, - exportAs: 'mdAutocomplete' + exportAs: 'mdAutocomplete', + host: { + '[class.mat-autocomplete]': 'true' + } }) export class MdAutocomplete { + /** Whether the autocomplete panel displays above or below its trigger. */ + positionY: AutocompletePositionY = 'below'; + + /** Whether the autocomplete panel should be visible, depending on option length. */ + showPanel = false; + @ViewChild(TemplateRef) template: TemplateRef; + @ViewChild('panel') panel: ElementRef; @ContentChildren(MdOption) options: QueryList; + + /** Function that maps an option's control value to its display value in the trigger. */ + @Input() displayWith: (value: any) => string; + + /** Unique ID to be used by autocomplete trigger's "aria-owns" property. */ + id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`; + + /** + * Sets the panel scrollTop. This allows us to manually scroll to display + * options below the fold, as they are not actually being focused when active. + */ + _setScrollTop(scrollTop: number): void { + if (this.panel) { + this.panel.nativeElement.scrollTop = scrollTop; + } + } + + /** Panel should hide itself when the option list is empty. */ + _setVisibility() { + Promise.resolve().then(() => this.showPanel = !!this.options.length); + } + + /** Sets a class on the panel based on its position (used to set y-offset). */ + _getClassList() { + return { + 'mat-autocomplete-panel-below': this.positionY === 'below', + 'mat-autocomplete-panel-above': this.positionY === 'above', + 'mat-autocomplete-visible': this.showPanel, + 'mat-autocomplete-hidden': !this.showPanel + }; + } + } diff --git a/src/lib/autocomplete/index.ts b/src/lib/autocomplete/index.ts index 84e8c23169e8..9cbfc471ee27 100644 --- a/src/lib/autocomplete/index.ts +++ b/src/lib/autocomplete/index.ts @@ -1,12 +1,14 @@ import {ModuleWithProviders, NgModule} from '@angular/core'; + import {MdOptionModule, OverlayModule, OVERLAY_PROVIDERS, CompatibilityModule} from '../core'; +import {CommonModule} from '@angular/common'; import {MdAutocomplete} from './autocomplete'; import {MdAutocompleteTrigger} from './autocomplete-trigger'; export * from './autocomplete'; export * from './autocomplete-trigger'; @NgModule({ - imports: [MdOptionModule, OverlayModule, CompatibilityModule], + imports: [MdOptionModule, OverlayModule, CompatibilityModule, CommonModule], exports: [MdAutocomplete, MdOptionModule, MdAutocompleteTrigger, CompatibilityModule], declarations: [MdAutocomplete, MdAutocompleteTrigger], }) diff --git a/src/lib/button-toggle/README.md b/src/lib/button-toggle/README.md index befdfbebdff9..fb4499d1cda0 100644 --- a/src/lib/button-toggle/README.md +++ b/src/lib/button-toggle/README.md @@ -1,139 +1 @@ -# md-button-toggle - -`MdButtonToggle` is a group of buttons that can be toggled. -There are two modes, `multiple` and `exclusive`. -When in 'exclusive' mode, only one button can be selected at a time (like radio buttons). -When in 'multiple' mode, multiple buttons can be selected at once (like checkboxes). -You can read more about button toggles in the -[Material Design spec](https://material.google.com/components/buttons.html#buttons-toggle-buttons). - -## Usage - -### Basic Usage - -`md-button-toggle` can be used on its own and acts as a checkbox. - -```html -Bold -``` - -Output: - -![Basic Toggle Button Example](https://material.angularjs.org/material2_assets/button-toggle/basic-toggle.png) - -### Exclusive Selection - -`md-button-toggle` can be used in an exclusive selection group when surrounded by -`md-button-toggle-group`. This styles all buttons within the group to appear as a single -group of button toggles and allows only one button toggle to be selected at a time. - -```html - - format_align_left - format_align_center - format_align_right - format_align_justify - -``` - -Output: - -![Exclusive Toggle Button Example](https://material.angularjs.org/material2_assets/button-toggle/exclusive-toggle.png) - -### Multiple Selection - -`md-button-toggle` can be used in a multiple selection group when surrounded by -`md-button-toggle-group multiple`. This styles all buttons within the group to appear as a single -group of button toggles. This component does not yet support `NgModel` when using `multiple` mode. - -```html - - Flour - Eggs - Sugar - Milk - -``` - -Output: - -![Multiple Toggle Button Example](https://material.angularjs.org/material2_assets/button-toggle/multi-toggle.png) - -## Dynamic Exclusive Selection - -`md-button-toggle`s can be used with `ngModel` to dynamically create groups and update the value for -a group. - -```html - - - {{pie}} - - -

Your favorite type of pie is: {{favoritePie}}

-``` - -### Disabled Button Toggle - -`md-button-toggle-group` and `md-button-toggle` can both be disabled by adding a `disabled` -attribute to either one. Adding `disabled` to an exclusive group or a multiple group will disable -the entire group. Adding `disabled` to a single toggle will disable that toggle. - -```html - - One - Two - Three - - - - Red - Blue - -``` - -Output: - -![Disabled Toggle Buttons Example](https://material.angularjs.org/material2_assets/button-toggle/disabled-toggles.png) - -## `` - -### Bound Properties - -| Name | Type | Description | -| --- | --- | --- | -| `id` | string | The unique ID of the toggle. IDs are generated by default when not specified. | -| `name` | string | Optional, defaults to parent group name if one exists for exclusive selection toggles, otherwise null. This is used to differentiate between different groups. | -| `value` | any | Value of the toggle. Only used when in a group to determine which are selected. | -| `checked` | boolean | Optional, default = `false`. Whether or not the toggle is checked. | -| `disabled` | boolean | Optional, default = `false`. Whether or not the toggle is disabled | - -### Events - -| Name | Description | -| --- | --- | -| `change` | Emitted when the `checked` value is changed. | - -## `` - -### Bound Properties - -| Name | Type | Description | -| --- | --- | --- | -| `name` | string | Optional, the name of the group. | -| `disabled` | boolean | Optional, default = `false`. | -| `value` | any | The current value for the group. Will be set to the value of the selected toggle or a list of values from the selected toggles. | -| `selected` | `mdButtonToggle` | The current selected toggle or a list of the selected toggles. | -| `vertical` | boolean | Whether the group should show the toggles vertically. Default is `false`. | - -### Attributes - -| Name | Type | Description | -| --- | --- | --- | -| `multiple` | boolean | Optional, default = `false`. Whether or not the group allows multiple selection. | - -### Events - -| Name | Description | -| --- | --- | -| `change` | Emitted when the `value` of the group changes. | +Please see the official documentation at https://material.angular.io/components/component/button-toggle \ No newline at end of file diff --git a/src/lib/button-toggle/_button-toggle-theme.scss b/src/lib/button-toggle/_button-toggle-theme.scss index e46e437518a5..4b7f6c9309f3 100644 --- a/src/lib/button-toggle/_button-toggle-theme.scss +++ b/src/lib/button-toggle/_button-toggle-theme.scss @@ -2,21 +2,25 @@ @import '../core/theming/theming'; -@mixin md-button-toggle-theme($theme) { +@mixin mat-button-toggle-theme($theme) { $foreground: map-get($theme, foreground); $background: map-get($theme, background); - md-button-toggle { - color: md-color($foreground, hint-text); + .mat-button-toggle { + color: mat-color($foreground, hint-text); } - .md-button-toggle-checked { - background-color: md-color($md-grey, 300); - color: md-color($foreground, base); + .mat-button-toggle-checked { + background-color: mat-color($mat-grey, 300); + color: mat-color($foreground, base); } - .md-button-toggle-disabled { - background-color: map_get($md-grey, 200); - color: md-color($foreground, disabled-button); + .mat-button-toggle-disabled { + background-color: map_get($mat-grey, 200); + color: mat-color($foreground, disabled-button); + + &.mat-button-toggle-checked { + background-color: mat-color($mat-grey, 400); + } } } diff --git a/src/lib/button-toggle/button-toggle.html b/src/lib/button-toggle/button-toggle.html index 60cccfd2e44a..face84340cfa 100644 --- a/src/lib/button-toggle/button-toggle.html +++ b/src/lib/button-toggle/button-toggle.html @@ -1,5 +1,5 @@ -