Skip to content
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

CustomSelectControlV2: Add root element wrapper #62803

Merged
merged 3 commits into from
Jun 26, 2024

Conversation

ciampo
Copy link
Contributor

@ciampo ciampo commented Jun 24, 2024

What?

Extracted from #62424

Add a root element wrapper to CustomSelectControlV2

Why?

  • To fix layout bugs where outer CSS rules "leak" to the internal elements of CustomSelectControl (eg. if the parent has display: flex, which is what happens when using CustomSelectControl as a direct child of ToolsPanelItem)
  • For legacy reasons, in order to have a place where to apply the components-custom-select-control className

How?

Adding a root element wrapper (ie a div) instead of a React Fragment, wrapping label + button under one element.

Testing Instructions

In the editor

To test in the editor, apply this patch

Click to expand
diff --git a/packages/block-editor/src/components/font-appearance-control/index.js b/packages/block-editor/src/components/font-appearance-control/index.js
index 18e814ad23..1340396c86 100644
--- a/packages/block-editor/src/components/font-appearance-control/index.js
+++ b/packages/block-editor/src/components/font-appearance-control/index.js
@@ -1,10 +1,20 @@
 /**
  * WordPress dependencies
  */
-import { CustomSelectControl } from '@wordpress/components';
+import {
+	CustomSelectControl,
+	privateApis as componentsPrivateApis,
+} from '@wordpress/components';
 import { useMemo } from '@wordpress/element';
 import { __, _x, sprintf } from '@wordpress/i18n';
 
+/**
+ * Internal dependencies
+ */
+import { unlock } from '../../lock-unlock';
+
+const { CustomSelectControlV2Legacy } = unlock( componentsPrivateApis );
+
 const FONT_STYLES = [
 	{
 		name: _x( 'Regular', 'font style' ),
@@ -205,7 +215,7 @@ export default function FontAppearanceControl( props ) {
 
 	return (
 		hasStylesOrWeights && (
-			<CustomSelectControl
+			<CustomSelectControlV2Legacy
 				{ ...otherProps }
 				className="components-font-appearance-control"
 				label={ label }
diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts
index f55373664e..9a26b7c7e3 100644
--- a/packages/components/src/private-apis.ts
+++ b/packages/components/src/private-apis.ts
@@ -14,6 +14,7 @@ import {
 	useCompositeStore as useCompositeStoreV2,
 } from './composite/v2';
 import { default as CustomSelectControl } from './custom-select-control';
+import { default as CustomSelectControlV2Legacy } from './custom-select-control-v2/legacy-component';
 import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils';
 import { createPrivateSlotFill } from './slot-fill';
 import {
@@ -40,6 +41,7 @@ lock( privateApis, {
 	CompositeRowV2,
 	useCompositeStoreV2,
 	CustomSelectControl,
+	CustomSelectControlV2Legacy,
 	__experimentalPopoverLegacyPositionToPlacement,
 	createPrivateSlotFill,
 	ComponentsContext,

  1. Load the post editor
  2. Select the block, and in the block toolbar, add the "Appearance" controls in the typography section
  3. Notice how the label now has bottom margin
Before (trunk) After (this PR)
Screenshot 2024-06-25 at 10 54 05 Screenshot 2024-06-25 at 10 54 45

@ciampo ciampo self-assigned this Jun 25, 2024
@ciampo ciampo changed the title CustomSelectControlV2 Legacy Adapter: Add root element wrapper CustomSelectControlV2: Add root element wrapper Jun 25, 2024
@ciampo ciampo force-pushed the fix/custom-select-control-v2-root-wrapper branch from e29ca7a to 078a2de Compare June 25, 2024 08:49
@ciampo ciampo requested a review from a team June 25, 2024 09:07
@ciampo ciampo added [Type] Bug An existing feature does not function as intended [Package] Components /packages/components labels Jun 25, 2024
@ciampo ciampo marked this pull request as ready for review June 25, 2024 09:07
@ciampo ciampo requested a review from ajitbohra as a code owner June 25, 2024 09:07
Copy link

github-actions bot commented Jun 25, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ciampo <[email protected]>
Co-authored-by: mirka <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

...restProps
} = props;

return (
<>
// Where should `restProps` be forwarded to?
Copy link
Contributor Author

@ciampo ciampo Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something unrelated to this PR, but that we should definitely discuss.

Currently, ...restProps are assumed to be intended for CustomSelectButton, but I'd argue that folks would expect that most attributes would be assigned to the root wrapper (which will likely become BaseControl)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be the CustomSelectButton. Is there anything other than className that you'd want to pass to the root wrapper? I'm thinking about refs and event listeners, which would pass more naturally onto the button.

Copy link
Contributor Author

@ciampo ciampo Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that if we start adding exceptions like className, devEx will suffer because it won't be clear where each prop will be forwarded to. Also, if a consumer of the component wanted to set a className on the trigger button, how would they do it?

Another though: if we don't pass any props to the root wrapper element, do you see that as a limitation for consumers? For example,:

  • let's say that as a consumer I want to hide CustomSelectControl — setting display: none would only hide the button, but keep the label around
  • let's say I'm rendering CustomSelectControlV2 inside a flexbox parent, and I'd like to set flex-shring on it. Using className or style won't cut it, since those styles should be applied to the root wrapper. The only way to achieve that will be to add an external wrapper component to CustomSelectControlV2.

I think that this is a broader conversation that we need to have as we export components that internally compose smaller components.

For example, in DropdownMenuV2 we solved this by exporting a separate Trigger subcomponent, and exposing a trigger render prop. Which means that props by default can be passed to the root element, while trigger-specific props are passed directly to the Trigger subcomponent.

Curious to hear @DaniGuardiola 's thoughts on this one too.

Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 🚀

@@ -21,6 +22,7 @@ function CustomSelectControl( props: LegacyCustomSelectProps ) {
onChange,
size = 'default',
value,
className: classNameProp,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the rename?

Copy link
Contributor Author

@ciampo ciampo Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I would otherwise get a 'className' is already declared in the upper scope linting error later in the file where options is mapped into <CustomSelectItem />s

...restProps
} = props;

return (
<>
// Where should `restProps` be forwarded to?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be the CustomSelectButton. Is there anything other than className that you'd want to pass to the root wrapper? I'm thinking about refs and event listeners, which would pass more naturally onto the button.

@ciampo ciampo force-pushed the fix/custom-select-control-v2-root-wrapper branch from 078a2de to 1374b58 Compare June 26, 2024 14:45
@ciampo
Copy link
Contributor Author

ciampo commented Jun 26, 2024

I'm going to merge once CI cheks pass — although we should continue the conversation in #62803 (comment) and work on it via a follow-up as necessary.

@ciampo ciampo enabled auto-merge (squash) June 26, 2024 14:46
@ciampo ciampo merged commit 2be17bb into trunk Jun 26, 2024
61 checks passed
@ciampo ciampo deleted the fix/custom-select-control-v2-root-wrapper branch June 26, 2024 15:19
@github-actions github-actions bot added this to the Gutenberg 18.8 milestone Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants