-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat(menu): add Menu.PlainButton as a minimally styled Menu button #1516
Conversation
Codecov Report
@@ Coverage Diff @@
## next #1516 +/- ##
==========================================
- Coverage 91.97% 91.88% -0.09%
==========================================
Files 284 284
Lines 4300 4303 +3
Branches 793 793
==========================================
- Hits 3955 3954 -1
- Misses 320 324 +4
Partials 25 25
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
Alternatively, we can check if Another alternative would be to not provide further styling on top of the and just have recipes for styling and include chevron icon with the button text. However, mocks and docs for menu are currently opinionated as such so I am hesitant to remove them |
size-limit report 📦
|
return ( | ||
<HeadlessMenu.Button | ||
as={ClickableStyle} |
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.
Originally was <ClickableStyle>
but changed it to <Button>
since the alternative (link) is not an appropriate trigger for a menu
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 this makes sense. The rationale here was that, since Headless gives you a <button>
, this composed a styled button in the same way Button
does internally. If we give it Button
directly, looks like we get the same result. using ClickableStyle
composes one less thing I guess
What things can be used as a menu trigger?
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.
So far we have text in the mocks, and the vertical dots icon in GST
src/components/Menu/Menu.tsx
Outdated
/> | ||
</> | ||
</HeadlessMenu.Button> | ||
<HeadlessMenu.Button as={Button} status={status || 'neutral'} {...other} /> |
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.
Kept as={Button}
for accessibility features (such as target size) and EDS variants
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.
Dang, Headless's Menu.Button isn't necessarily a button? 😬
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 is but I was thinking keeping it EDS themed hence the EDS Button
src/components/Menu/Menu.tsx
Outdated
/> | ||
</> | ||
</HeadlessMenu.Button> | ||
<HeadlessMenu.Button as={Button} status={status || 'neutral'} {...other} /> |
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.
Dang, Headless's Menu.Button isn't necessarily a button? 😬
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.
nice, I really like this updated solution! clean and nicely leverages existing styling 👍 had 1 question about the status
prop & wanted a look from @booc0mtaco but otherwise good to approve
src/components/Menu/Menu.stories.tsx
Outdated
}, | ||
}; | ||
|
||
export const DotsVerticalButton: StoryObj<MenuProps> = { |
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.
nit
export const DotsVerticalButton: StoryObj<MenuProps> = { | |
export const WithVerticalDotsButton: StoryObj<MenuProps> = { |
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.
@anniehu4 @jinlee93 we don't have a written convention for these but i have been using prefixes With
if it demonstrates a composition, and When
if the story exists to show some set of prop values.
Also, all With
stories might actually be a signal that the story is actually a recipe in the format, "here's how you can combine ______ and _____ to get a _______"
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.
WithVerticalDots does sound like a recipe, and could be redundant with the WithCustomButton story. Consider removing into a recipe?
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.
@jinlee93 the custom ones using fpo
stuff make sense per component, as they show where placeholders belong. We can add the recipe showing how to use the vertical dots as/with a custom button and keep both
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.
Moved vertical dots story to its own recipe, since there already is a story using fpo block for button
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! would be good to document that convention somewhere
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 did have an alternative suggestion for how we might compose custom buttons, mostly when this was a breaking change. This seems more straight-forward and extends default behavior!
src/components/Menu/Menu.stories.tsx
Outdated
}, | ||
}; | ||
|
||
export const DotsVerticalButton: StoryObj<MenuProps> = { |
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.
@anniehu4 @jinlee93 we don't have a written convention for these but i have been using prefixes With
if it demonstrates a composition, and When
if the story exists to show some set of prop values.
Also, all With
stories might actually be a signal that the story is actually a recipe in the format, "here's how you can combine ______ and _____ to get a _______"
return ( | ||
<HeadlessMenu.Button | ||
as={ClickableStyle} |
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 this makes sense. The rationale here was that, since Headless gives you a <button>
, this composed a styled button in the same way Button
does internally. If we give it Button
directly, looks like we get the same result. using ClickableStyle
composes one less thing I guess
What things can be used as a menu trigger?
src/components/Menu/Menu.stories.tsx
Outdated
<Menu.Button showExpandIcon={false} status="neutral" variant="icon"> | ||
<div className="fpo !py-0">Menu Button</div> | ||
</Menu.Button> |
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 do believe there is an abstraction that can further simplify this, but I havent had the time to think thru it properly. As a hint tho, since we have control over what Menu.Button
is, we could also specify additional .Button
subcomponents that apply the desired props when used, so that consumers don't have to remember these prop values, class overrides, etc.
For example, something like:
<Menu.PlainButton>
<div className="fpo">... or an icon, or an avatar component, etc.</div>
</Menu.PlainButton>
In this case, whatever is in PlainButton
(coming from a properly accessible design) takes over all the duties, and this essentially exposes the untreated HeadlessUI button.
This wouldn't block the current PR. If such an approach makes sense, it is additive and would mean we could later deprecate anything it would replace.
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.
ooh interesting. I also just realized that the Button variants (especially "icon"
) don't work properly because the menu's built-in button styles are overriding it. Ideally we want to make this "plain" styling case really easy -- Andrew's suggestion would work, or something like <Menu.Button variant="plain">
.
Using the EDS Button underneath still makes sense, but I'm now questioning if we should allow consumers to pass through all Button props ... may be better/less confusing for Menu to fully control the few cases
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'm liking the PlainButton api which just ships the Headless Menu Button. Not sure about the button variants as well. We can just go back to not accepting button props and if customization is wanted, consumers can use the PlainButton?
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.
One other benefit of having a "plain"
variant on Button
/ClickableStyle
is that it can manage implementing the focus ring. Ex:
<Button variant="plain">
<Avatar user={...} />
</Button>
would automatically allow a properly-spaced focus ring clamped around the shape of the children
(or could have Tailwind utilities provided to change the wrapper shape to get a circle). PlainButton
could allow as
so the consumer can implement the entire button if we ship the headless wrapper. I'm not sure I follow the button prop bit, but we can chat thru that if it still is relevant
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.
Think you commented this just as I pushed a commit with PlainButton
haha. I do like the as
prop.
The button prop was allowing MenuButton to accept Button props for further styling, but I removed that since that's possible with PlainButton now.
<Menu.Button | ||
className="!border-transparent-white-0 !bg-transparent-white-0 hover:!bg-button-secondary-neutral-background-hover" | ||
showExpandIcon={false} | ||
variant="icon" | ||
> | ||
<Icon name="dots-vertical" purpose="informative" title="show more" /> | ||
</Menu.Button> |
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.
Related to that other comment, how might we create a sub-component that allows for this bit of code to be much briefer, and specify the proper prop values such that we avoid the boilerplate of lines 22-24
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.
we should especially not need people to pass extra styling to get this case working 😬
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.
Only needs the hover styling now with PlainButton
.menu__plain-button:focus { | ||
@mixin focus; | ||
} | ||
} |
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.
Just some light accessibility styling for the plain button. Can remove if we can assume if usage will always be accessible
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.
@jinlee93 the designs should apply the accessible size, and specifying a minimum will be a visual hint that something is off if the input is too small.
We might also apply some centering in this 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.
4332d69
to
3987a5b
Compare
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.
nice!
@jinlee93 nudge to update the PR title if it's not accurate anymore :) |
Ya will update summary too |
## [11.0.0](v10.0.0...v11.0.0) (2023-03-17) ### ⚠ BREAKING CHANGES * add `indeterminate` prop to <Checkbox> that's separate from `checked` (#1520) ### Features * add `indeterminate` prop to <Checkbox> that's separate from `checked` ([#1520](#1520)) ([d8e2cc4](d8e2cc4)) * **LoadingIndicator:** extract and use SVG animation directly ([#1540](#1540)) ([6e315ea](6e315ea)) * **menu:** add Menu.PlainButton as a minimally styled Menu button ([#1516](#1516)) ([8268d8e](8268d8e)) ### Bug Fixes * actually use our shared prettier config ([c98ea51](c98ea51)) * **Avatar:** loosen props for avatar aria-label component ([#1544](#1544)) ([4ab9183](4ab9183)) * markdown story styling ([#1536](#1536)) ([89eba6b](89eba6b))
EDS-845
Summary:
Test Plan:
edu-stack
ortraject
as a sanity check if changes affect build or deploy, or are breaking, such as token changes, widely used component updates, hooks changes, and major dependency upgrades.