-
Notifications
You must be signed in to change notification settings - Fork 469
get by label concat values #681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
4033c6e
b609b32
732a8ab
eb8517d
f3a3c65
df29ead
de06780
9d2be9f
49c6148
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -12,30 +12,73 @@ import { | |||||||||||||||||||||||
} from './all-utils' | ||||||||||||||||||||||||
import {queryAllByText} from './text' | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
function getCombinations(labels, matcher) { | ||||||||||||||||||||||||
const combs = [[]] | ||||||||||||||||||||||||
const matching = [] | ||||||||||||||||||||||||
for (const label of labels) { | ||||||||||||||||||||||||
const copy = [...combs] // See note below. | ||||||||||||||||||||||||
for (const prefix of copy) { | ||||||||||||||||||||||||
const combination = prefix.concat(label.textToMatch) | ||||||||||||||||||||||||
combs.push(combination) | ||||||||||||||||||||||||
if (matcher(combination.join(' '), label.node)) { | ||||||||||||||||||||||||
matching.push(label.node) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
return matching | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
function queryAllLabels(container) { | ||||||||||||||||||||||||
return Array.from(container.querySelectorAll('label,input')) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It occurs to me that an element can be labeled by anything, not just That said, I'm not sure how to handle "everything else." It seems to me that rather than searching for all possible elements that can label anything, we should be searching for everything that's labeled first, determine the contents of their label, and then return the elements whose label matches the query. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For what it's worth, I think that searching for labelled items and then for their labels is more feasible than querying for all elements that could be labels. Like you said above! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you'd like to tack a whack at that, I think it would solve a lot of problems. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For sure I would like to do it! |
||||||||||||||||||||||||
.map(node => { | ||||||||||||||||||||||||
let textToMatch = | ||||||||||||||||||||||||
node.tagName.toLowerCase() === 'label' | ||||||||||||||||||||||||
? node.textContent | ||||||||||||||||||||||||
: node.value || null | ||||||||||||||||||||||||
// The children of a textarea are part of `textContent` as well. We | ||||||||||||||||||||||||
// need to remove them from the string so we can match it afterwards. | ||||||||||||||||||||||||
Array.from(node.querySelectorAll('textarea')).forEach(textarea => { | ||||||||||||||||||||||||
textToMatch = textToMatch.replace(textarea.value, '') | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// The children of a select are also part of `textContent`, so we | ||||||||||||||||||||||||
// need also to remove their text. | ||||||||||||||||||||||||
Array.from(node.querySelectorAll('select')).forEach(select => { | ||||||||||||||||||||||||
textToMatch = textToMatch.replace(select.textContent, '') | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
return {node, textToMatch} | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
.filter(({textToMatch}) => textToMatch !== null) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
function queryAllLabelsByText( | ||||||||||||||||||||||||
container, | ||||||||||||||||||||||||
text, | ||||||||||||||||||||||||
{exact = true, trim, collapseWhitespace, normalizer} = {}, | ||||||||||||||||||||||||
) { | ||||||||||||||||||||||||
const matcher = exact ? matches : fuzzyMatches | ||||||||||||||||||||||||
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer}) | ||||||||||||||||||||||||
return Array.from(container.querySelectorAll('label')).filter(label => { | ||||||||||||||||||||||||
let textToMatch = label.textContent | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// The children of a textarea are part of `textContent` as well. We | ||||||||||||||||||||||||
// need to remove them from the string so we can match it afterwards. | ||||||||||||||||||||||||
Array.from(label.querySelectorAll('textarea')).forEach(textarea => { | ||||||||||||||||||||||||
textToMatch = textToMatch.replace(textarea.value, '') | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
const textToMatchByLabels = queryAllLabels(container) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const nodesByLabelMatchingText = textToMatchByLabels | ||||||||||||||||||||||||
.filter(({node, textToMatch}) => | ||||||||||||||||||||||||
matcher(textToMatch, node, text, matchNormalizer), | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
.map(({node}) => node) | ||||||||||||||||||||||||
const labelsNotMatchingTextAndNotEmpty = textToMatchByLabels.filter( | ||||||||||||||||||||||||
({node, textToMatch}) => | ||||||||||||||||||||||||
Boolean(textToMatch) && | ||||||||||||||||||||||||
nodesByLabelMatchingText.findIndex(nodeByLabelByText => | ||||||||||||||||||||||||
nodeByLabelByText.isEqualNode(node), | ||||||||||||||||||||||||
) === -1, | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the right change should be: const labelsNotMatchingTextAndNotEmpty = textToMatchByLabels.filter(
({node, textToMatch}) =>
textToMatch && !nodesByLabelMatchingText.some(n => n.isEqualNode(node)),
) because I would like to retrieve all the labels in |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// The children of a select are also part of `textContent`, so we | ||||||||||||||||||||||||
// need also to remove their text. | ||||||||||||||||||||||||
Array.from(label.querySelectorAll('select')).forEach(select => { | ||||||||||||||||||||||||
textToMatch = textToMatch.replace(select.textContent, '') | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
const concatLabelsMatching = getCombinations( | ||||||||||||||||||||||||
labelsNotMatchingTextAndNotEmpty, | ||||||||||||||||||||||||
(textToMatch, node) => matcher(textToMatch, node, text, matchNormalizer), | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return matcher(textToMatch, label, text, matchNormalizer) | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
return nodesByLabelMatchingText.concat(concatLabelsMatching) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
function queryAllByLabelText( | ||||||||||||||||||||||||
|
@@ -50,6 +93,7 @@ function queryAllByLabelText( | |||||||||||||||||||||||
exact, | ||||||||||||||||||||||||
normalizer: matchNormalizer, | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
const labelledElements = labels | ||||||||||||||||||||||||
.reduce((matchedElements, label) => { | ||||||||||||||||||||||||
const elementsForLabel = [] | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what note this is referring to. Also, if I'm following this logic correctly, the for loop on the next line will only be run only once (for this iteration of the nested loop) and the value of the
prefix
will be[]
every time. Any reason we need a loop here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I am totally getting your point, are you telling that this loop runs only once inside the outer loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. Because
copy
is[[]]
and so it's an array with only one element in it. It'll never be....Wait. I guess that's only the case on the first iteration of the outer loop. Future iterations could have more... Hmmm... I need to take more time to understand what's going on here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it's only the first iteration of the outer loop that makes the inner one run only once.