Skip to content

Commit

Permalink
feat: multi elements support for attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
fczbkk committed Aug 15, 2021
1 parent 991531d commit a273c02
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 6 deletions.
24 changes: 18 additions & 6 deletions src/selector-attribute.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {sanitizeSelectorItem} from './utilities-selectors'
import {convertMatchListToRegExp} from './utilities-data'
import {CssSelector} from './types'
import {
sanitizeSelectorItem,
sanitizeSelectorNeedle
} from './utilities-selectors';
import {convertMatchListToRegExp, getIntersection} from './utilities-data';
import {CssSelector, SelectorNeedle} from './types';

// List of attributes to be ignored. These are handled by different selector types.
export const ATTRIBUTE_BLACKLIST = convertMatchListToRegExp([
Expand All @@ -13,7 +16,7 @@ export const ATTRIBUTE_BLACKLIST = convertMatchListToRegExp([
/**
* Get attribute selectors for an element.
*/
function attributeNodeToSelector ({
export function attributeNodeToSelector ({
nodeName,
nodeValue
}: Node) {
Expand All @@ -23,15 +26,24 @@ function attributeNodeToSelector ({
/**
* Checks whether attribute should be used as a selector.
*/
function isValidAttributeNode ({nodeName}: Node) {
export function isValidAttributeNode ({nodeName}: Node) {
return !ATTRIBUTE_BLACKLIST.test(nodeName)
}

/**
* Get attribute selectors for an element.
*/
export function getAttributeSelectors (element: Element): Array<CssSelector> {
export function getElementAttributeSelectors (element: Element): Array<CssSelector> {
return Array.from(element.attributes)
.filter(isValidAttributeNode)
.map(attributeNodeToSelector)
}

/**
* Get attribute selectors matching all elements.
*/
export function getAttributeSelectors (needle: SelectorNeedle): Array<CssSelector> {
const elements = sanitizeSelectorNeedle(needle)
const elementSelectors = elements.map(getElementAttributeSelectors)
return getIntersection(elementSelectors)
}
13 changes: 13 additions & 0 deletions src/utilities-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ export function getCombinations<T> (
.sort((a, b) => a.length - b.length)
}

/**
* Creates array containing only items included in all input arrays.
*/
export function getIntersection<T> (items: Array<Array<T>> = []): Array<T> {
const [firstItem = [], ...otherItems] = items
if (otherItems.length === 0) {
return firstItem
}
return (otherItems).reduce((accumulator, currentValue) => {
return accumulator.filter((item) => currentValue.includes(item))
}, firstItem)
}

/**
* Converts array of arrays into a flat array.
*/
Expand Down
14 changes: 14 additions & 0 deletions test/selector-attribute.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,18 @@ describe('selector - attribute', function () {
assert.equal(result.length, 0)
})

it('should generate attribute selectors for multiple elements', function () {
root.innerHTML = `
<div aaa="bbb" ccc="ddd"></div>
<div aaa="bbb"></div>
<div xxx="yyy"></div>
`
const elements = root.querySelectorAll('div')
const withIntersection = getAttributeSelectors([elements[0], elements[1]])
const noIntersection = getAttributeSelectors([elements[0], elements[2]])
assert.sameMembers(withIntersection, ["[aaa='bbb']"])
assert.sameMembers(noIntersection, [])
})


})
21 changes: 21 additions & 0 deletions test/utilities-get-intersection.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {assert} from 'chai'
import {getIntersection} from '../src/utilities-data.ts'

describe('utilities - getIntersection', () => {
it('should produce empty array from empty input', () => {
const result = getIntersection()
assert.sameMembers(result, [])
})
it('should replicate single array', () => {
const result = getIntersection([['a', 'b', 'c']])
assert.sameMembers(result, ['a', 'b', 'c'])
})
it('should produce empty array if no intersection', () => {
const result = getIntersection([['a', 'b', 'c'], ['x', 'y', 'z']])
assert.sameMembers(result, [])
})
it('should produce intersecting members', () => {
const result = getIntersection([['a', 'b', 'c'], ['b', 'c', 'd']])
assert.sameMembers(result, ['b', 'c'])
})
})

0 comments on commit a273c02

Please sign in to comment.