-
Notifications
You must be signed in to change notification settings - Fork 783
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
fix(link-in-text-block): take into account ancestor inline element styles #4135
base: develop
Are you sure you want to change the base?
Changes from all commits
bc4bf09
47bf2b5
859ffcf
c50700e
470b462
2f100c1
014585f
5ebde67
4d839b9
25b3001
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 |
---|---|---|
@@ -1,19 +1,6 @@ | ||
import Color from './color'; | ||
|
||
/** | ||
* Creates a string array of fonts for given CSSStyleDeclaration object | ||
* @private | ||
* @param {Object} style CSSStyleDeclaration object | ||
* @return {Array} | ||
*/ | ||
function _getFonts(style) { | ||
return style | ||
.getPropertyValue('font-family') | ||
.split(/[,;]/g) | ||
.map(font => { | ||
return font.trim().toLowerCase(); | ||
}); | ||
} | ||
import { sanitize } from '../text'; | ||
import { getComposedParent } from '../dom'; | ||
|
||
/** | ||
* Determine if the text content of two nodes is styled in a way that they can be distinguished without relying on color | ||
|
@@ -24,64 +11,105 @@ function _getFonts(style) { | |
* @param {HTMLElement} ancestorNode The ancestor node element to check | ||
* @return {Boolean} | ||
*/ | ||
function elementIsDistinct(node, ancestorNode) { | ||
var nodeStyle = window.getComputedStyle(node); | ||
export default function elementIsDistinct(node, ancestorNode) { | ||
const nodeStyle = window.getComputedStyle(node); | ||
|
||
// Check if the link has a background | ||
if (nodeStyle.getPropertyValue('background-image') !== 'none') { | ||
return true; | ||
} | ||
const ancestorStyle = window.getComputedStyle(ancestorNode); | ||
let curNode = node; | ||
while (curNode !== ancestorNode) { | ||
straker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const curNodeStyle = window.getComputedStyle(curNode); | ||
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. In the first iteration curNodeStyle === nodeStyle. Lets avoid calling this expensive function twice. |
||
|
||
// Check if the link has a border or outline | ||
var hasBorder = ['border-bottom', 'border-top', 'outline'].reduce( | ||
straker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(result, edge) => { | ||
var borderClr = new Color(); | ||
borderClr.parseString(nodeStyle.getPropertyValue(edge + '-color')); | ||
|
||
// Check if a border/outline was specified | ||
return ( | ||
result || | ||
// or if the current border edge / outline | ||
(nodeStyle.getPropertyValue(edge + '-style') !== 'none' && | ||
parseFloat(nodeStyle.getPropertyValue(edge + '-width')) > 0 && | ||
borderClr.alpha !== 0) | ||
); | ||
}, | ||
false | ||
); | ||
|
||
if (hasBorder) { | ||
return true; | ||
// Check if the link has a background | ||
if (curNodeStyle.getPropertyValue('background-image') !== 'none') { | ||
return true; | ||
} | ||
|
||
if ( | ||
hasSameText(node, curNode) && | ||
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'm a little confused, why is hasSameText() not in the while condition? I.e. while (curNode !== ancestorNode && hasSameText(node, curNode)) { My intuition says that that's what should be happening. Remind me why an ancestor with a background-image that has different text would count as making the link distinct? |
||
(hasVisibleBorder(curNodeStyle) || | ||
hasVisibleTextDecortation(curNodeStyle, ancestorStyle)) | ||
) { | ||
return true; | ||
} | ||
|
||
curNode = getComposedParent(curNode); | ||
} | ||
|
||
var parentStyle = window.getComputedStyle(ancestorNode); | ||
// Compare fonts | ||
if (_getFonts(nodeStyle)[0] !== _getFonts(parentStyle)[0]) { | ||
// font styles are inherited so we only need to check | ||
// the target node | ||
if (getFonts(nodeStyle)[0] !== getFonts(ancestorStyle)[0]) { | ||
return true; | ||
} | ||
|
||
var hasStyle = [ | ||
'text-decoration-line', | ||
'text-decoration-style', | ||
'font-weight', | ||
'font-style', | ||
'font-size' | ||
].reduce((result, cssProp) => { | ||
let hasStyle = ['font-weight', 'font-style', 'font-size'].some(cssProp => { | ||
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. Maybe this stuff that inherits should go above the while loop. Also, a comment here would be nice to indicate this isn't in the while loop because it inherits. After not having thought about this for three weeks, it took me a bit to realise why you did it this way. |
||
return ( | ||
result || | ||
nodeStyle.getPropertyValue(cssProp) !== | ||
parentStyle.getPropertyValue(cssProp) | ||
ancestorStyle.getPropertyValue(cssProp) | ||
); | ||
}, false); | ||
}); | ||
|
||
var tDec = nodeStyle.getPropertyValue('text-decoration'); | ||
const tDec = nodeStyle.getPropertyValue('text-decoration'); | ||
if (tDec.split(' ').length < 3) { | ||
// old style CSS text decoration | ||
hasStyle = | ||
hasStyle || tDec !== parentStyle.getPropertyValue('text-decoration'); | ||
hasStyle || tDec !== ancestorStyle.getPropertyValue('text-decoration'); | ||
} | ||
|
||
return hasStyle; | ||
} | ||
|
||
export default elementIsDistinct; | ||
/** | ||
* Creates a string array of fonts for given CSSStyleDeclaration object | ||
* @private | ||
* @param {Object} style CSSStyleDeclaration object | ||
* @return {Array} | ||
*/ | ||
function getFonts(style) { | ||
return style | ||
.getPropertyValue('font-family') | ||
.split(/[,;]/g) | ||
.map(font => { | ||
return font.trim().toLowerCase(); | ||
}); | ||
} | ||
|
||
function hasSameText(node, ancestorNode) { | ||
// 3 is an arbitrary number that helps account for punctuation | ||
// and emoji that takes up two characters | ||
return ancestorNode.textContent | ||
.split(node.textContent) | ||
.every(text => sanitize(text).length <= 3); | ||
} | ||
|
||
function hasVisibleBorder(nodeStyle) { | ||
// Check if the link has a border or outline | ||
return ['border-bottom', 'border-top', 'outline'].some(edge => { | ||
const borderClr = new Color(); | ||
borderClr.parseString(nodeStyle.getPropertyValue(edge + '-color')); | ||
|
||
// Check if a border/outline was specified | ||
return ( | ||
nodeStyle.getPropertyValue(edge + '-style') !== 'none' && | ||
parseFloat(nodeStyle.getPropertyValue(edge + '-width')) > 0 && | ||
borderClr.alpha !== 0 | ||
); | ||
}); | ||
} | ||
|
||
function hasVisibleTextDecortation(nodeStyle, ancestorStyle) { | ||
// Check if the link has text-decoration styles | ||
return ['text-decoration-line', 'text-decoration-style'].some(cssProp => { | ||
const ancestorValue = ancestorStyle.getPropertyValue(cssProp); | ||
const nodeValue = nodeStyle.getPropertyValue(cssProp); | ||
|
||
/* | ||
For logic purposes we can treat the target node and all inline ancestors as a single logic point since if any of them define a text-decoration style it will visually apply that style to the target. | ||
|
||
A target node is distinct if it defines a text-decoration and either the ancestor does not define one or the target's text-decoration is different than the ancestor. A target that does not define a text-decoration can never be distinct from the ancestor. | ||
*/ | ||
return ( | ||
nodeValue !== 'none' && | ||
(ancestorValue === 'none' || nodeValue !== ancestorValue) | ||
); | ||
}); | ||
} |
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 don't think we can just push any inline ancestor. If the element has non-link text in it is no longer a distinguishing style. Take something like the following, that should not pass because it has a bold parent:
This probably does need to do some filtering. It doesn't matter if it's just a comma or something, but whole worlds I don't think counts anymore.
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.
The above issue was resolved for strong / underline, but not for pseudo elements. This shouldn't be passed because of that pseudoElm class:
It makes me wonder whether checking for pseudo elms should be done inside
elementIsDistinct
to avoid the repeat?