-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Added contrast verification to paragraph colors #3483
Conversation
blocks/library/paragraph/index.js
Outdated
this.grabTextColor(); | ||
} | ||
|
||
grabTextColor() { |
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.
@jorgefilipecosta have you considered factoring out color related logic into a higher-order component? It looks very familiar to the implementation of the button. In both cases, we have text color and background color that are compared. From what I see this wrapper component could handle:
- node
- editableRef
- state
- bindRef
- grabTextColor
- componentDidMount & componentDidUpdate
blocks/library/paragraph/index.js
Outdated
</PanelBody> | ||
<ContrastChecker | ||
textColor={ textColor || fallbackTextColor } | ||
backgroundColor={ backgroundColor || '#fff' } |
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.
Is there a plan to add fallback logic to backgroundColor
, too? Or maybe it is always white anyway :)
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.
That's a nice question, I used a logic for background color but, the getComputeStyle always returned black, while the background is white If background color was explicitly set the getComputedSyle returned correctly. So I assumed the background when not explicitly set is white.
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.
Better try to change it using CSS. It is possible that an external plugin would apply such change.
This is a really cool feature 💯 The code looks good from what I can see, I left a suggestion to extract a higher order component to hide the logic related to the color pickers. Anyway if you feel that the current solution works better in this case, let me know and I will do more in-depth code check. |
c7bdf26
to
4d03540
Compare
Hi @gziolo, I followed your advice and I created a High Order Component to abstract part of the common logic withFallbackStyles. Some parts change from block to block and I was not able to abstract. |
blocks/library/button/index.js
Outdated
const ButtonBlockFallbackStyles = withFallbackStyles( ( node, ownProps ) => { | ||
const { textColor, color } = ownProps.attributes; | ||
return { | ||
fallbackBackgroundColor: color ? undefined : getComputedStyle( node.querySelector( `.${ ownProps.className }` ) ).backgroundColor, |
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.
Alternatively, you can use span[key="button"]
as a query selector.
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.
Hi @gziolo, I tried this approach, but unfortunately react element keys are not passed to the dom (possible to confirm using browser dev tools), so this query selector was not returning and I end up not applying this change.
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.
Fair enough 👍
blocks/library/paragraph/index.js
Outdated
const { textColor, backgroundColor } = ownProps.attributes; | ||
const computedStyles = ( ! textColor || ! backgroundColor ) ? getComputedStyle( node.querySelector( '[contenteditable="true"]' ) ) : null; | ||
return { | ||
fallbackBackgroundColor: backgroundColor ? undefined : computedStyles.backgroundColor, |
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.
Can you explain why we set undefined
when color is passed as a prop? From what I can see we can safely set it, but it will be ignored. It would make reading code easier if we would always compute it even when it's not used. Unless I missed something.
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.
If there are custom colors set the fallbackColors will be equal to the custom colors set. As we have a logic that stops grabbing colors when we have all fallbackColors needed, if later we remove the custom colors, the fallbackColors are not updated and would still be equal to the custom colors set the first time instead of the colors that appear when no custom color is selected.
blocks/library/paragraph/style.scss
Outdated
@@ -18,3 +18,7 @@ p.has-drop-cap:not( :focus ) { | |||
p.has-background { | |||
padding: 20px 30px; | |||
} | |||
|
|||
.wp-block-paragraph { |
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.
@jasmussen or @karmatosed, is it okey to set it explicitly here?
|
||
grabFallbackStyles() { | ||
const { grabStylesCompleted, fallbackStyles } = this.state; | ||
if ( this.nodeRef && ! grabStylesCompleted ) { |
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.
Can you explain why we need to use grabStylesCompleted
in here? Can we check if fallbackStyles
is still undefined
instead? It looks like you can always compute grabStylesCompleted
using the existing variables.
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.
Hi @gziolo, for example in button we may have set a custom text color, so we can not compute the fallbackTextColor but we can compute the fallbackBackgroudColor. So our mapNodeToProps returns undefined for the properties it cannot compute the fallback color (fallbackTextColor). When there is no undefined value ( ! findKey( newFallbackStyles, ( obj ) => obj === undefined ) ) = true we know we don't need to grab colors anymore. The value of this is saved in grabStylesCompleted so we are not running this line with findKey all the time.
Another advantage is when inserting a component no custom colors are set so all fallback colors are immediately saved and logic to grab color is just executed one time.
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.
Would it work if you would use every from lodash instead of findKey:
every( newFallbackStyles )
It might better express intent.
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 agree using "every" would make things simpler to understand, but I think we can not use it here. Every works on a collection here we are working a single object and we just want to make sure there is no key/property of that object that is undefined.
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.
This is a perfectly fine usage
every( { a: true, b: 1, c: undefined } );
It works with with both arrays and objects:
collection (Array|Object): The collection to iterate over.
Some utility methods that operate on collections, work with strings, too. See includes.
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.
Thank you for your suggestion @gziolo you were right and your suggestion was applied before merging.
It works perfectly fine, great job. Thanks for doing all refinements. Now, seeing all that logic extracted, I started to think if we can take a slightly different approach and wrap HOC directly on What if you would be able to use it as follows: <ContrastCheckerWithFallbackStyles
node={ this.nodeRef }
textColor={ textColor }
backgroundColor={ backgroundColor }
isLargeText={ true }
/> This way the contrast checker would only have a reference to the node to compute fallbacks when necessary. Anyway, I'm also fine with what we aleady have here. 👍 |
4564831
to
54ac133
Compare
Codecov Report
@@ Coverage Diff @@
## master #3483 +/- ##
==========================================
- Coverage 37.15% 37.09% -0.06%
==========================================
Files 275 276 +1
Lines 6629 6653 +24
Branches 1202 1210 +8
==========================================
+ Hits 2463 2468 +5
- Misses 3517 3528 +11
- Partials 649 657 +8
Continue to review full report at Codecov.
|
Hi @gziolo, I did a new commit that uses this approach instead :) It has the advantage of not color grabbing logic being executed at all if contrast verification is not needed. No changes were needed in our HoC, and the only change applied in the contrast verifier was adding the fallbackColors properties directly to it (optional props). |
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’ll test again tomorrow, it looks great at the moment. I really like how independent contrast checker component became 👏
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.
Code wise looks good to me. It also works as advertised.
I would also suggest asking @afercia or @nic-bertino to verify if it works as expected before you merge it.
Let's get the conflicts resolved on this and then merge, it's better than what we have right now so worth getting in. |
e9bd575
to
5e7d630
Compare
…les higher order component.
…llbackStyles wrapper.
5e7d630
to
22d59ba
Compare
Description
This PR aims to address the remaining part of #2381, by adding a contrast checking following WCAG 2.0- AA (https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) to the paragraph.
How Has This Been Tested?
Test some colors in the paragraph and verify that when contrast is high no message, in low contrast color pairs a message appear.
Verify that when the background is darker than text, we suggest making the background darker and/or the text lighter and when text is darker we suggest making the text darker and/or background lighter.
Verify that for some color pairs e.g. background #C46161 and black text or our blue with no background the message does not appear, if the font size is equal or larger than 18 and the message appears if the font size is smaller.
Screenshots (jpeg or gifs if applicable):