-
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
ToolsPanel: Add CSS classes to first and last displayed ToolsPanelItems #37546
ToolsPanel: Add CSS classes to first and last displayed ToolsPanelItems #37546
Conversation
Size Change: +138 B (0%) Total Size: 1.13 MB
ℹ️ View Unchanged
|
I was testing this both using this standalone PR, and rebased on #34027, and can confirm that the I tested using the Typography, Border and Color panels. The Similarly, removing optional controls resets the last class to the last control for me 👍 Where there is one control only both classes Is that intentional? I don't see any side effects of this, but just checking in case there could be conflict between first and last styles when applied to the same element.
Do you mean because it's not using React Testing Library queries? I'd tend to think it's okay since the classnames are fairly invisible to the user 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.
This LGTM.
I left a couple of questions, also I had a question about the use of first
and last
classnames over at https://github.com/WordPress/gutenberg/pull/34027/files#r779209344
They seem pretty generic to me, but I might not have the entire picture.
All things I think could be dealt with later.
It was intentional. The use case I was focused on was achieving the same styling of the color dropdowns in the color panel after switching to the
Yes, it felt a little rough to me that I couldn't retrieve the desired element directly through React Testing Library queries. Other PRs have been warned against using |
That's a good point. It should be a trivial change so I can make that shortly. |
Thinking a little more on this, I'm not sure what the best approach is. More specific class names such as @ciampo would you be able to provide some wisdom on the best approach here? |
Would using a fragment like this be better/more acceptable? <ToolsPanelItem { ...controlProps }>
<>Item 2</>
</ToolsPanelItem>
...
expect( screen.getByText( 'Item 2' ) ).toHaveClass( 'first' ); |
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.
Hey @aaronrobertshaw and @ramonjd , thank you for the ping (and happy new year!)
I've left a few inline comments, sorry for the delay in reviewing!
5b3ae82
to
4a3b43a
Compare
I know there are ongoing discussions, but I just took this for another test spin and, functionally, it's all working as expected, similar to the last round of testing I also checked every storybook example to make sure the classes are applied 👍 |
The snapshot test appears to be failing on this PR (but passes locally) due to the styled components' class names changing. I've run out of time today but will revisit this tomorrow. Any suggestions on how best to handle this are welcome 🙂 |
packages/components/src/tools-panel/stories/tools-panel-with-item-group-slot.js
Outdated
Show resolved
Hide resolved
border-left: 1px solid rgba( 0, 0, 0, 0.1 ); | ||
border-right: 1px solid rgba( 0, 0, 0, 0.1 ); | ||
border-bottom: 1px solid rgba( 0, 0, 0, 0.1 ); | ||
|
||
&&.first { | ||
border-top-left-radius: 10px; | ||
border-top-right-radius: 10px; | ||
border-top: 1px solid rgba( 0, 0, 0, 0.1 ); | ||
} | ||
|
||
&.last { | ||
border-bottom-left-radius: 10px; | ||
border-bottom-right-radius: 10px; | ||
} | ||
|
||
&& > div, | ||
&& > div > button { | ||
width: 100%; | ||
border-radius: inherit; | ||
} |
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.
By using ItemGroup
's isBordered
and isSeparated
props, we don't need to define custom CSS for borders (apart from a one-line override for the last
item)
see diff
diff --git a/packages/components/src/tools-panel/stories/tools-panel-with-item-group-slot.js b/packages/components/src/tools-panel/stories/tools-panel-with-item-group-slot.js
index 950a3c3f5e..ae4eaad17a 100644
--- a/packages/components/src/tools-panel/stories/tools-panel-with-item-group-slot.js
+++ b/packages/components/src/tools-panel/stories/tools-panel-with-item-group-slot.js
@@ -169,6 +169,8 @@ export const ToolsPanelWithItemGroupSlot = () => {
<Panel>
<ToolsPanelItems.Slot
as={ ItemGroup }
+ isBordered
+ isSeparated
className={ slotWrapperClassName }
resetAll={ resetAll }
/>
@@ -218,29 +220,18 @@ const SlotWrapper = css`
const ToolsPanelItemClass = css`
padding: 0;
- border-left: 1px solid rgba( 0, 0, 0, 0.1 );
- border-right: 1px solid rgba( 0, 0, 0, 0.1 );
- border-bottom: 1px solid rgba( 0, 0, 0, 0.1 );
-
- &&.first {
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
- border-top: 1px solid rgba( 0, 0, 0, 0.1 );
+
+ &.first {
+ border-left: 2px solid blue;
}
&.last {
- border-bottom-left-radius: 10px;
- border-bottom-right-radius: 10px;
+ border-right: 2px solid red;
+ border-bottom-color: transparent;
}
&& > div,
&& > div > button {
width: 100%;
- border-radius: inherit;
- }
-
- /* .components-dropdown class overrides ToolsPanelItemPlaceholder styles */
- &[class*='ToolsPanelItemPlaceholder'] {
- display: none;
}
`;
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.
Thanks. These were leftover from when the Item
wasn't a direct child of the slot. I'll clean it up.
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.
Revisiting this, the proposed suggestion won't really work for our needs and jogged my memory on why the original approach was taken.
There are a couple of issues:
- The
border-radius: inherit
for the button is needed so the focus outline is consistent with the rounded corners - We still need the application of the
border-radius
on the.first
and.last
classes because theItemGroup
styling doesn't handle hidden elements. (Part of the whole reason for this PR in the first place) - Using
isBordered
andisSeparated
forces everything to visually be represented as an item.
Regarding the last point, it is a problem when in our real-world use case, a contrast checker is also rendered into the panel. This means the contrast checker is not a ToolsPanelItem
but appears within the slot which is technically rendered as an ItemGroup
. Not forcing isBordered
and isSeparated
means it can be visually separated.
I've updated the story to include a contrast checker.
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 border-radius: inherit for the button is needed so the focus outline is consistent with the rounded corners
Makes sense, thank you for the explanation!
We still need the application of the border-radius on the .first and .last classes because the ItemGroup styling doesn't handle hidden elements. (Part of the whole reason for this PR in the first place)
Could you show a scenario in which ItemGroup
would fail in this scenario? When I came up with the code path above, I tested the Storybook example and everything seems to work fine when showing/hiding toolspanel items.
Using isBordered and isSeparated forces everything to visually be represented as an item.
Regarding the last point, it is a problem when in our real-world use case, a contrast checker is also rendered into the panel. This means the contrast checker is not a ToolsPanelItem but appears within the slot which is technically rendered as an ItemGroup. Not forcing isBordered and isSeparated means it can be visually separated.
Thank you for the explanation! There's an issue, though: ItemGroup
would technically expect ContrastChecker
to be rendered inside an Item
.
The main reason is that by default ItemGroup
renders an element with role="list"
, which itself expects children with role listitem
. We could override the role
, but then I would argue that we shouldn't use ItemGroup
at all, since we would be making use of its 2 major features (list semantics and visual bordered styles).
A potential solution could be to render ItemGroup
as a child of ToolsPanel
:
ToolsPanel
├── ItemGroup
│ ├── ToolsPanelItem as Item
│ │ ├── [item contents]
│ ├── ToolsPanelItem as Item
│ │ ├── [item contents]
│ ├── ToolsPanelItem as Item
│ │ ├── [item contents]
├── ContrastChecker
although we may probably need to re-apply the grid container styles to ItemGroup
. If that didn't work, maybe ItemGroup
is not the right fit for this scenario?
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.
Could you show a scenario in which ItemGroup would fail in this scenario? When I came up with the code path above, I tested the Storybook example and everything seems to work fine when showing/hiding toolspanel items.
The inheritance of the border-radius doesn't work fine because the ItemGroup
styles rely on > *:first-of-type > *
and > *:last-of-type > *
to apply the rounded
style border radii. These styles can't take into account whether the element in the item is actually just a non-visible placeholder.
I've updated the story to explicitly disable isRounded
on the slot's ItemGroup
. As noted previously, we still need styles to handle the border-radius on the .first
and .last
items. They can be made to inherit from the wrapping ItemGroup
(with an extra rule to target the item wrapper), exactly how this styling happens isn't really the issue. We could apply a custom border-radius to the ItemGroup
then inherit on the appropriate items or just style those first/last items directly. Either way these first/last classes were needed.
With all the iterations on this, I didn't remove the border color and styling part of the styles as I was distracted by the contrast checker issue when looking at the implications for #34027.
There's an issue, though: ItemGroup would technically expect ContrastChecker to be rendered inside an Item.
This is a fair point and is relevant to #34027. Either way, we still need the classes this PR provides and I don't think should be a blocker here.
I'll remove the contrast checker from the example here so as to avoid future confusion.
A potential solution could be to render ItemGroup as a child of ToolsPanel:
With a view towards #34027, this is easier said than done. Any block or plugin could add new color options to be included within the Inspector Controls color panel. This means the fills that render in the slot come from different places and may contain a mix of color options, contrast checkers or any other element they wish to add into the panel (help text etc).
Moving the contrast checkers to the bottom of the panel was done via CSS ordering in #34027.
I don't believe we can programmatically control the fills, especially when we don't know exactly what we'll be dealing with, so to make the ItemGroup
color options only and an inner element of the ToolsPanel
we'd likely need to create one or two new slots and nest those inside an already nested slot in the block editor. It makes the color panel a special case with the Inspector Controls group slots and probably more difficult for consumers.
Given that, I'd be inclined in #34027 to no longer use the ItemGroup
at all.
If that didn't work, maybe ItemGroup is not the right fit for this scenario
Agreed. This is more a hangover of the color panel being updated without consideration or knowledge that there was a PR open for review to move it to using the ToolsPanel
.
I'll update #34027 to remove the use of ItemGroup
for the colors panel.
That is obviously separate to this PR so I think this should be ok to land. Since the decision to adopt the new first/last classname props was accepted we've only been iterating on the storybook example.
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.
Sorry for the delayed response (has a couple of setbacks this week)!
I agree with all of your points — the style rules (and semantics requirements) related to ItemGroup
are very strict, and everything is made even more complicated by the fact that tool panel items can be added via slot-fills.
In the light of our findings I think that the best way forward, at least for now, is to avoid using ItemGroup
in combination with ToolsPanel
— both in #34027 and in this PR (you may even, at this point, re-introduce the contrast checker).
We will also have a look at how to improve the ItemGroup
component (of course separately from this PR), in order to make it more flexible/adaptable to different situations (both in terms of styles and in terms of the list semantics).
That is very weird, tests are passing locally for me as well. Maybe this PR needs to be rebased to get the latest version of all deps? |
a373122
to
62c3769
Compare
After rebasing, the emotion styles were different locally as well. The snapshot was updated in 62c3769 and unit tests are passing. |
That's great news! Sorry if this review is taking longer than expected, but this PR is actually of great value to understand any potential limitations that we have with the current set of components and the way each component interacts with each other (e.g. |
It will take as long as it needs 🙂
The catch, in this case, is twofold. First, we are using the ToolsPanel as an ItemGroup which blurs the lines. Second, we aren't controlling what gets added to the ToolsPanel/ItemGroup. So you are right we do have a limitation there but I don't think that is the fault of either component. The likely cleanest approach is to create additional nested slots so we can in fact control (or set expectations for) what is rendered internally to the ToolsPanel. One slot for the color controls, another for anything else. Those fills are then rendered appropriately (within ItemGroup or not) into the color panel slot which itself is within the Inspector Controls slot and so on. As always thanks for the continued effort and attention to detail 👍 |
Very true. In any case, it served as a very good stress test for
Definitely not a fault, but in my opinion a good indication for which direction we need to look at when developing our components further.
That is definitely an idea, but for now I think we can keep it simple (i.e. let's not use |
966c29b
to
5fe3da5
Compare
I've rebased this PR to resolve conflicts after the merge of the ToolsPanel memorization updates. The unit tests are passing still and storybook examples are all functioning as expected. Once the e2es pass, I'll merge this one. |
Just chiming in to say thank you for making structural improvements to the itemgroup. The component is important both for global styles, and for toolspanels going forward, allowing us to much more carefully manage prominence of individual tool controls, while still making them available. The technical implementation is in good hands here, and from a recent implementation challenge, any flexibility in how these are rendered will be welcome 👌 |
|
Related:
Description
The switch of the Color block support panel to the
ToolsPanel
requires rendering the color controls as the new color dropdowns within anItemGroup
. To maintain the order of controls when some are injected via SlotFill, theToolsPanel
will render placeholder elements that are hidden. These placeholders interfere with the normalItemGroup
styling which relies on CSS pseudo-selectors e.g.:last-child
.To achieve the same result we need to be able to target the first and last displayed
ToolsPanelItem
s. This PR aims to achieve that by applyingfirst
andlast
CSS classes to the appropriateToolsPanelItem
.How has this been tested?
A simple unit test is included with these changes.
Manually:
ToolsPanel
panels in the sidebar and ensure the correct items get the new classesblock.json
file. Test various options like mixing optional and default controls etc.Example ad hoc control for group block
Note this control isn't meant to be fully functional.
Types of changes
New feature.
Checklist:
*.native.js
files for terms that need renaming or removal).