fix: rename and move internal/controller/events

Split `internal/controller/events` into `internal/events/redispatch-event` and `internal/events/form-label-activation`

PiperOrigin-RevId: 592350371
This commit is contained in:
Elizabeth Mitchell 2023-12-19 14:48:55 -08:00 committed by Copybara-Service
parent 57168f6a95
commit eca1357f1a
13 changed files with 147 additions and 116 deletions

View File

@ -20,7 +20,7 @@ import {
import {
dispatchActivationClick,
isActivationClick,
} from '../../internal/events/events.js';
} from '../../internal/events/form-label-activation.js';
import {
internals,
mixinElementInternals,

View File

@ -16,8 +16,8 @@ import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {
dispatchActivationClick,
isActivationClick,
redispatchEvent,
} from '../../internal/events/events.js';
} from '../../internal/events/form-label-activation.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {
createValidator,
getValidityAnchor,

View File

@ -10,7 +10,7 @@ import {html, nothing} from 'lit';
import {property, query} from 'lit/decorators.js';
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
import {redispatchEvent} from '../../internal/events/events.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {MultiActionChip} from './multi-action-chip.js';
import {renderRemoveButton} from './trailing-icons.js';

View File

@ -12,7 +12,7 @@ import {classMap} from 'lit/directives/class-map.js';
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {redispatchEvent} from '../../internal/events/events.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {
DIALOG_DEFAULT_CLOSE_ANIMATION,

View File

@ -4,43 +4,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Re-dispatches an event from the provided element.
*
* This function is useful for forwarding non-composed events, such as `change`
* events.
*
* @example
* class MyInput extends LitElement {
* render() {
* return html`<input @change=${this.redispatchEvent}>`;
* }
*
* protected redispatchEvent(event: Event) {
* redispatchEvent(this, event);
* }
* }
*
* @param element The element to dispatch the event from.
* @param event The event to re-dispatch.
* @return Whether or not the event was dispatched (if cancelable).
*/
export function redispatchEvent(element: Element, event: Event) {
// For bubbling events in SSR light DOM (or composed), stop their propagation
// and dispatch the copy.
if (event.bubbles && (!element.shadowRoot || event.composed)) {
event.stopPropagation();
}
const copy = Reflect.construct(event.constructor, [event.type, event]);
const dispatched = element.dispatchEvent(copy);
if (!dispatched) {
event.preventDefault();
}
return dispatched;
}
/**
* Dispatches a click event to the given element that triggers a native action,
* but is not composed and therefore is not seen outside the element.

View File

@ -0,0 +1,92 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// import 'jasmine'; (google3-only)
import {
dispatchActivationClick,
isActivationClick,
} from './form-label-activation.js';
describe('label events', () => {
let instance: HTMLDivElement;
beforeEach(() => {
instance = document.createElement('div');
instance
.attachShadow({mode: 'open'})
.append(document.createElement('slot'));
// To have event.target set correctly, the EventTarget instance must be
// attached to the DOM.
document.body.appendChild(instance);
});
afterEach(() => {
document.body.removeChild(instance);
});
describe('isActivationClick()', () => {
it('returns true for click on listener', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
instance.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(true);
});
it('returns false for click on element listener shadowRoot', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
innerEl.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(false);
});
it('returns false for click on element listener child', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
const slottedEl = document.createElement('div');
instance.append(slottedEl);
slottedEl.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(false);
});
});
describe('dispatchActivationClick()', () => {
it('dispatches an event', () => {
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
const listener = jasmine.createSpy('listener');
innerEl.addEventListener('click', listener);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(1);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(2);
});
it('dispatches an event that cannot be heard outside dispatching scope', () => {
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
const listener = jasmine.createSpy('listener');
instance.addEventListener('click', listener);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(0);
});
});
});

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Re-dispatches an event from the provided element.
*
* This function is useful for forwarding non-composed events, such as `change`
* events.
*
* @example
* class MyInput extends LitElement {
* render() {
* return html`<input @change=${this.redispatchEvent}>`;
* }
*
* protected redispatchEvent(event: Event) {
* redispatchEvent(this, event);
* }
* }
*
* @param element The element to dispatch the event from.
* @param event The event to re-dispatch.
* @return Whether or not the event was dispatched (if cancelable).
*/
export function redispatchEvent(element: Element, event: Event) {
// For bubbling events in SSR light DOM (or composed), stop their propagation
// and dispatch the copy.
if (event.bubbles && (!element.shadowRoot || event.composed)) {
event.stopPropagation();
}
const copy = Reflect.construct(event.constructor, [event.type, event]);
const dispatched = element.dispatchEvent(copy);
if (!dispatched) {
event.preventDefault();
}
return dispatched;
}

View File

@ -6,11 +6,7 @@
// import 'jasmine'; (google3-only)
import {
dispatchActivationClick,
isActivationClick,
redispatchEvent,
} from './events.js';
import {redispatchEvent} from './redispatch-event.js';
describe('events', () => {
let instance: HTMLDivElement;
@ -119,66 +115,4 @@ describe('events', () => {
.toBe('bar');
});
});
describe('isActivationClick()', () => {
it('returns true for click on listener', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
instance.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(true);
});
it('returns false for click on element listener shadowRoot', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
innerEl.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(false);
});
it('returns false for click on element listener child', () => {
const listener = jasmine.createSpy('listener', isActivationClick);
listener.and.callThrough();
instance.addEventListener('click', listener);
const slottedEl = document.createElement('div');
instance.append(slottedEl);
slottedEl.dispatchEvent(
new MouseEvent('click', {bubbles: true, composed: true}),
);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener.calls.mostRecent().returnValue).toBe(false);
});
});
describe('dispatchActivationClick()', () => {
it('dispatches an event', () => {
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
const listener = jasmine.createSpy('listener');
innerEl.addEventListener('click', listener);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(1);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(2);
});
it('dispatches an event that cannot be heard outside dispatching scope', () => {
const innerEl = document.createElement('div');
instance.shadowRoot!.append(innerEl);
const listener = jasmine.createSpy('listener');
instance.addEventListener('click', listener);
dispatchActivationClick(innerEl);
expect(listener).toHaveBeenCalledTimes(0);
});
});
});

View File

@ -11,7 +11,7 @@ import {html, isServer, LitElement} from 'lit';
import {property, query} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import {isActivationClick} from '../../internal/events/events.js';
import {isActivationClick} from '../../internal/events/form-label-activation.js';
import {
createValidator,
getValidityAnchor,

View File

@ -15,7 +15,7 @@ import {html as staticHtml, StaticValue} from 'lit/static-html.js';
import {Field} from '../../field/internal/field.js';
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {redispatchEvent} from '../../internal/events/events.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {
createValidator,
getValidityAnchor,

View File

@ -19,8 +19,8 @@ import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {
dispatchActivationClick,
isActivationClick,
redispatchEvent,
} from '../../internal/events/events.js';
} from '../../internal/events/form-label-activation.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {mixinElementInternals} from '../../labs/behaviors/element-internals.js';
import {
getFormValue,

View File

@ -15,8 +15,8 @@ import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {
dispatchActivationClick,
isActivationClick,
redispatchEvent,
} from '../../internal/events/events.js';
} from '../../internal/events/form-label-activation.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {
createValidator,
getValidityAnchor,

View File

@ -15,7 +15,7 @@ import {Field} from '../../field/internal/field.js';
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
import {stringConverter} from '../../internal/controller/string-converter.js';
import {redispatchEvent} from '../../internal/events/events.js';
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
import {
createValidator,
getValidityAnchor,