-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Hide color pickers in paragraph and button if no colors are available. #5570
Hide color pickers in paragraph and button if no colors are available. #5570
Conversation
import { ifCondition, PanelColor, withContext } from '@wordpress/components'; | ||
import { compose } from '@wordpress/element'; | ||
|
||
export default compose( |
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.
Was trying to think of a way we could avoid duplicating the logic here between ColorPalette
and ConditionalPanelColor
. Unfortunate there's no easy way to avoid rendering a wrapper if none of its children render. I wonder if we could do a render prop which self-contains the null rendering:
<EditorColors>
{ ( { colors, disableCustomColors } ) => (
<PanelColor title={ __( 'Text Color' ) } colorValue={ textColor }>
<ColorPalette
value={ textColor }
onChange={ ( colorValue ) => setAttributes( { textColor: colorValue } ) }
colors={ colors }
disableCustomColors={ disableCustomColors }
/>
</PanelColor>
) }
</EditorColors>
const EditorColors = compose(
withContext( 'editor' )(
( settings, props ) => ( {
colors: props.colors || settings.colors,
disableCustomColors: props.disableCustomColors !== undefined ?
props.disableCustomColors :
settings.disableCustomColors,
} )
),
ifCondition(
( { colors, disableCustomColors } ) => ! ( isEmpty( colors ) && disableCustomColors )
)
)( ( { children, ...props } ) => children( props ) );
This way, we don't need ColorPalette
to retrieve color settings from context, because they'd always be passed in.
Thoughts?
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.
In either case, if we're creating a new component, it should be added to blocks/index.js
to export on the global.
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 @aduth thank you for providing an alternative implementation, I really like the EditorColors component idea. I applied it to this PR.
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.
While I see the value of avoiding duplication, I think from a plugin author's perspective, duplication is fine or at least we could extract the logic into a EditorColors
component but I think it should be baked in into these two components instead of using it at the block level. The block author could just use the ColorPalette
or ColorPanel
directly in that case.
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 @youknowriad, I understand your points. PanelColor is a generic component in @wordpress/components
and we should not add the context information or conditional render directly to it. Maybe we should keep the context in the ColorPalette but change it to use a component that retrieves colors from the context.
Then we create a new component HasColorsColorsToChoose or something similar that conditional render the children if colors are available or custom colors are enabled. And we use it to wrap color logic without changing how any of the existing components work, they would keep their existing functionality and would work alone.
The other alternative is similar to what I had at the start a ConditionalPanelColor component that just conditionally renders PanelColor, but now we make use of a component that retrieves the colors from the context so no code duplication exists when comparing to the ColoPalette.
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 other alternative is similar to what I had at the start a ConditionalPanelColor component that just renders PanelColor , but now we make use of a component that retrieves the colors from the state so no code duplication exists when comparing to the ColoPalette.
I was thinking more in that vein. The same we we have wp.blocks.ColorPalette
we could have we.blocks.ColorPanel
with context awareness. We can push the logic further by extracting the generic Color Palette to the components module so we'd also have wp.components.ColorPalette
and wp.components.ColorPanel
without any context awareness. Thoughs?
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 think it makes sense but maybe in the @blocks version, we should remove support for passing colors as props when that is a requirement the @components version can be used.
As if the behavior of the PanelColor in blocks is to not render if no colors are available if the plugin creator wants custom colors using the @blocks he would need to pass the colors also PanelColor which would only use them to check the conditional logic and does not need to know the colors for anything else.
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.
makes sense to me 👍 just thinking how we'd ensure backwards compatibility in that case. Maybe support it for a release or two with a deprecated message?
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.
Exactly, for the PanelColor @blocks no deprecation is necessary as it is something new the existing one is at @components and will stay untouched. For the ColorPalette we would support but with a deprecation message when the prop is used saying to change to the @components version.
13fc90d
to
8d3fe23
Compare
@jorgefilipecosta nice work. Can we go further and include This code looks so similar that it might make sense even go further and simplify it to:
One of the block uses |
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.
Not relevant to the changes here, but: What should be expected if I add this to my theme?
add_theme_support( 'disable-custom-colors' );
add_theme_support( 'editor-color-palette', [] );
No idea where it's added, but this shows the color picker, with a single option for black. Doesn't seem correct to me (and even if it's working-as-intended, certainly not intuitive).
blocks/editor-colors/index.js
Outdated
} ) | ||
), | ||
ifCondition( | ||
( { colors, disableCustomColors } ) => ! ( isEmpty( colors ) && disableCustomColors ) |
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 spent far too much time staring at this to make sense of it. Seems correct, not sure how we could make it clearer, but doing De Morgan's in my head a few times and thinking in inverses with disabling made this quite difficult to discern.
8d3fe23
to
f7fef36
Compare
Hi @gziolo I think it totally makes sense for the contrast checker to be inside the "conditional render", I missed it and I moved it inside. I thought a little bit more and for now, I renamed EditorColors to ColorContext to make more evident that this is just a context retrieval component and not a UI mechanism. I also remove the conditional render from it and added a new prop hasColorsToChoose that blocks can make use to conditional render the children or for example show a warning or use predefined colors if colors are mandatory for the block to make sense. Hi @aduth,
That happens, because we define the colors as arguments instead of as an array: Thank you both for reviews! |
Sounds great, but definitely not for this PR :) |
blocks/library/button/index.js
Outdated
isLargeText={ true } | ||
/> } | ||
<ColorContext> | ||
{ ( { colors, disableCustomColors, hasColorsToChoose } ) => ( hasColorsToChoose ? [ |
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.
It might make sense to refactor this code to work with Fragment
component instead of an array:
{ ( { colors, disableCustomColors, hasColorsToChoose } ) => ( hasColorsToChoose && (
<Fragment>
<PanelColor title={ __( 'Background Color' ) } colorValue={ color }>
....
</Fragment>
) }
It removes the need to have ,
and key
:)
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 always question my self about what to use arrays or fragment component. For this cases, I will try to default to Fragment from now on. The code was updated.
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.
ContrastCheckerWithFallbackStyles needs to be fixed.
f7fef36
to
fca1496
Compare
Hi @gziolo, I also found out about the problem with the contrast checker while testing this PR. Turns out it was not related to the changes here and also happened on the master (it was caused during a refactor to Notices). My PR that fixes it, was already merged #5800. I rebased this PR so this problem should not happen now. |
blocks/color-palette/index.js
Outdated
@@ -77,12 +77,3 @@ export function ColorPalette( { colors, disableCustomColors = false, value, onCh | |||
</div> | |||
); | |||
} | |||
|
|||
export default withContext( 'editor' )( |
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 breaking change in how this component works, you need to provide the colors as a prop now. Should we provide a BC alternative?
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.
ColorPalette
become a general purpose component after this refactoring. What we could do to provide backward compatibility is move ColorPallete
to components
module and still expose it in here with the context applied for the 2 upcoming release cycles. We would use wp.components.ColorPallete
for the core blocks. What do you think?
blocks/library/button/index.js
Outdated
backgroundColor={ color } | ||
isLargeText={ true } | ||
/> } | ||
<ColorContext> |
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.
Why did you choose to implement this as a component with children as function. I'm a little bit concerned about this because it's not so obvious for block authors? Do you think it's better to bake these checks inside PanelColor
and ColorPalette
instead?
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 @youknowriad, this pattern was used to avoid code duplication #5570 (comment).
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.
@youknowriad, I would rather further consolidate those color panels as proposed in #5570 (comment).
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.
Hmm, this would work to:
<PanelColor title={ __( 'Background Color' ) } attributeName="color" value={ color } />
😃
I guess it would have to be:
<PanelColor title={ __( 'Background Color' ) } onChange={ ( colorValue ) => setAttributes( { color: colorValue } ) } value={ color } />
fca1496
to
8626263
Compare
This PR was updated yesterday I followed @gziolo and @youknowriad suggestions and we still have no code duplication. The components/color-palette/index.js is exactly equal to the one we had in blocks with just the context removed. |
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 had some more comments. It also needs to be rebased.
blocks/panel-color/index.js
Outdated
/** | ||
* WordPress dependencies | ||
*/ | ||
import { PanelColor } from '@wordpress/components'; |
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.
It can be alsoimported as:
import { PanelColor as PanelColorComponent } from '@wordpress/components';
Then we can name this component the same way as the file: PanelColor
.
blocks/panel-color/index.js
Outdated
); | ||
} | ||
|
||
export default withColorContext( ConditionalPanelColor ); |
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.
To avoid if ( ! hasColorsToChoose ) {
check it can be expressed as:
export default compose(
withColorContext,
ifCondition( ( { hasColorsToChoose } ) => hasColorsToChoose ),
)( PanelColor );
Aside: ifCondition
could support string to pass a prop name :)
components/color-palette/index.js
Outdated
<div className="blocks-color-palette"> | ||
{ map( colors, ( color ) => { | ||
const style = { color: color }; | ||
const className = classnames( 'blocks-color-palette__item', { 'is-active': value === color } ); |
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.
Class names are no longer valid. They should be prefixed with components-
not blocks-
because they were moved to a different module.
blocks/color-palette/index.js
Outdated
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Dropdown, withContext } from '@wordpress/components'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { ColorPalette } from '@wordpress/components'; |
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.
Do we need to keep this file since we always use PanelColor
in all core blocks?
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, in order to be back compatible with blocks created by the community and that use @blocks/ColorPalette
. Since the file is very small we could export it from @blocks
directly, but we would be importing other components and use a different logic in the file. Using a file allows us to use the same export logic in the @blocks
file, and after two versions we can deprecate this and delete this file.
494f181
to
3d10db5
Compare
Hi @gziolo, thank you for the catches, the feedback was addressed. |
3d10db5
to
d4adfbc
Compare
If the theme sets the colors palette to empty and disable custom colors, the color picker mechanism did not work correctly. It just showed a non-functional back color to choose from. Now we hide the color picker mechanism as in that case it is not possible to choose colors.
d4adfbc
to
340789b
Compare
This PR was rebased and all feedback was addressed. |
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.
Let's ship it 🚢
It had enough iterations, works as advertised and looks solid 👍
Thank you for the review @gziolo! |
If the theme sets the color palette to empty and disables custom colors, the color picker mechanism did not work correctly. It just showed a non-functional back color to choose from. Now we hide the color picker mechanism as in that case it is not possible to choose colors.
Closes: #5447
How Has This Been Tested?
Disable custom colors, and set the color pallete to empty. That can be done by adding the following lines to the functions.php of the theme:
Verify in paragraph and button that the color panels are now hidden.
Enable some colors in the palette, and/or enable custom colors. Verify that if one or two the features are enabled the color panels are shown.