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

refactor(chips): Deprecate md-on-append in favor of new methods. #5497

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/components/autocomplete/demoInsideDialog/dialog.tmpl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<md-dialog aria-label="Autocomplete Dialog Example" ng-cloak>
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Autocomplete Dialog Example</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="ctrl.cancel()">
<md-icon md-svg-src="img/icons/ic_close_24px.svg" aria-label="Close dialog"></md-icon>
</md-button>
</div>
</md-toolbar>

<md-dialog-content>
<div class="md-dialog-content">
<form ng-submit="$event.preventDefault()">
<p>Use <code>md-autocomplete</code> to search for matches from local or remote data sources.</p>
<md-autocomplete
md-selected-item="ctrl.selectedItem"
md-search-text="ctrl.searchText"
md-items="item in ctrl.querySearch(ctrl.searchText)"
md-item-text="item.display"
md-min-length="0"
placeholder="What is your favorite US state?">
<md-item-template>
<span md-highlight-text="ctrl.searchText" md-highlight-flags="^i">{{item.display}}</span>
</md-item-template>
<md-not-found>
No states matching "{{ctrl.searchText}}" were found.
</md-not-found>
</md-autocomplete>
</form>
</div>
</md-dialog-content>

<div class="md-actions">
<md-button aria-label="Finished" ng-click="ctrl.finish($event)">Finished</md-button>
</div>
</md-dialog>
9 changes: 9 additions & 0 deletions src/components/autocomplete/demoInsideDialog/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div ng-controller="DemoCtrl as ctrl" layout="column" ng-cloak>
<md-content class="md-padding">
<p>
Click the button below to open the dialog with an autocomplete.
</p>

<md-button ng-click="ctrl.openDialog($event)" class="md-raised">Open Dialog</md-button>
</md-content>
</div>
84 changes: 84 additions & 0 deletions src/components/autocomplete/demoInsideDialog/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
(function () {
'use strict';
angular
.module('autocompleteDemoInsideDialog', ['ngMaterial'])
.controller('DemoCtrl', DemoCtrl);

function DemoCtrl($mdDialog) {
var self = this;

self.openDialog = function($event) {
$mdDialog.show({
controller: DialogCtrl,
controllerAs: 'ctrl',
templateUrl: 'dialog.tmpl.html',
parent: angular.element(document.body),
targetEvent: $event,
clickOutsideToClose:true
})
}
}

function DialogCtrl ($timeout, $q, $scope, $mdDialog) {
var self = this;

// list of `state` value/display objects
self.states = loadAll();
self.querySearch = querySearch;

// ******************************
// Template methods
// ******************************

self.cancel = function($event) {
$mdDialog.cancel();
};
self.finish = function($event) {
$mdDialog.hide();
};

// ******************************
// Internal methods
// ******************************

/**
* Search for states... use $timeout to simulate
* remote dataservice call.
*/
function querySearch (query) {
return query ? self.states.filter( createFilterFor(query) ) : self.states;
}

/**
* Build `states` list of key/value pairs
*/
function loadAll() {
var allStates = 'Alabama, Alaska, Arizona, Arkansas, California, Colorado, Connecticut, Delaware,\
Florida, Georgia, Hawaii, Idaho, Illinois, Indiana, Iowa, Kansas, Kentucky, Louisiana,\
Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana,\
Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina,\
North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina,\
South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia,\
Wisconsin, Wyoming';

return allStates.split(/, +/g).map( function (state) {
return {
value: state.toLowerCase(),
display: state
};
});
}

/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);

return function filterFn(state) {
return (state.value.indexOf(lowercaseQuery) === 0);
};

}
}
})();
2 changes: 1 addition & 1 deletion src/components/autocomplete/js/autocompleteController.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,9 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
select(ctrl.index);
break;
case $mdConstant.KEY_CODE.ENTER:
if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return;
event.stopPropagation();
event.preventDefault();
if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return;
select(ctrl.index);
break;
case $mdConstant.KEY_CODE.ESCAPE:
Expand Down
144 changes: 143 additions & 1 deletion src/components/chips/chips.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ describe('<md-chips>', function() {

var BASIC_CHIP_TEMPLATE =
'<md-chips ng-model="items"></md-chips>';
var CHIP_TRANSFORM_TEMPLATE =
'<md-chips ng-model="items" md-transform-chip="transformChip($chip)"></md-chips>';
var CHIP_APPEND_TEMPLATE =
'<md-chips ng-model="items" md-on-append="appendChip($chip)"></md-chips>';
var CHIP_ADD_TEMPLATE =
'<md-chips ng-model="items" md-on-add="addChip($chip, $index)"></md-chips>';
var CHIP_REMOVE_TEMPLATE =
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index)"></md-chips>';
var CHIP_SELECT_TEMPLATE =
Expand Down Expand Up @@ -109,7 +113,15 @@ describe('<md-chips>', function() {
expect(chips[1].innerHTML).toContain('Orange');
});

it('should call the append method when adding a chip', function() {
// TODO: Remove in 1.0 release after deprecation
it('should warn of deprecation when using md-on-append', inject(function($log) {
spyOn($log, 'warn');
buildChips(CHIP_APPEND_TEMPLATE);
expect($log.warn).toHaveBeenCalled();
}));

// TODO: Remove in 1.0 release after deprecation
it('should retain the deprecated md-on-append functionality until removed', function() {
var element = buildChips(CHIP_APPEND_TEMPLATE);
var ctrl = element.controller('mdChips');

Expand All @@ -129,6 +141,62 @@ describe('<md-chips>', function() {
expect(scope.items[3]).toBe('GrapeGrape');
});

it('should call the transform method when adding a chip', function() {
var element = buildChips(CHIP_TRANSFORM_TEMPLATE);
var ctrl = element.controller('mdChips');

var doubleText = function(text) {
return "" + text + text;
};
scope.transformChip = jasmine.createSpy('transformChip').and.callFake(doubleText);

element.scope().$apply(function() {
ctrl.chipBuffer = 'Grape';
simulateInputEnterKey(ctrl);
});

expect(scope.transformChip).toHaveBeenCalled();
expect(scope.transformChip.calls.mostRecent().args[0]).toBe('Grape');
expect(scope.items.length).toBe(4);
expect(scope.items[3]).toBe('GrapeGrape');
});

it('should not add the chip if md-transform-chip returns null', function() {
var element = buildChips(CHIP_TRANSFORM_TEMPLATE);
var ctrl = element.controller('mdChips');

var nullChip = function(text) {
return null;
};
scope.transformChip = jasmine.createSpy('transformChip').and.callFake(nullChip);

element.scope().$apply(function() {
ctrl.chipBuffer = 'Grape';
simulateInputEnterKey(ctrl);
});

expect(scope.transformChip).toHaveBeenCalled();
expect(scope.transformChip.calls.mostRecent().args[0]).toBe('Grape');
expect(scope.items.length).toBe(3);
});

it('should call the add method when adding a chip', function() {
var element = buildChips(CHIP_ADD_TEMPLATE);
var ctrl = element.controller('mdChips');

scope.addChip = jasmine.createSpy('addChip');

element.scope().$apply(function() {
ctrl.chipBuffer = 'Grape';
simulateInputEnterKey(ctrl);
});

expect(scope.addChip).toHaveBeenCalled();
expect(scope.addChip.calls.mostRecent().args[0]).toBe('Grape'); // Chip
expect(scope.addChip.calls.mostRecent().args[1]).toBe(4); // Index
});


it('should call the remove method when removing a chip', function() {
var element = buildChips(CHIP_REMOVE_TEMPLATE);
var ctrl = element.controller('mdChips');
Expand Down Expand Up @@ -328,6 +396,80 @@ describe('<md-chips>', function() {
expect(scope.items[3]).toBe('Kiwi');
expect(element.find('input').val()).toBe('');
}));

it('simultaneously allows selecting an existing chip AND adding a new one', inject(function($mdConstant) {
// Setup our scope and function
setupScopeForAutocomplete();
scope.transformChip = jasmine.createSpy('transformChip');

// Modify the base template to add md-transform-chip
var modifiedTemplate = AUTOCOMPLETE_CHIPS_TEMPLATE
.replace('<md-chips', '<md-chips md-on-append="transformChip($chip)"');

var element = buildChips(modifiedTemplate);

var ctrl = element.controller('mdChips');
$timeout.flush(); // mdAutcomplete needs a flush for its init.
var autocompleteCtrl = element.find('md-autocomplete').controller('mdAutocomplete');

element.scope().$apply(function() {
autocompleteCtrl.scope.searchText = 'K';
});
autocompleteCtrl.focus();
$timeout.flush();

/*
* Send a down arrow/enter to select the right fruit
*/
var downArrowEvent = {
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.DOWN_ARROW,
which: $mdConstant.KEY_CODE.DOWN_ARROW
};
var enterEvent = {
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.ENTER,
which: $mdConstant.KEY_CODE.ENTER
};
element.find('input').triggerHandler(downArrowEvent);
element.find('input').triggerHandler(enterEvent);
$timeout.flush();

// Check our transformChip calls
expect(scope.transformChip).not.toHaveBeenCalledWith('K');
expect(scope.transformChip).toHaveBeenCalledWith('Kiwi');
expect(scope.transformChip.calls.count()).toBe(1);

// Check our output
expect(scope.items.length).toBe(4);
expect(scope.items[3]).toBe('Kiwi');
expect(element.find('input').val()).toBe('');

// Reset our jasmine spy
scope.transformChip.calls.reset();

/*
* Use the "new chip" functionality
*/

// Set the search text
element.scope().$apply(function() {
autocompleteCtrl.scope.searchText = 'Acai Berry';
});

// Fire our event and flush any timeouts
element.find('input').triggerHandler(enterEvent);
$timeout.flush();

// Check our transformChip calls
expect(scope.transformChip).toHaveBeenCalledWith('Acai Berry');
expect(scope.transformChip.calls.count()).toBe(1);

// Check our output
expect(scope.items.length).toBe(5);
expect(scope.items[4]).toBe('Acai Berry');
expect(element.find('input').val()).toBe('');
}));
});

describe('user input templates', function() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/chips/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ <h2 class="md-title">Display an ordered set of objects as chips (with custom tem
<p>Note: the variables <code>$chip</code> and <code>$index</code> are available in custom chip templates.</p>

<md-chips class="custom-chips" ng-model="ctrl.vegObjs" readonly="ctrl.readonly"
md-on-append="ctrl.newVeg($chip)">
md-transform-chip="ctrl.newVeg($chip)">
<md-chip-template>
<span>
<strong>[{{$index}}] {{$chip.name}}</strong>
Expand Down
7 changes: 7 additions & 0 deletions src/components/chips/demoContactChips/style.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
md-content.autocomplete {
min-height: 250px;

// NOTE: Due to a bug with the virtual repeat sizing, we must manually set the width of
// the input so that the autocomplete popup will be properly sized. See issue #4450.
input {
min-width: 400px;
}
}
.md-item-text.compact {
padding-top: 8px;
Expand All @@ -15,6 +21,7 @@ md-content.autocomplete {
}
.md-list-item-text {
padding: 14px 0;
max-width: 190px;
h3 {
margin: 0 !important;
padding: 0;
Expand Down
8 changes: 7 additions & 1 deletion src/components/chips/demoCustomInputs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ <h2 class="md-title">Use an <code>input</code> element to build an ordered set
<br/>
<h2 class="md-title">Use <code>md-autocomplete</code> to build an ordered set of chips.</h2>

<md-chips ng-model="ctrl.selectedVegetables" md-autocomplete-snap md-require-match="true">
<md-chips ng-model="ctrl.selectedVegetables" md-autocomplete-snap
md-transform-chip="ctrl.transformChip($chip)"
md-require-match="ctrl.autocompleteDemoRequireMatch">
<md-autocomplete
md-selected-item="ctrl.selectedItem"
md-search-text="ctrl.searchText"
Expand All @@ -32,6 +34,10 @@ <h2 class="md-title">Use <code>md-autocomplete</code> to build an ordered set o
</md-chip-template>
</md-chips>

<md-checkbox ng-model="ctrl.autocompleteDemoRequireMatch">
Tell the autocomplete to require a match (when enabled you cannot create new chips)
</md-checkbox>

<br />
<h2 class="md-title">Vegetable Options</h2>

Expand Down
15 changes: 15 additions & 0 deletions src/components/chips/demoCustomInputs/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@
self.numberChips = [];
self.numberChips2 = [];
self.numberBuffer = '';
self.autocompleteDemoRequireMatch = true;
self.transformChip = transformChip;

/**
* Return the proper object when the append is called.
*/
function transformChip(chip) {
// If it is an object, it's already a known chip
if (angular.isObject(chip)) {
return chip;
}

// Otherwise, create a new one
return { name: chip, type: 'new' }
}

/**
* Search for vegetables.
Expand Down
Loading