-
Notifications
You must be signed in to change notification settings - Fork 2k
Create Callout
component for displaying splash over the Deployments route
#103414
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
Conversation
Jetpack Cloud live (direct link)
Automattic for Agencies live (direct link)
|
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: App Entrypoints (~169 bytes added 📈 [gzipped])
Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used. Legend What is parsed and gzip size?Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory. Generated by performance advisor bot at iscalypsofastyet.com. |
CallOut
component for displaying splash over the Deployments route
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.
@jameskoster @jasmussen I'm starting to implement DES-52. This PR is super early, but I definitely have some component-library type questions already.
Lego's focus is definitely getting the hosting dashboard off the ground, not necessarily fully implementing every permutation of the CallOut
. Specifically the hosting dashboard doesn't have a need for the "full bleed" or "highlighted" variants right now. But I still want to make sure what I do implement is good enough to go in the blessed set of @automattic/
components.
/** | ||
* A short paragraph providing supporting context or details to reinforce the title. | ||
*/ | ||
children?: ReactNode; |
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 originally had separate props for description
, actions
, etc. This works for the example used in the CallOut
design spec.
However the dashboard mockups use two paragraphs.
So that means there are two <Text>
elements.
Since all elements on the left are vertically stacked, with the same gap
between each, does it make more sense to just take an arbitrary children
prop, and let the consumer use <Text>
and <Button>
elements as they please? The VStack
makes sure they'll be arranged correctly.
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 using children
this way seems like a reasonable choice, especially with wanting to reinforce a consistent vertical gap.
A couple alternatives that came to mind, but don't seem like great fits either:
description
prop and expecting the consumer to render their ownHStack
if needing multiple paragraphs. Hard to enforce consistency.description
accepting an array of either<Text>
elements or strings. Arrays of elements aren't very ergonomic since they requirekey
s, and arrays of strings might not support the type of flexibility we want in content (links, emphasis, etc.)
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 not have description
with ReactNode
? The consumer could still pass multiple items with <>
and get the spacing from the internal VStack, and it's a bit more specific to where the content goes IMO.
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 not have
description
withReactNode
?
IMO that basically is a children
prop, except with a children
you don't need to wrap in <>
when you've got multiple.
Perhaps an explicit prop name is better for enforcing consistent usage of the component though?
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 to @ntsekouras 's point, when using children
it should be obvious where those children would be rendered, and I'm not sure it's inherently obvious that it'd be rendered below the heading and above the actions.
Although I think it's the obvious place if you think about it for more than a few seconds, just not immediately obvious. So a soft opinion from me on children
vs. description
, anyways.
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 should be obvious where those children would be rendered
I think that's a fair point. I'll try adding two separate description
and actions
for the sake of making the props explanatory.
> | ||
<VStack justify="flex-start" alignment="flex-start" spacing="4"> | ||
{ icon && <Icon icon={ icon } /> } | ||
<Heading level="6" className="call-out-title"> |
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.
Choosing heading level feels fraught. How will I know in the context which level is correct?
On the other hand, having screen readers treat it as regular paragraph text also doesn't seem right.
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.
Choosing heading level feels fraught. How will I know in the context which level is correct? On the other hand, having screen readers treat it as regular paragraph text also doesn't seem right.
Maybe this should be a required prop provided when rendered by a consumer? (e.g. headingLevel
)
Though on the semantics of how a call-out would be used, I wonder if it's something where the content isn't necessarily part of the document structure, but rather more like an <aside>
, that can associate the callout heading using aria-labelledby
. I don't feel entirely confident that this wouldn't still be most appropriate to mark up as a heading.
Separately, I wonder if the <Heading>
component from @wordpress/components
could manage React context so that we could assign a heading level relative to the closest heading (e.g. + 1
from the current ancestor heading level). I see that it internally does have some context behaviors, so maybe it is possible? (or at least could be extended to support)
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 thought about the <aside>
and aria-labelledby
idea, but I think semantically heading is still correct. In the case of the v2 dashboard, when the user focuses in to an area covered by a call-out, it should just appear to be that the call-out message is the main/only content of the area. You wouldn't expect to have an <aside>
be the main content, just a heading and a paragraph telling you that this nav area needs a purchase.
It'd be cool if internally <Heading>
could automatically set the correct level. Without that I think h6
is probably a good bet and I've added an optional headingLevel
prop if a particularly assiduous developer would like to set a more correct one.
@import "@wordpress/base-styles/variables"; | ||
|
||
.call-out { | ||
max-width: 600px; |
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 design spec has guidance for showing 4 or 6 column wide call outs. Are these column widths encoded anywhere yet?
Should CallOut
have a cols
prop which sets a max width?
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 This PR is still early, but I just wanted to point out to you where I was placing the CallOut
in the React tree.
} ) { | ||
return ( | ||
<VStack spacing={ 8 } className="dashboard-page-layout" style={ sizes[ size ] }> | ||
{ callOut && <CallOutParent>{ callOut }</CallOutParent> } |
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've added the CallOut
directly to the PageLayout
. I'd prefer to experiment with call outs before wrapping them into any sort of official layout. However it needs the CallOut
needs to render over the page heading, and this is where the heading gets rendered.
@@ -50,4 +53,12 @@ function PageLayout( { | |||
); | |||
} | |||
|
|||
function CallOutParent( { children }: { children: React.ReactNode } ) { |
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 not using a modal to implement the "sneeze guard". It looks like a modal, but it's a bit different. For example pressing escape doesn't navigate you away from the page.
This sort of call out will need special focus management though, to ensure the user doesn't tab into the content behind the "sneeze guard". I'll see if there's anything I can commandeer from core for this.
Sounds good to me, I like to start with as little as possible and grow in variants/densities either when it becomes necessary or when there's a clearly identified future need. |
> | ||
<VStack justify="flex-start" alignment="flex-start" spacing="4"> | ||
{ icon && <Icon icon={ icon } /> } | ||
<Heading level="6" className="call-out-title"> |
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.
Choosing heading level feels fraught. How will I know in the context which level is correct? On the other hand, having screen readers treat it as regular paragraph text also doesn't seem right.
Maybe this should be a required prop provided when rendered by a consumer? (e.g. headingLevel
)
Though on the semantics of how a call-out would be used, I wonder if it's something where the content isn't necessarily part of the document structure, but rather more like an <aside>
, that can associate the callout heading using aria-labelledby
. I don't feel entirely confident that this wouldn't still be most appropriate to mark up as a heading.
Separately, I wonder if the <Heading>
component from @wordpress/components
could manage React context so that we could assign a heading level relative to the closest heading (e.g. + 1
from the current ancestor heading level). I see that it internally does have some context behaviors, so maybe it is possible? (or at least could be extended to support)
/** | ||
* A short paragraph providing supporting context or details to reinforce the title. | ||
*/ | ||
children?: ReactNode; |
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 using children
this way seems like a reasonable choice, especially with wanting to reinforce a consistent vertical gap.
A couple alternatives that came to mind, but don't seem like great fits either:
description
prop and expecting the consumer to render their ownHStack
if needing multiple paragraphs. Hard to enforce consistency.description
accepting an array of either<Text>
elements or strings. Arrays of elements aren't very ergonomic since they requirekey
s, and arrays of strings might not support the type of flexibility we want in content (links, emphasis, etc.)
return ( | ||
<PageLayout title={ __( 'Deployments' ) } callOut={ showCallOut && <DeploymentsCallOut /> } /> | ||
); |
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.
With how we're using the call-out as a modal-like element not part of the layout's VStack
, could we render it adjacent to the page layout rather than inside it? To limit prop overload on PageLayout
.
e.g.
return ( | |
<PageLayout title={ __( 'Deployments' ) } callOut={ showCallOut && <DeploymentsCallOut /> } /> | |
); | |
return ( | |
<> | |
<PageLayout title={ __( 'Deployments' ) } /> | |
{ showCallOut && <DeploymentsCallOut /> } | |
</> | |
); |
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.
From an a11y perspective I suppose the Callout
will need to be outside because PageLayout
contents will need to be inert
or aria-hidden="true"
.
I guess it will also need to trap focus inside the 'modal' too, but still provide access back to the main nav.
Alternatively we could just use an actual modal dialog. I suspect this would be much simpler 😅
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 actually not sure we'll have any content outside the callout, most endpoints ... will fail unless the user actually has the right plans... so the content outside the callout probably needs to be "fake". We might need a system for that.
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 good point, I prefer this.
The downside is the call-out will no longer have access to the page layout's height, which it can use to ensure all the components on the page get covered, without resorting to position: fixed
.
6d1d15e
to
891176d
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.
Forgive me for this very nitpicky comment, feel free to ignore, but is it Callout
or CallOut
? I believe we have been inconsistently using both terms in discussions (Figma and GH)
The DS figma uses
|
891176d
to
643f059
Compare
This PR modifies the release build for the following Calypso Apps: For info about this notification, see here: PCYsg-OT6-p2
To test WordPress.com changes, run |
No strong opinions on Callout or CallOut, though I found this:
|
643f059
to
65235f3
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.
ref: React.ForwardedRef< HTMLElement > | ||
) { | ||
return ( | ||
<Card ref={ ref } className={ `call-out call-out--${ variant }` }> |
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.
Based on my understanding reading various DS PRs, I think we're supposed to prefix the classname with a8c-components-
. See e.g. #102998 (comment)
Yes I think so. Can it be built into the component as a container query, using the |
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.
Great work here!
We just discussed in the Design Systems call last week that we'd like to defer adding components to @automattic/components
itself until it really has an actual use case outside of the initial local use case. This is to prevent spending too much time on premature abstractions, and to avoid the huge increase in technical maintenance cost once it lands in the package. The rest of the process will remain largely the same, such as involving the DS team from the early stages, and keeping it visible as a proposed addition to the DS. We just want to keep the implementation local until it really needs to be elevated to the actual package. Would that be the case here?
/** | ||
* The level of the heading to be used for the title. | ||
* @default 6 | ||
*/ | ||
headingLevel?: ComponentProps< typeof Heading >[ 'level' ]; |
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.
Note that level
also changes the semantic heading level as well as the visual, so simply exposing this prop without the ability to override the semantic level is problematic. If anything you probably want to hardcode the level
and expose the as
prop so only the semantics can be changed.
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.
exposing this prop without the ability to override the semantic level is problematic
I don't understand, the reason I'm exposing this prop is specially to allow the level to be set semantically. The <Heading>
component doesn't change visually, so this is just for the semantics.
We could expose an as
, but it feels appropriate that we force the title to be a heading.
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.
Oh, I see you're overriding the font size in CSS. The Heading
component was apparently intended to provide the typography sizing scale ("Heading
renders headings and titles using the library's typography system"), but since that scale is neither aligned with the current WP sizing scale nor are we even trying to leverage here, there's probably no reason to use that component. In fact we are actively discouraging style overrides like this.
Perhaps we might as well directly accept h2
thru h6
elements for the title? The default h6
is quite arbitrary and I can see consumers forgetting to change it.
I don't have the full context or a sense of urgency, but I've had conversations with designers about using this component outside of the host dashboard project. cc @nuriapenya and @marina.matijaca1 for input. Where are y'all planning to use |
65235f3
to
8f5e571
Compare
CallOut
component for displaying splash over the Deployments routeCallout
component for displaying splash over the Deployments route
@fushar @jameskoster when the content is stacked vertically and the image takes on it's natural height it just dominates. The design spec actually calls for the content to stay presented horizontally, but it's demo content is much shorter. The two paragraphs worth of copy really makes it look wrong presented horizontally. I looks a bit dry, but I think the fix on small screens might just be to hide the illustration. |
8a96dcc
to
0fb20c4
Compare
@ellatrix I've just rebased your fancy After rebasing the It makes sense that transitions wouldn't be compatible with overlapping elements. Does it make sense to you that we should disable the transitions when the page the user is coming from or going to has an overlay? Have you got any ideas for how we could add a test to the selectors in |
0fb20c4
to
7e9d33a
Compare
Yeah that does look quite heavy. Maybe it's okay for the layout to remain horizontal. What do you think @matt-west? I'm going to cc @annchichi too since you've been looking at this component recently; did you have any feelings about how the layout would adapt as the container shrinks? |
I'm tempted to drop the graphic on mobile. In the hosting dashboard, the graphic is complimentary to the main content, but not really essential. That might not always be the case though if the graphic is used to show a specific feature/animation. |
@jameskoster @matt-west This is what it looks like horizontal on an iPhone 12 Pro if I also decrease the card's padding and column gap to (ignore misalignment along the right, the dashboard appears to have a layout bug on small screens) I don't think it looks that bad. Although this graphic just happens to crop really well at this aspect ratio. The staging site illustration wouldn't work as well |
c745145
to
bfdfb0d
Compare
I'm skeptical of that, not because the idea is bad, but because ideally we think in generic components, meaning if we decide to drop the graphic from mobile, we're essentially doing that for all callouts going forward. We just need to be sure that's what we want. |
@jasmussen That's fair. |
@ellatrix could you help with this comment? #103414 (comment) Thanks! |
@p-jackson I think the CleanShot images are broken in your comments ![]() |
const showCallout = | ||
( ! site.plan || ! hasDeploymentsFeature( site.plan ) ) && | ||
// Show to all atomic sites regardless of plan status e.g. an atomic site in the revert queue | ||
// after a plan downgrade will continue to see Deployments until the revert is complete | ||
! site.is_wpcom_atomic; |
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 condition seems to be different from the original logic in v1:
const isActiveAtomicSite = isAtomicSite && ! isPlanExpired; |
showCallout
should be the inverse of the above condition. 🤔
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.
Interesting, I think it's sort of annoying when an atomic site can't see the atomic features. But that might just be a dev thing 😃 It makes sense to copy v1 business logic unless making an explicit proposal to change it.
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.
IIRC the "features" in the wpcom backend code is tied to plans, not the atomic state.
We can iterate on it later when we actually implement the Deployments screen.
<DataViewsCard> | ||
<></> | ||
</DataViewsCard> |
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 not sure how this works. Shouldn't the content of main
depends on showCallout
? As in, if showCallout
is true, we should render a blurred placeholder content here; otherwise, we render the real component (the real Deployments page). How would we handle that?
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.
Oooh that's an interesting point. I was imagining the placeholder content would use the same elements as the real page, and it would be the data that was swapped out for dummy data. But I see now that the placeholder content could be totally different elements.
I like the way that in this situation the placeholder is using the same elements as the live data. But shall we wait until we have some placeholder components before we settle on what the right abstraction is? The main thing about this PR is that the CalloutOverlay
needs to own the placeholder elements so that it can correctly blur them.
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 see now that the placeholder content could be totally different elements.
Yeah, this is what I imagine, too:
- Deployments renders deployment list with many rows of fake data
- Monitoring renders fake graphs
placeholder content would use the same elements as the real page, and it would be the data that was swapped out for dummy data.
Not sure but I think it's harder to pass around the showCallout
state to various downstream components, instead of just coming up with a fake static content in the first place.
Not a blocker though, we can change the logic later, just wanted to call out (pun slightly intended)!
Maybe we introduce a property to allow consumers to hide the illustration at a certain size? |
bfdfb0d
to
46e7624
Compare
I think this PR is in a good place to merge.
|
Like the "experimental" HStack and VStack, these are fine to use.
46e7624
to
8b09b64
Compare
I've summarised the mobile layout issues in DES-52 |
This Pull Request is now available for translation here: https://translate.wordpress.com/deliverables/17505574 Some locales (Hebrew) have been temporarily machine-translated due to translator availability. All other translations are usually ready within a few days. Untranslated and machine-translated strings will be sent for translation next Monday and are expected to be completed by the following Friday. Thank you @p-jackson for including a screenshot in the description! This is really helpful for our translators. |
DES-52
Proposed Changes
Adds the
<Callout>
component todashboard/components/
.dhW7zYBamXJIeJIE3wy5f4-fi-3510_117161
Also adds a callout to the Deployments page on the /v2 dashboard when the site doesn't have the necessary plan.
This PR only implements the callout features necessary for the hosting dashboard callouts. The design system also has a full-bleed image variation, but none of the dashboard mockups use it.
Seems best to implement things once we have a use case for them.
The blurred background (
<CalloutOverlay>
) is kept separate from<Callout>
. I think this is best because the exact way we need to obscure the page content depends heavily on the specifics of our app's page layout. Other users of the callout design pattern may be embedding it directly within content without any overlay. It's just presented as a generic card in the DS spec.Since the deployment page is empty as of now, the easiest way to test focus management is in The
CalloutOverlay
storybook. The reason I haven't reused the<Modal>
component is because this isn't really a modal. You can tab from the nav bars into the callout and back again.CleanShot.2025-05-21.at.21.08.28.mp4
Why are these changes being made?
Rather than hiding menus when a site doesn't have the necessary plan, this call-out pattern gives the user some impression of what the feature looks like (behind a blurred background) and provides instructions on next steps if they're interested.
Testing Instructions
To check storybook run
yarn storybook:start
in the wp-calypso directory.To test the dashboard
calypso.localhost:3000/v2
When testing focus management, you should open the accessibility tree in the dev tools and confirm that the content under the overlay disappears from the a11y tree when it is covered.
Pre-merge Checklist