mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(keycodes): add utilities for checking modifier keys (#13933)
Based off of the conversation in #13790, these changes add some utilities for dealing with modifier keys on keyboard events.
This commit is contained in:
committed by
vivian-hu
parent
31bffb38fe
commit
78998638d3
@@ -1,10 +1,26 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
load("//tools:defaults.bzl", "ts_library", "ts_web_test_suite")
|
||||
|
||||
ts_library(
|
||||
name = "keycodes",
|
||||
module_name = "@angular/cdk/keycodes",
|
||||
srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]),
|
||||
module_name = "@angular/cdk/keycodes",
|
||||
deps = ["@matdeps//tslib"],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "keycodes_test_sources",
|
||||
srcs = glob(["**/*.spec.ts"]),
|
||||
deps = [
|
||||
"@matdeps//@types/jasmine",
|
||||
"//src/cdk/testing",
|
||||
":keycodes"
|
||||
],
|
||||
testonly = 1,
|
||||
)
|
||||
|
||||
ts_web_test_suite(
|
||||
name = "unit_tests",
|
||||
deps = [":keycodes_test_sources"],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import {createKeyboardEvent} from '@angular/cdk/testing';
|
||||
import {hasModifierKey} from './modifiers';
|
||||
|
||||
describe('keyboard modifiers', () => {
|
||||
it('should check whether the alt key is pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
|
||||
expect(hasModifierKey(event)).toBe(false);
|
||||
Object.defineProperty(event, 'altKey', {get: () => true});
|
||||
expect(hasModifierKey(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check whether the shift key is pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
|
||||
expect(hasModifierKey(event)).toBe(false);
|
||||
Object.defineProperty(event, 'shiftKey', {get: () => true});
|
||||
expect(hasModifierKey(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check whether the meta key is pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
|
||||
expect(hasModifierKey(event)).toBe(false);
|
||||
Object.defineProperty(event, 'metaKey', {get: () => true});
|
||||
expect(hasModifierKey(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check whether the ctrl key is pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
|
||||
expect(hasModifierKey(event)).toBe(false);
|
||||
Object.defineProperty(event, 'ctrlKey', {get: () => true});
|
||||
expect(hasModifierKey(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check if a particular modifier key is pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
Object.defineProperty(event, 'ctrlKey', {get: () => true});
|
||||
|
||||
expect(hasModifierKey(event, 'altKey')).toBe(false);
|
||||
Object.defineProperty(event, 'altKey', {get: () => true});
|
||||
expect(hasModifierKey(event, 'altKey')).toBe(true);
|
||||
});
|
||||
|
||||
it('should check if multiple specific modifier keys are pressed', () => {
|
||||
const event = createKeyboardEvent('keydown', 0);
|
||||
Object.defineProperty(event, 'ctrlKey', {get: () => true});
|
||||
|
||||
expect(hasModifierKey(event, 'altKey', 'shiftKey')).toBe(false);
|
||||
Object.defineProperty(event, 'altKey', {get: () => true});
|
||||
Object.defineProperty(event, 'shiftKey', {get: () => true});
|
||||
expect(hasModifierKey(event, 'altKey', 'shiftKey')).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
type ModifierKey = 'altKey' | 'shiftKey' | 'ctrlKey' | 'metaKey';
|
||||
|
||||
/**
|
||||
* Checks whether a modifier key is pressed.
|
||||
* @param event Event to be checked.
|
||||
*/
|
||||
export function hasModifierKey(event: KeyboardEvent, ...modifiers: ModifierKey[]): boolean {
|
||||
if (modifiers.length) {
|
||||
return modifiers.some(modifier => event[modifier]);
|
||||
}
|
||||
|
||||
return event.altKey || event.shiftKey || event.ctrlKey || event.metaKey;
|
||||
}
|
||||
@@ -7,3 +7,4 @@
|
||||
*/
|
||||
|
||||
export * from './keycodes';
|
||||
export * from './modifiers';
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import {FocusableOption, FocusKeyManager} from '@angular/cdk/a11y';
|
||||
import {Direction, Directionality} from '@angular/cdk/bidi';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {END, ENTER, HOME, SPACE} from '@angular/cdk/keycodes';
|
||||
import {END, ENTER, HOME, SPACE, hasModifierKey} from '@angular/cdk/keycodes';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
@@ -442,9 +442,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
_onKeydown(event: KeyboardEvent) {
|
||||
// TODO(crisbeto): move into a CDK utility once
|
||||
// the similar PRs for other components are merged in.
|
||||
const hasModifier = event.altKey || event.shiftKey || event.ctrlKey || event.metaKey;
|
||||
const hasModifier = hasModifierKey(event);
|
||||
const keyCode = event.keyCode;
|
||||
const manager = this._keyManager;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {FocusMonitor, FocusableOption, FocusOrigin} from '@angular/cdk/a11y';
|
||||
import {ENTER, SPACE} from '@angular/cdk/keycodes';
|
||||
import {ENTER, SPACE, hasModifierKey} from '@angular/cdk/keycodes';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
@@ -140,7 +140,7 @@ export class MatExpansionPanelHeader implements OnDestroy, FocusableOption {
|
||||
// Toggle for space and enter keys.
|
||||
case SPACE:
|
||||
case ENTER:
|
||||
if (!event.altKey && !event.metaKey && !event.shiftKey && !event.ctrlKey) {
|
||||
if (!hasModifierKey(event)) {
|
||||
event.preventDefault();
|
||||
this._toggle();
|
||||
}
|
||||
|
||||
+9
-4
@@ -75,7 +75,14 @@ def ng_test_library(deps = [], tsconfig = None, **kwargs):
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def ng_web_test_suite(deps = [], srcs = [], static_css = [], bootstrap = [], **kwargs):
|
||||
def ts_web_test_suite(srcs = [], **kwargs):
|
||||
_ts_web_test_suite(
|
||||
# Required for running the compiled ng modules that use TypeScript import helpers.
|
||||
srcs = ["@matdeps//node_modules/tslib:tslib.js"] + srcs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def ng_web_test_suite(deps = [], static_css = [], bootstrap = [], **kwargs):
|
||||
# Always include a prebuilt theme in the test suite because otherwise tests, which depend on CSS
|
||||
# that is needed for measuring, will unexpectedly fail. Also always adding a prebuilt theme
|
||||
# reduces the amount of setup that is needed to create a test suite Bazel target. Note that the
|
||||
@@ -109,9 +116,7 @@ def ng_web_test_suite(deps = [], srcs = [], static_css = [], bootstrap = [], **k
|
||||
""" % css_label
|
||||
)
|
||||
|
||||
_ts_web_test_suite(
|
||||
# Required for running the compiled ng modules that use TypeScript import helpers.
|
||||
srcs = ["@matdeps//node_modules/tslib:tslib.js"] + srcs,
|
||||
ts_web_test_suite(
|
||||
# Depend on our custom test initialization script. This needs to be the first dependency.
|
||||
deps = ["//test:angular_test_init"] + deps,
|
||||
bootstrap = [
|
||||
|
||||
Reference in New Issue
Block a user