@semantic-ui/query
Advanced tools
Comparing version 0.0.31 to 0.0.32
@@ -26,5 +26,5 @@ { | ||
"dependencies": { | ||
"@semantic-ui/utils": "^0.0.31" | ||
"@semantic-ui/utils": "^0.0.32" | ||
}, | ||
"version": "0.0.31" | ||
"version": "0.0.32" | ||
} |
123
src/query.js
@@ -8,22 +8,23 @@ import { isPlainObject, isString, isArray, isDOM, isFunction, findIndex, inArray, isClient, isObject, each } from '@semantic-ui/utils'; | ||
const tagRegExp = /^<(\w+)(\s*\/)?>$/; | ||
export class Query { | ||
/* | ||
This avoids keeping a copy of window/globalThis in | ||
memory when an element references the global object | ||
reducing memory footprint | ||
*/ | ||
const globalThisProxy = new Proxy({}, { | ||
get(target, prop) { | ||
return globalThis[prop]; | ||
}, | ||
set(target, prop, value) { | ||
globalThis[prop] = value; | ||
return true; | ||
}, | ||
}); | ||
/* | ||
This avoids keeping a copy of window/globalThis in | ||
memory when an element references the global object | ||
reducing memory footprint | ||
*/ | ||
static globalThisProxy = new Proxy({}, { | ||
get(target, prop) { | ||
return globalThis[prop]; | ||
}, | ||
set(target, prop, value) { | ||
globalThis[prop] = value; | ||
return true; | ||
}, | ||
}); | ||
export class Query { | ||
/* | ||
We keep an array of event handlers for teardown | ||
*/ | ||
static eventHandlers = []; | ||
@@ -39,3 +40,3 @@ | ||
// We dont want to store a copy of globalThis in each query instance | ||
elements = [globalThisProxy]; | ||
elements = [Query.globalThisProxy]; | ||
this.isBrowser = isClient; | ||
@@ -50,4 +51,5 @@ this.isGlobal = true; | ||
if (selector.slice(0, 1) == '<') { | ||
const tagName = selector.match(tagRegExp)[1]; | ||
elements = [document.createElement(tagName)]; | ||
const template = document.createElement('template'); | ||
template.innerHTML = selector.trim(); | ||
elements = Array.from(template.content.childNodes); | ||
} else { | ||
@@ -166,3 +168,3 @@ // Use querySelectorAll for normal selectors | ||
removeAllEvents() { | ||
Query._eventHandlers = []; | ||
Query.eventHandlers = []; | ||
} | ||
@@ -364,6 +366,6 @@ | ||
if (!Query._eventHandlers) { | ||
Query._eventHandlers = []; | ||
if (!Query.eventHandlers) { | ||
Query.eventHandlers = []; | ||
} | ||
Query._eventHandlers.push(...eventHandlers); | ||
Query.eventHandlers.push(...eventHandlers); | ||
@@ -402,3 +404,3 @@ if(options?.returnHandler) { | ||
off(event, handler) { | ||
Query._eventHandlers = Query._eventHandlers.filter((eventHandler) => { | ||
Query.eventHandlers = Query.eventHandlers.filter((eventHandler) => { | ||
if ( | ||
@@ -423,2 +425,20 @@ eventHandler.event === event && | ||
trigger(eventType, eventParams) { | ||
return this.each(el => { | ||
if (typeof el.dispatchEvent !== 'function') { | ||
return; | ||
} | ||
const event = new Event(eventType, { bubbles: true, cancelable: true }); | ||
if(eventParams) { | ||
Object.assign(event, eventParams); | ||
} | ||
el.dispatchEvent(event); | ||
}); | ||
} | ||
click(eventParams) { | ||
return this.trigger('click', eventParams); | ||
} | ||
dispatchEvent(eventName, eventData = {}, eventSettings = {}) { | ||
@@ -466,6 +486,5 @@ const eventOptions = { | ||
} | ||
else if (this.length) { | ||
return this.map(el => el.innerHTML || el.nodeValue).join('');; | ||
else if (this.length > 0) { | ||
return this.map(el => el.innerHTML || el.nodeValue).join(''); | ||
} | ||
return this; | ||
} | ||
@@ -640,3 +659,3 @@ | ||
} | ||
return this; | ||
return; | ||
} | ||
@@ -761,12 +780,44 @@ | ||
reverse() { | ||
const els = this.get().reverse(); | ||
return this.chain(els); | ||
} | ||
insertContent(target, content, position) { | ||
const $content = this.chain(content); | ||
$content.each(el => { | ||
target.insertAdjacentElement(position, el); | ||
}); | ||
} | ||
prepend(content) { | ||
return this.each((el) => { | ||
this.insertContent(el, content, 'afterbegin'); | ||
}); | ||
} | ||
append(content) { | ||
return this.each((el) => { | ||
this.insertContent(el, content, 'beforeend'); | ||
}); | ||
} | ||
insertBefore(selector) { | ||
this.chain(selector).each((el) => { | ||
this.insertContent(el, this.selector, 'beforebegin'); | ||
}); | ||
} | ||
insertAfter(selector) { | ||
const targets = this.chain(selector); | ||
const elements = this.get(); | ||
targets.each((target, index) => { | ||
const element = elements[index] || elements[elements.length - 1]; | ||
if (target.parentNode) { | ||
target.parentNode.insertBefore(element, target.nextSibling); | ||
this.chain(selector).each((el) => { | ||
this.insertContent(el, this.selector, 'afterend'); | ||
}); | ||
} | ||
detach() { | ||
return this.each((el) => { | ||
if (el.parentNode) { | ||
el.parentNode.removeChild(el); | ||
} | ||
}); | ||
return this; | ||
} | ||
@@ -773,0 +824,0 @@ |
@@ -134,10 +134,2 @@ import { describe, vi, beforeAll, beforeEach, afterEach, afterAll, it, expect } from 'vitest'; | ||
it('should handle invoking methods on the globalThisProxy object', () => { | ||
const $window = $('window'); | ||
const alertSpy = vi.spyOn(window, 'alert'); | ||
$window.prop('alert')('Test alert'); | ||
expect(alertSpy).toHaveBeenCalledWith('Test alert'); | ||
}); | ||
it('should handle dimension properties correctly for the window object', () => { | ||
@@ -152,3 +144,15 @@ const $window = $('window'); | ||
}); | ||
it('should proxy method calls to the global object', () => { | ||
const alertMock = vi.fn(); | ||
globalThis.alert = alertMock; | ||
$('window').prop('alert')('test'); | ||
expect(alertMock).toHaveBeenCalledWith('test'); | ||
}); | ||
it('should allow setting global properties', () => { | ||
$('window').prop('testProp', 'testValue'); | ||
expect(globalThis.testProp).toBe('testValue'); | ||
}); | ||
}); | ||
}); |
@@ -132,3 +132,38 @@ import { describe, beforeEach, afterEach, expect, it, vi } from 'vitest'; | ||
it('should create an element from an HTML string', () => { | ||
const $div = $('<div>Hello world</div>'); | ||
expect($div.length).toBe(1); | ||
expect($div[0].tagName).toBe('DIV'); | ||
expect($div[0].textContent).toBe('Hello world'); | ||
}); | ||
// Additional test case for HTML parsing with attributes | ||
it('should create an element from an HTML string with attributes', () => { | ||
const $div = $('<div class="test" id="myDiv">Hello world</div>'); | ||
expect($div.length).toBe(1); | ||
expect($div[0].tagName).toBe('DIV'); | ||
expect($div[0].textContent).toBe('Hello world'); | ||
expect($div[0].className).toBe('test'); | ||
expect($div[0].id).toBe('myDiv'); | ||
}); | ||
it('should create multiple elements from an HTML string', () => { | ||
const $elements = $('<p>One</p><p>Two</p>'); | ||
expect($elements.length).toBe(2); | ||
expect($elements[0].tagName).toBe('P'); | ||
expect($elements[0].textContent).toBe('One'); | ||
expect($elements[1].tagName).toBe('P'); | ||
expect($elements[1].textContent).toBe('Two'); | ||
}); | ||
// Additional test case for mixed content | ||
it('should handle mixed content in HTML string', () => { | ||
const $elements = $('<p>Paragraph</p><!-- Comment -->'); | ||
expect($elements.length).toBe(2); | ||
expect($elements[0].nodeType).toBe(Node.ELEMENT_NODE); | ||
expect($elements[0].tagName).toBe('P'); | ||
expect($elements[0].textContent).toBe('Paragraph'); | ||
expect($elements[1].nodeType).toBe(Node.COMMENT_NODE); | ||
}); | ||
}); | ||
@@ -719,2 +754,86 @@ | ||
describe('trigger', () => { | ||
it('should trigger a custom event on elements', () => { | ||
const div = document.createElement('div'); | ||
document.body.appendChild(div); | ||
const callback = vi.fn(); | ||
$('div').on('customEvent', callback); | ||
$('div').trigger('customEvent'); | ||
expect(callback).toHaveBeenCalled(); | ||
}); | ||
it('should trigger a custom event with extra parameters', () => { | ||
const div = document.createElement('div'); | ||
document.body.appendChild(div); | ||
const callback = vi.fn(); | ||
$('div').on('customEvent', callback); | ||
$('div').trigger('customEvent', { detail: 'test' }); | ||
expect(callback).toHaveBeenCalledWith(expect.objectContaining({ | ||
detail: 'test' | ||
})); | ||
}); | ||
it('should trigger events on multiple elements', () => { | ||
const div1 = document.createElement('div'); | ||
const div2 = document.createElement('div'); | ||
document.body.appendChild(div1); | ||
document.body.appendChild(div2); | ||
const callback1 = vi.fn(); | ||
const callback2 = vi.fn(); | ||
$('div').eq(0).on('customEvent', callback1); | ||
$('div').eq(1).on('customEvent', callback2); | ||
$('div').trigger('customEvent'); | ||
expect(callback1).toHaveBeenCalled(); | ||
expect(callback2).toHaveBeenCalled(); | ||
}); | ||
}); | ||
describe('click', () => { | ||
it('should trigger a click event on elements', () => { | ||
const button = document.createElement('button'); | ||
document.body.appendChild(button); | ||
const callback = vi.fn(); | ||
$('button').on('click', callback); | ||
$('button').click(); | ||
expect(callback).toHaveBeenCalled(); | ||
}); | ||
it('should trigger a click event with extra parameters', () => { | ||
const button = document.createElement('button'); | ||
document.body.appendChild(button); | ||
const callback = vi.fn(); | ||
$('button').on('click', callback); | ||
$('button').click({ detail: 'test' }); | ||
expect(callback).toHaveBeenCalledWith(expect.objectContaining({ | ||
detail: 'test' | ||
})); | ||
}); | ||
it('should trigger click events on multiple elements', () => { | ||
const button1 = document.createElement('button'); | ||
const button2 = document.createElement('button'); | ||
document.body.appendChild(button1); | ||
document.body.appendChild(button2); | ||
const callback1 = vi.fn(); | ||
const callback2 = vi.fn(); | ||
$('button').eq(0).on('click', callback1); | ||
$('button').eq(1).on('click', callback2); | ||
$('button').click(); | ||
expect(callback1).toHaveBeenCalled(); | ||
expect(callback2).toHaveBeenCalled(); | ||
}); | ||
}); | ||
describe('remove', () => { | ||
@@ -1038,2 +1157,17 @@ | ||
}); | ||
it('should handle camelCase and kebab-case property names', () => { | ||
const div = document.createElement('div'); | ||
$(div).css('backgroundColor', 'red'); | ||
expect(div.style.backgroundColor).toBe('red'); | ||
$(div).css('background-color', 'blue'); | ||
expect(div.style.backgroundColor).toBe('blue'); | ||
}); | ||
it('should return computed styles when requested', () => { | ||
const div = document.createElement('div'); | ||
div.style.fontSize = '16px'; | ||
document.body.appendChild(div); | ||
expect($(div).css('font-size', null, { includeComputed: true })).toBe('16px'); | ||
}); | ||
}); | ||
@@ -1071,4 +1205,11 @@ | ||
document.body.appendChild(div2); | ||
expect($('div').attr('test')).toStrictEqual(['test','test2']); | ||
expect($('div').attr('test')).toStrictEqual(['test', 'test2']); | ||
}); | ||
it('should handle properties that are not reflected as attributes', () => { | ||
const input = document.createElement('input'); | ||
$(input).prop('value', 'test'); | ||
expect(input.value).toBe('test'); | ||
expect(input.getAttribute('value')).toBe(null); | ||
}); | ||
}); | ||
@@ -1200,30 +1341,44 @@ | ||
describe('insertAfter', () => { | ||
it('should move elements after each target element', () => { | ||
const div1 = document.createElement('div'); | ||
div1.textContent = 'Div 1'; | ||
const div2 = document.createElement('div'); | ||
div2.textContent = 'Div 2'; | ||
const p1 = document.createElement('p'); | ||
p1.textContent = 'Paragraph 1'; | ||
const p2 = document.createElement('p'); | ||
p2.textContent = 'Paragraph 2'; | ||
document.body.appendChild(div1); | ||
document.body.appendChild(p1); | ||
document.body.appendChild(div2); | ||
document.body.appendChild(p2); | ||
describe('Insertion Methods', () => { | ||
$('div').insertAfter('p'); | ||
beforeEach(() => { | ||
document.body.innerHTML = '<div id="target"></div><div id="target2"></div>'; | ||
}); | ||
expect(document.body.children[0].tagName).toBe('P'); | ||
expect(document.body.children[0].textContent).toBe('Paragraph 1'); | ||
expect(document.body.children[1].tagName).toBe('DIV'); | ||
expect(document.body.children[1].textContent).toBe('Div 1'); | ||
expect(document.body.children[2].tagName).toBe('P'); | ||
expect(document.body.children[2].textContent).toBe('Paragraph 2'); | ||
expect(document.body.children[3].tagName).toBe('DIV'); | ||
expect(document.body.children[3].textContent).toBe('Div 2'); | ||
describe('prepend', () => { | ||
it('should prepend content to the target', () => { | ||
$('#target').prepend('<p>Test</p>'); | ||
expect(document.getElementById('target').innerHTML).toBe('<p>Test</p>'); | ||
}); | ||
}); | ||
describe('append', () => { | ||
it('should append content to the target', () => { | ||
$('#target').append('<p>Test</p>'); | ||
expect(document.getElementById('target').innerHTML).toBe('<p>Test</p>'); | ||
}); | ||
}); | ||
describe('insertBefore', () => { | ||
it('should insert elements before the target', () => { | ||
$('<p>Test</p>').insertBefore('#target'); | ||
expect(document.body.innerHTML).toBe('<p>Test</p><div id="target"></div><div id="target2"></div>'); | ||
}); | ||
}); | ||
describe('insertAfter', () => { | ||
it('should insert elements after the target', () => { | ||
$('<p>Test</p>').insertAfter('#target'); | ||
expect(document.body.innerHTML).toBe('<div id="target"></div><p>Test</p><div id="target2"></div>'); | ||
}); | ||
it('should insert elements after each target element', () => { | ||
$('<p>Test</p>').insertAfter('div'); | ||
expect(document.body.innerHTML).toBe('<div id="target"></div><p>Test</p><div id="target2"></div><p>Test</p>'); | ||
}); | ||
}); | ||
}); | ||
describe('next', () => { | ||
@@ -1295,2 +1450,81 @@ it('should return the next sibling element', () => { | ||
describe('Value-returning methods', () => { | ||
beforeEach(() => { | ||
document.body.innerHTML = ''; | ||
}); | ||
describe('attr', () => { | ||
it('should return undefined when getting attribute on non-existent element', () => { | ||
expect($('.non-existent').attr('id')).toBe(undefined); | ||
}); | ||
}); | ||
describe('prop', () => { | ||
it('should return undefined when getting property on non-existent element', () => { | ||
expect($('.non-existent').prop('id')).toBe(undefined); | ||
}); | ||
}); | ||
describe('css', () => { | ||
it('should return undefined when getting style on non-existent element', () => { | ||
expect($('.non-existent').css('color')).toBe(undefined); | ||
}); | ||
}); | ||
describe('html', () => { | ||
it('should return undefined when getting HTML on non-existent element', () => { | ||
expect($('.non-existent').html()).toBe(undefined); | ||
}); | ||
}); | ||
describe('text', () => { | ||
it('should return undefined when getting text on non-existent element', () => { | ||
expect($('.non-existent').text()).toBe(undefined); | ||
}); | ||
}); | ||
describe('val', () => { | ||
it('should return undefined when getting value on non-existent element', () => { | ||
expect($('.non-existent').val()).toBe(undefined); | ||
}); | ||
}); | ||
describe('height', () => { | ||
it('should return undefined when getting height on non-existent element', () => { | ||
expect($('.non-existent').height()).toBe(undefined); | ||
}); | ||
}); | ||
describe('width', () => { | ||
it('should return undefined when getting width on non-existent element', () => { | ||
expect($('.non-existent').width()).toBe(undefined); | ||
}); | ||
}); | ||
describe('scrollHeight', () => { | ||
it('should return undefined when getting scrollHeight on non-existent element', () => { | ||
expect($('.non-existent').scrollHeight()).toBe(undefined); | ||
}); | ||
}); | ||
describe('scrollWidth', () => { | ||
it('should return undefined when getting scrollWidth on non-existent element', () => { | ||
expect($('.non-existent').scrollWidth()).toBe(undefined); | ||
}); | ||
}); | ||
describe('scrollLeft', () => { | ||
it('should return undefined when getting scrollLeft on non-existent element', () => { | ||
expect($('.non-existent').scrollLeft()).toBe(undefined); | ||
}); | ||
}); | ||
describe('scrollTop', () => { | ||
it('should return undefined when getting scrollTop on non-existent element', () => { | ||
expect($('.non-existent').scrollTop()).toBe(undefined); | ||
}); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91712
2281
+ Added@semantic-ui/utils@0.0.32(transitive)
- Removed@semantic-ui/utils@0.0.31(transitive)
Updated@semantic-ui/utils@^0.0.32