From 794a275606eb38e8f572b135b04184fe517a3c61 Mon Sep 17 00:00:00 2001 From: Seth Silesky <5115498+silesky@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:34:21 -0600 Subject: [PATCH 1/4] wip --- .../dom-gen/__tests__/change-gen.test.ts | 71 +++++++++++++++++++ .../signal-generators/dom-gen/change-gen.ts | 5 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts diff --git a/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts new file mode 100644 index 000000000..2774a53db --- /dev/null +++ b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts @@ -0,0 +1,71 @@ +import { sleep } from '@segment/analytics-core' +import { createInteractionSignal } from '../../../../types/factories' +import { SignalEmitter } from '../../../emitter' +import { OnChangeGenerator } from '../change-gen' + +describe('OnChangeGenerator', () => { + let onChangeGenerator: OnChangeGenerator + let emitter: SignalEmitter + let unregister: () => void + beforeEach(() => { + onChangeGenerator = new OnChangeGenerator() + emitter = new SignalEmitter() + }) + + afterEach(() => { + unregister() + }) + + it('should emit a signal on change event', async () => { + const emitSpy = jest.spyOn(emitter, 'emit') + const target = document.createElement('input') + target.type = 'text' + target.value = 'new value' + + const event = new Event('change', { bubbles: true }) + Object.defineProperty(event, 'target', { value: target }) + + unregister = onChangeGenerator.register(emitter) + document.dispatchEvent(event) + + await sleep(100) + + expect(emitSpy).toHaveBeenCalledWith( + createInteractionSignal({ + eventType: 'change', + listener: 'onchange', + target: expect.any(Object), + change: { value: 'new value' }, + }) + ) + }) + + it('should not emit a signal for ignored elements', () => { + const emitSpy = jest.spyOn(emitter, 'emit') + const target = document.createElement('input') + target.type = 'password' + + const event = new Event('change', { bubbles: true }) + Object.defineProperty(event, 'target', { value: target }) + + unregister = onChangeGenerator.register(emitter) + document.dispatchEvent(event) + + expect(emitSpy).not.toHaveBeenCalled() + }) + + it('should not emit a signal for elements handled by mutation observer', () => { + const emitSpy = jest.spyOn(emitter, 'emit') + const target = document.createElement('input') + target.type = 'text' + target.setAttribute('value', 'initial value') + + const event = new Event('change', { bubbles: true }) + Object.defineProperty(event, 'target', { value: target }) + + unregister = onChangeGenerator.register(emitter) + document.dispatchEvent(event) + + expect(emitSpy).not.toHaveBeenCalled() + }) +}) diff --git a/packages/signals/signals/src/core/signal-generators/dom-gen/change-gen.ts b/packages/signals/signals/src/core/signal-generators/dom-gen/change-gen.ts index 7a9c2b7f5..73e372cfc 100644 --- a/packages/signals/signals/src/core/signal-generators/dom-gen/change-gen.ts +++ b/packages/signals/signals/src/core/signal-generators/dom-gen/change-gen.ts @@ -67,7 +67,10 @@ export class OnChangeGenerator implements SignalGenerator { const parseChange = (target: HTMLElement): ChangedEvent | undefined => { if (target instanceof HTMLSelectElement) { return { - selectedOptions: Array.from(target.selectedOptions), + selectedOptions: Array.from(target.selectedOptions).map((option) => ({ + value: option.value, + label: option.label, + })), } } if (target instanceof HTMLTextAreaElement) { From 282258432dbbaa008917f298713db44c12153644 Mon Sep 17 00:00:00 2001 From: Seth Silesky <5115498+silesky@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:35:43 -0600 Subject: [PATCH 2/4] update changeset --- .changeset/friendly-cycles-flow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/friendly-cycles-flow.md diff --git a/.changeset/friendly-cycles-flow.md b/.changeset/friendly-cycles-flow.md new file mode 100644 index 000000000..8b3f4795a --- /dev/null +++ b/.changeset/friendly-cycles-flow.md @@ -0,0 +1,5 @@ +--- +'@segment/analytics-signals': patch +--- + +Fix bug where in vanilla React environments, the onChange events would error due to circular references. From ada93ac31710c76904fe75a2967f8e863d28d588 Mon Sep 17 00:00:00 2001 From: Seth Silesky <5115498+silesky@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:44:32 -0600 Subject: [PATCH 3/4] wip --- .../signal-generators/dom-gen/__tests__/change-gen.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts index 2774a53db..f25d34dfd 100644 --- a/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts +++ b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts @@ -1,4 +1,3 @@ -import { sleep } from '@segment/analytics-core' import { createInteractionSignal } from '../../../../types/factories' import { SignalEmitter } from '../../../emitter' import { OnChangeGenerator } from '../change-gen' @@ -28,8 +27,6 @@ describe('OnChangeGenerator', () => { unregister = onChangeGenerator.register(emitter) document.dispatchEvent(event) - await sleep(100) - expect(emitSpy).toHaveBeenCalledWith( createInteractionSignal({ eventType: 'change', From d68380f6ff8df6fb6b1935f41d302a4be339e093 Mon Sep 17 00:00:00 2001 From: Seth Silesky <5115498+silesky@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:04:51 -0600 Subject: [PATCH 4/4] wip --- .../dom-gen/__tests__/change-gen.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts index f25d34dfd..3347eb6e7 100644 --- a/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts +++ b/packages/signals/signals/src/core/signal-generators/dom-gen/__tests__/change-gen.test.ts @@ -65,4 +65,34 @@ describe('OnChangeGenerator', () => { expect(emitSpy).not.toHaveBeenCalled() }) + + it('should emit a signal with selectedOptions for select elements', () => { + const emitSpy = jest.spyOn(emitter, 'emit') + const target = document.createElement('select') + const option1 = document.createElement('option') + option1.value = 'value1' + option1.label = 'label1' + option1.selected = true + const option2 = document.createElement('option') + option2.value = 'value2' + option2.label = 'label2' + target.append(option1, option2) + + const event = new Event('change', { bubbles: true }) + Object.defineProperty(event, 'target', { value: target }) + + unregister = onChangeGenerator.register(emitter) + document.dispatchEvent(event) + + expect(emitSpy).toHaveBeenCalledWith( + createInteractionSignal({ + eventType: 'change', + listener: 'onchange', + target: expect.any(Object), + change: { + selectedOptions: [{ value: 'value1', label: 'label1' }], + }, + }) + ) + }) })