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

Commit 251cfed

Browse files
Splaktarjelbourn
authored andcommitted
fix(checkbox): enter submits form when submit button is disabled (#11640)
match the behavior of the native input type checkbox Fixes #11639
1 parent 7cde443 commit 251cfed

File tree

3 files changed

+312
-14
lines changed

3 files changed

+312
-14
lines changed

src/components/checkbox/checkbox.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,31 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $
169169
}
170170
}
171171

172+
/**
173+
* @param {KeyboardEvent} ev 'keypress' event to handle
174+
*/
172175
function keypressHandler(ev) {
173176
var keyCode = ev.which || ev.keyCode;
177+
var submit, form;
178+
174179
ev.preventDefault();
175180
switch (keyCode) {
176181
case $mdConstant.KEY_CODE.SPACE:
177182
element.addClass('md-focused');
178183
listener(ev);
179184
break;
180185
case $mdConstant.KEY_CODE.ENTER:
181-
var form = $mdUtil.getClosest(ev.target, 'form');
182-
// We have to use a native event here as the form directive does not use jqlite
186+
// Match the behavior of the native <input type="checkbox">.
187+
// When the enter key is pressed while focusing a native checkbox inside a form,
188+
// the browser will trigger a `click` on the first non-disabled submit button/input
189+
// in the form. Note that this is different from text inputs, which
190+
// will directly submit the form without needing a submit button/input to be present.
191+
form = $mdUtil.getClosest(ev.target, 'form');
183192
if (form) {
184-
form.dispatchEvent(new Event('submit'));
193+
submit = form.querySelector('button[type="submit"]:enabled, input[type="submit"]:enabled');
194+
if (submit) {
195+
submit.click();
196+
}
185197
}
186198
break;
187199
}

src/components/checkbox/checkbox.spec.js

Lines changed: 295 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
describe('mdCheckbox', function() {
33
var CHECKED_CSS = 'md-checked';
44
var INDETERMINATE_CSS = 'md-indeterminate';
5-
var $compile, $log, pageScope, $mdConstant;
5+
var $compile, $log, pageScope, $mdConstant, $window;
66

77
beforeEach(module('ngAria', 'material.components.checkbox'));
88

99
beforeEach(inject(function($injector) {
1010
$compile = $injector.get('$compile');
1111
$log = $injector.get('$log');
1212
$mdConstant = $injector.get('$mdConstant');
13+
$window = $injector.get('$window');
1314

1415
var $rootScope = $injector.get('$rootScope');
15-
pageScope = $rootScope.$new();
16+
pageScope = $rootScope.$new(false);
1617
}));
1718

1819
function compileAndLink(template, opt_scope) {
@@ -298,15 +299,299 @@ describe('mdCheckbox', function() {
298299
expect(isChecked(checkbox)).toBe(false);
299300
});
300301

301-
it('should submit a parent form when ENTER is pressed', function () {
302-
var form = compileAndLink('<form><md-checkbox ng-checked="value"></md-checkbox></form>');
303-
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
304-
type: 'keypress',
305-
keyCode: $mdConstant.KEY_CODE.ENTER
302+
it('should not submit a parent form when ENTER is pressed and there is no submit button/input',
303+
function() {
304+
var submitted = false;
305+
pageScope.onSubmit = function() {
306+
submitted = true;
307+
};
308+
var form = compileAndLink('<form name="form" ng-submit="onSubmit()">' +
309+
'<md-checkbox ng-checked="value"></md-checkbox></form>');
310+
311+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
312+
var node = $window.document.body.appendChild(form[0]);
313+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
314+
type: 'keypress',
315+
keyCode: $mdConstant.KEY_CODE.ENTER
316+
});
317+
pageScope.$apply();
318+
expect(submitted).toBe(false);
319+
expect(pageScope.form.$submitted).toBe(false);
320+
$window.document.body.removeChild(node);
321+
});
322+
323+
it('should click an enabled submit button when ENTER is pressed',
324+
function() {
325+
var submitted = false;
326+
pageScope.onSubmit = function() {
327+
submitted = true;
328+
};
329+
var form = compileAndLink(
330+
'<form name="form"><md-checkbox ng-checked="value"></md-checkbox>' +
331+
'<button type="submit" ng-click="onSubmit()">Submit</button></form>');
332+
333+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
334+
var node = $window.document.body.appendChild(form[0]);
335+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
336+
type: 'keypress',
337+
keyCode: $mdConstant.KEY_CODE.ENTER
338+
});
339+
pageScope.$apply();
340+
expect(submitted).toBe(true);
341+
expect(pageScope.form.$submitted).toBe(true);
342+
$window.document.body.removeChild(node);
343+
});
344+
345+
it('should click an enabled submit input when ENTER is pressed',
346+
function() {
347+
var submitted = false;
348+
pageScope.onSubmit = function() {
349+
submitted = true;
350+
};
351+
var form = compileAndLink(
352+
'<form name="form"><md-checkbox ng-checked="value"></md-checkbox>' +
353+
'<input type="submit" ng-click="onSubmit()" value="Submit"></form>');
354+
355+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
356+
var node = $window.document.body.appendChild(form[0]);
357+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
358+
type: 'keypress',
359+
keyCode: $mdConstant.KEY_CODE.ENTER
360+
});
361+
pageScope.$apply();
362+
expect(submitted).toBe(true);
363+
expect(pageScope.form.$submitted).toBe(true);
364+
$window.document.body.removeChild(node);
365+
});
366+
367+
it('should submit a parent form when ENTER is pressed and there is an enabled submit button',
368+
function() {
369+
var submitted = false;
370+
pageScope.onSubmit = function() {
371+
submitted = true;
372+
};
373+
var form = compileAndLink(
374+
'<form name="form" ng-submit="onSubmit()"><md-checkbox ng-checked="value">' +
375+
'</md-checkbox><button type="submit">Submit</button></form>');
376+
377+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
378+
var node = $window.document.body.appendChild(form[0]);
379+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
380+
type: 'keypress',
381+
keyCode: $mdConstant.KEY_CODE.ENTER
382+
});
383+
pageScope.$apply();
384+
expect(submitted).toBe(true);
385+
expect(pageScope.form.$submitted).toBe(true);
386+
$window.document.body.removeChild(node);
387+
});
388+
389+
it('should submit a parent form when ENTER is pressed and there is an enabled submit input',
390+
function() {
391+
var submitted = false;
392+
pageScope.onSubmit = function() {
393+
submitted = true;
394+
};
395+
var form = compileAndLink(
396+
'<form name="form" ng-submit="onSubmit()"><md-checkbox ng-checked="value">' +
397+
'</md-checkbox><input type="submit" value="Submit"></form>');
398+
399+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
400+
var node = $window.document.body.appendChild(form[0]);
401+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
402+
type: 'keypress',
403+
keyCode: $mdConstant.KEY_CODE.ENTER
404+
});
405+
pageScope.$apply();
406+
expect(submitted).toBe(true);
407+
expect(pageScope.form.$submitted).toBe(true);
408+
$window.document.body.removeChild(node);
409+
});
410+
411+
it('should not submit a parent form when ENTER is pressed and the submit components are disabled',
412+
function() {
413+
var submitted = false;
414+
pageScope.onSubmit = function() {
415+
submitted = true;
416+
};
417+
var form = compileAndLink(
418+
'<form name="form"><md-checkbox ng-checked="value"></md-checkbox>' +
419+
'<input type="submit" ng-click="onSubmit()" value="Submit" disabled>' +
420+
'<button type="submit" ng-click="onSubmit()" disabled>Submit</button></form>');
421+
422+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
423+
var node = $window.document.body.appendChild(form[0]);
424+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
425+
type: 'keypress',
426+
keyCode: $mdConstant.KEY_CODE.ENTER
427+
});
428+
pageScope.$apply();
429+
expect(submitted).toBe(false);
430+
expect(pageScope.form.$submitted).toBe(false);
431+
$window.document.body.removeChild(node);
432+
});
433+
434+
it('should click the first enabled submit input when there are multiple',
435+
function() {
436+
var submitted = false, submitted2 = false;
437+
pageScope.onSubmit = function() {
438+
submitted = true;
439+
};
440+
pageScope.onSubmit2 = function() {
441+
submitted2 = true;
442+
};
443+
var form = compileAndLink(
444+
'<form name="form"><md-checkbox ng-checked="value">' +
445+
'</md-checkbox><input type="submit" value="Submit" ng-click="onSubmit()">' +
446+
'<input type="submit" value="Second Submit" ng-click="onSubmit2()"></form>');
447+
448+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
449+
var node = $window.document.body.appendChild(form[0]);
450+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
451+
type: 'keypress',
452+
keyCode: $mdConstant.KEY_CODE.ENTER
453+
});
454+
pageScope.$apply();
455+
expect(submitted).toBe(true);
456+
expect(submitted2).toBe(false);
457+
expect(pageScope.form.$submitted).toBe(true);
458+
$window.document.body.removeChild(node);
459+
});
460+
461+
it('should click the first enabled submit button when there are multiple',
462+
function() {
463+
var submitted = false, submitted2 = false;
464+
pageScope.onSubmit = function() {
465+
submitted = true;
466+
};
467+
pageScope.onSubmit2 = function() {
468+
submitted2 = true;
469+
};
470+
var form = compileAndLink(
471+
'<form name="form"><md-checkbox ng-checked="value">' +
472+
'</md-checkbox><button type="submit" ng-click="onSubmit()">Submit</button>' +
473+
'<button type="submit" ng-click="onSubmit2()">Second Submit</button></form>');
474+
475+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
476+
var node = $window.document.body.appendChild(form[0]);
477+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
478+
type: 'keypress',
479+
keyCode: $mdConstant.KEY_CODE.ENTER
480+
});
481+
pageScope.$apply();
482+
expect(submitted).toBe(true);
483+
expect(submitted2).toBe(false);
484+
expect(pageScope.form.$submitted).toBe(true);
485+
$window.document.body.removeChild(node);
486+
});
487+
488+
it('should click the first submit button when there is a submit input after it',
489+
function() {
490+
var submitted = false, submitted2 = false;
491+
pageScope.onSubmit = function() {
492+
submitted = true;
493+
};
494+
pageScope.onSubmit2 = function() {
495+
submitted2 = true;
496+
};
497+
var form = compileAndLink(
498+
'<form name="form"><md-checkbox ng-checked="value">' +
499+
'</md-checkbox><button type="submit" ng-click="onSubmit()">Submit</button>' +
500+
'<input type="submit" value="Second Submit" ng-click="onSubmit2()"></form>');
501+
502+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
503+
var node = $window.document.body.appendChild(form[0]);
504+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
505+
type: 'keypress',
506+
keyCode: $mdConstant.KEY_CODE.ENTER
507+
});
508+
pageScope.$apply();
509+
expect(submitted).toBe(true);
510+
expect(submitted2).toBe(false);
511+
expect(pageScope.form.$submitted).toBe(true);
512+
$window.document.body.removeChild(node);
513+
});
514+
515+
it('should click the first submit input when there is a submit button after it',
516+
function() {
517+
var submitted = false, submitted2 = false;
518+
pageScope.onSubmit = function() {
519+
submitted = true;
520+
};
521+
pageScope.onSubmit2 = function() {
522+
submitted2 = true;
523+
};
524+
var form = compileAndLink(
525+
'<form name="form"><md-checkbox ng-checked="value">' +
526+
'</md-checkbox><input type="submit" value="Submit" ng-click="onSubmit()">' +
527+
'<button type="submit" ng-click="onSubmit2()">Second Submit</button></form>');
528+
529+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
530+
var node = $window.document.body.appendChild(form[0]);
531+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
532+
type: 'keypress',
533+
keyCode: $mdConstant.KEY_CODE.ENTER
534+
});
535+
pageScope.$apply();
536+
expect(submitted).toBe(true);
537+
expect(submitted2).toBe(false);
538+
expect(pageScope.form.$submitted).toBe(true);
539+
$window.document.body.removeChild(node);
540+
});
541+
542+
it('should click the submit button when the first submit input is disabled',
543+
function() {
544+
var submitted = false, submitted2 = false;
545+
pageScope.onSubmit = function() {
546+
submitted = true;
547+
};
548+
pageScope.onSubmit2 = function() {
549+
submitted2 = true;
550+
};
551+
var form = compileAndLink(
552+
'<form name="form"><md-checkbox ng-checked="value">' +
553+
'</md-checkbox><input type="submit" value="Submit" ng-click="onSubmit()" disabled>' +
554+
'<button type="submit" ng-click="onSubmit2()">Second Submit</button></form>');
555+
556+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
557+
var node = $window.document.body.appendChild(form[0]);
558+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
559+
type: 'keypress',
560+
keyCode: $mdConstant.KEY_CODE.ENTER
561+
});
562+
pageScope.$apply();
563+
expect(submitted).toBe(false);
564+
expect(submitted2).toBe(true);
565+
expect(pageScope.form.$submitted).toBe(true);
566+
$window.document.body.removeChild(node);
567+
});
568+
569+
it('should click the submit input when the first submit button is disabled',
570+
function() {
571+
var submitted = false, submitted2 = false;
572+
pageScope.onSubmit = function() {
573+
submitted = true;
574+
};
575+
pageScope.onSubmit2 = function() {
576+
submitted2 = true;
577+
};
578+
var form = compileAndLink(
579+
'<form name="form"><md-checkbox ng-checked="value">' +
580+
'</md-checkbox><button type="submit" ng-click="onSubmit()" disabled>Submit</button>' +
581+
'<input type="submit" value="Second Submit" ng-click="onSubmit2()"></form>');
582+
583+
// We need to add the form to the DOM in order for `submit` events to be properly fired.
584+
var node = $window.document.body.appendChild(form[0]);
585+
angular.element(form[0].getElementsByTagName('md-checkbox')[0]).triggerHandler({
586+
type: 'keypress',
587+
keyCode: $mdConstant.KEY_CODE.ENTER
588+
});
589+
pageScope.$apply();
590+
expect(submitted).toBe(false);
591+
expect(submitted2).toBe(true);
592+
expect(pageScope.form.$submitted).toBe(true);
593+
$window.document.body.removeChild(node);
306594
});
307-
pageScope.$apply();
308-
expect(form[0].classList.contains('ng-submitted')).toBe(true);
309-
});
310595

311596
it('should mark the checkbox as selected on load with ng-checked', function() {
312597
pageScope.isChecked = function() { return true; };

src/core/util/util.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
537537
* @param {string|function} validateWith If a string is passed, it will be evaluated against
538538
* each of the parent nodes' tag name. If a function is passed, the loop will call it with each
539539
* of the parents and will use the return value to determine whether the node is a match.
540-
* @param {boolean} onlyParent Only start checking from the parent element, not `el`.
540+
* @param {boolean=} onlyParent Only start checking from the parent element, not `el`.
541+
* @returns {Node|null} closest matching parent Node or null if not found
541542
*/
542543
getClosest: function getClosest(el, validateWith, onlyParent) {
543544
if (angular.isString(validateWith)) {

0 commit comments

Comments
 (0)