Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate accessing jQuery.Event#originalEvent #16690

Merged
merged 1 commit into from
May 26, 2018
Merged
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
161 changes: 161 additions & 0 deletions packages/ember-glimmer/tests/integration/event-dispatcher-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
reset as instrumentationReset,
} from '@ember/instrumentation';
import { EMBER_IMPROVED_INSTRUMENTATION } from '@ember/canary-features';
import { jQueryDisabled, jQuery } from 'ember-views';
import { ENV } from 'ember-environment';
import { HAS_NATIVE_PROXY } from 'ember-utils';
import { DEBUG } from '@glimmer/env';

let canDataTransfer = !!document.createEvent('HTMLEvents').dataTransfer;

Expand Down Expand Up @@ -300,3 +304,160 @@ if (canDataTransfer) {
}
);
}

if (jQueryDisabled) {
moduleFor(
'EventDispatcher#native-events',
class extends RenderingTest {
['@test native events are passed when jQuery is not present'](assert) {
let receivedEvent;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
assert.ok(receivedEvent, 'click event was triggered');
assert.notOk(receivedEvent.originalEvent, 'event is not a jQuery.Event');
}
}
);
} else {
moduleFor(
'EventDispatcher#jquery-events',
class extends RenderingTest {
beforeEach() {
this.jqueryIntegration = ENV._JQUERY_INTEGRATION;
}

afterEach() {
ENV._JQUERY_INTEGRATION = this.jqueryIntegration;
}

['@test jQuery events are passed when jQuery is present'](assert) {
let receivedEvent;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
assert.ok(receivedEvent, 'click event was triggered');
assert.ok(receivedEvent instanceof jQuery.Event, 'event is a jQuery.Event');
}

[`@${HAS_NATIVE_PROXY ? 'test' : 'skip'} accessing jQuery.Event#originalEvent is deprecated`](
assert
) {
let receivedEvent;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
expectDeprecation(() => {
let { originalEvent } = receivedEvent;
assert.ok(originalEvent, 'jQuery event has originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of originalEvent are available');
}, 'Accessing jQuery.Event specific properties is deprecated. Either use the ember-jquery-legacy addon to normalize events to native events, or explicitly opt into jQuery integration using @ember/optional-features.');
}

['@test other jQuery.Event properties do not trigger deprecation'](assert) {
let receivedEvent;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
expectNoDeprecation(() => {
receivedEvent.stopPropagation();
receivedEvent.stopImmediatePropagation();
receivedEvent.preventDefault();
assert.ok(receivedEvent.bubbles, 'properties of jQuery event are available');
assert.equal(receivedEvent.type, 'click', 'properties of jQuery event are available');
});
}

['@test accessing jQuery.Event#originalEvent does not trigger deprecations when jquery integration is explicitly enabled'](
assert
) {
let receivedEvent;
ENV._JQUERY_INTEGRATION = true;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
expectNoDeprecation(() => {
let { originalEvent } = receivedEvent;
assert.ok(originalEvent, 'jQuery event has originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of originalEvent are available');
});
}

[`@${
HAS_NATIVE_PROXY && DEBUG ? 'test' : 'skip'
} accessing jQuery.Event#__originalEvent does not trigger deprecations to support ember-jquery-legacy`](
assert
) {
let receivedEvent;

this.registerComponent('x-foo', {
ComponentClass: Component.extend({
click(event) {
receivedEvent = event;
},
}),
template: `<button id="foo">bar</button>`,
});

this.render(`{{x-foo}}`);

this.runTask(() => this.$('#foo').click());
expectNoDeprecation(() => {
let { __originalEvent: originalEvent } = receivedEvent;
assert.ok(originalEvent, 'jQuery event has __originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of __originalEvent are available');
});
}
}
);
}
5 changes: 4 additions & 1 deletion packages/ember-views/lib/system/event_dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Object as EmberObject } from 'ember-runtime';
import jQuery from './jquery';
import ActionManager from './action_manager';
import fallbackViewRegistry from '../compat/fallback-view-registry';
import addJQueryEventDeprecation from './jquery_event_deprecation';

const HAS_JQUERY = jQuery !== undefined;
const ROOT_ELEMENT_CLASS = 'ember-application';
Expand Down Expand Up @@ -244,7 +245,7 @@ export default EmberObject.extend({
let result = true;

if (view) {
result = view.handleEvent(eventName, evt);
result = view.handleEvent(eventName, addJQueryEventDeprecation(evt));
}

return result;
Expand All @@ -254,6 +255,8 @@ export default EmberObject.extend({
let attributes = evt.currentTarget.attributes;
let handledActions = [];

evt = addJQueryEventDeprecation(evt);

for (let i = 0; i < attributes.length; i++) {
let attr = attributes.item(i);
let attrName = attr.name;
Expand Down
51 changes: 51 additions & 0 deletions packages/ember-views/lib/system/jquery_event_deprecation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* global Proxy */
import { deprecate } from '@ember/debug';
import { ENV } from 'ember-environment';
import { HAS_NATIVE_PROXY } from 'ember-utils';
import { DEBUG } from '@glimmer/env';

export default function addJQueryEventDeprecation(jqEvent) {
if (!DEBUG || !HAS_NATIVE_PROXY) {
return jqEvent;
}

let boundFunctions = new Map();

// wrap the jQuery event in a Proxy to add the deprecation message for originalEvent, according to RFC#294
// we need a native Proxy here, so we can make sure that the internal use of originalEvent in jQuery itself does
// not trigger a deprecation
return new Proxy(jqEvent, {
get(target, name) {
switch (name) {
case 'originalEvent':
deprecate(
'Accessing jQuery.Event specific properties is deprecated. Either use the ember-jquery-legacy addon to normalize events to native events, or explicitly opt into jQuery integration using @ember/optional-features.',
ENV._JQUERY_INTEGRATION === true,
{
id: 'ember-views.event-dispatcher.jquery-event',
until: '4.0.0',
}
);
return target[name];

// provide an escape hatch for ember-jquery-legacy to access originalEvent without a deprecation
case '__originalEvent':
return target.originalEvent;

default:
if (typeof target[name] === 'function') {
// cache functions for reuse
if (!boundFunctions.has(name)) {
// for jQuery.Event methods call them with `target` as the `this` context, so they will access
// `originalEvent` from the original jQuery event, not our proxy, thus not trigger the deprecation
boundFunctions.set(name, target[name].bind(target));
}

return boundFunctions.get(name);
}
// same for jQuery's getter functions for simple properties
return target[name];
}
},
});
}