Skip to content
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
27 changes: 27 additions & 0 deletions src/pat/select2/select2.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from "jquery";
import Base from "@patternslib/patternslib/src/core/base";
import events from "@patternslib/patternslib/src/core/events";
import I18n from "../../core/i18n";
import utils from "../../core/utils";

Expand Down Expand Up @@ -149,6 +150,32 @@ export default Base.extend({
}

this.$el.select2(this.options);

// Select2 v3 signals changes via a jQuery `change` event, which isn't
// caught by native JavaScript event listeners.
// Let's re-trigger as native `input`- and `change`-events, so that
// those can pick it up. A native `<select>` fires both input and
// change when its value is committed, so we mirror both.
let dispatching = false;
this.$el.on("change", () => {
// The `dispatching` guard prevents infinite recursion: jQuery's
// `change` handler also catches the native `change` event we
// dispatch here.
if (dispatching) {
return;
}
dispatching = true;
this.el.dispatchEvent(events.input_event());
this.el.dispatchEvent(events.change_event());
dispatching = false;
});

// Select2 v3 signals loss of focus via a jQuery `select2-blur` event,
// which isn't caught by native JavaScript event listeners.
// Let's re-trigger as a native `blur` event so that native listeners can
// pick it up.
this.$el.on("select2-blur", () => this.el.dispatchEvent(events.blur_event()));

this.$el.on("select2-selected", (e) => callback(this.options.onSelected, e));
this.$el.on("select2-selecting", (e) => callback(this.options.onSelecting, e));
this.$el.on("select2-deselecting", (e) =>
Expand Down
54 changes: 54 additions & 0 deletions src/pat/select2/select2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,60 @@ describe("Select2", function () {
expect($noResults.length).toEqual(0);
});

it("re-emits the jQuery change event as native input and change events", async function () {
// Select2 v3 signals changes via a jQuery `change` event, which native
// event listeners (e.g. in pat-validation) don't receive. pat-select2
// bridges this by re-dispatching native `input` and `change` events,
// mirroring how a real `<select>` behaves on commit.
document.body.innerHTML = `
<input class="pat-select2"
data-pat-select2="tags: Red,Yellow,Blue"
value="Yellow" />
`;
registry.scan(document.body);
await utils.timeout(1);

const el = document.querySelector("input.pat-select2");

let native_input_events = 0;
let native_change_events = 0;
el.addEventListener("input", () => native_input_events++);
el.addEventListener("change", () => native_change_events++);

// A native listener does not receive jQuery-triggered events ...
$(el).trigger("change");

// ... but pat-select2 re-dispatches it as native input and change
// events. The re-dispatched native change event must not re-trigger
// the jQuery change handler (no infinite recursion), so each fires
// exactly once.
expect(native_input_events).toEqual(1);
expect(native_change_events).toEqual(1);
});

it("re-emits a native blur event when nothing is selected", async function () {
// Select2 v3 signals loss of focus via a jQuery `select2-blur` event,
// which native event listeners (e.g. in pat-validation) don't receive.
// When the widget is left empty, pat-select2 bridges this by
// re-dispatching a native `blur` event, so validation can kick in.
document.body.innerHTML = `
<input class="pat-select2"
data-pat-select2="tags: Red,Yellow,Blue" />
`;
registry.scan(document.body);
await utils.timeout(1);

const el = document.querySelector("input.pat-select2");

let native_blur_events = 0;
el.addEventListener("blur", () => native_blur_events++);

// A native listener does not receive jQuery-triggered events ...
$(el).trigger("select2-blur");
// ... but pat-select2 re-dispatches it as a native blur event.
expect(native_blur_events).toEqual(1);
});

it("HTML multiple select widget converted to hidden inuput, before applying select2", async function () {
document.body.innerHTML = `
<div>
Expand Down
Loading