Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a67f5be
Jetpack notices: back SimpleNotice with the @wordpress/ui Notice
CGastrell Sep 4, 2026
51d0a72
Jetpack notices: restore the spacing, underline and padding the class…
CGastrell Sep 7, 2026
2de3385
Jetpack notices: give SimpleNotice a title slot and use it where one …
CGastrell Sep 7, 2026
a996360
Jetpack notices: return null from the notice components that render n…
CGastrell Sep 7, 2026
30ea3d6
Jetpack notices: stop the toasts collapsing on desktop
CGastrell Sep 7, 2026
946f4a2
Jetpack notices: draw the toast shadow at the design system's elevation
CGastrell Sep 7, 2026
8bf8616
Jetpack notices: stop notices announcing themselves, and pad the two …
CGastrell Sep 7, 2026
b3efb09
Jetpack notices: lift the conflicting-SEO-plugin notice out of the sc…
CGastrell Sep 7, 2026
5ba5bc9
Jetpack notices: don't render the SEO banner's padded wrapper when th…
CGastrell Sep 7, 2026
422ce4e
Jetpack notices: drop the broken-link icon from the connection errors
CGastrell Sep 7, 2026
4e85039
Jetpack notices: keep `display` hiding the notice rather than unmount…
CGastrell Sep 8, 2026
c233ade
Merge remote-tracking branch 'origin/trunk' into update/simple-notice…
CGastrell Sep 8, 2026
1bc9995
Merge remote-tracking branch 'origin/trunk' into update/simple-notice…
CGastrell Sep 8, 2026
4be8624
Merge remote-tracking branch 'origin/trunk' into update/simple-notice…
CGastrell Sep 8, 2026
ace63c7
Jetpack notices: correct what happens to the underline rule upstream
CGastrell Sep 8, 2026
941ede6
Jetpack notices: trim the review nits before merge
CGastrell Sep 9, 2026
1984c63
Jetpack notices: say why the toast elevation is copied
CGastrell Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@use "../../scss/calypso-mixins";

.global-notices {
text-align: right;
pointer-events: none;

z-index: z-index.z-index("root", ".global-notices");
Expand Down Expand Up @@ -39,44 +38,26 @@
}
}

.global-notices .dops-notice {
flex-wrap: nowrap;
/* Targets children: `Notice.Root`'s own class is a hashed CSS-module name. */
.global-notices > * {
margin-bottom: 0;
text-align: left;
pointer-events: auto;
border-radius: 0;
box-shadow:
0 2px 5px rgba(0, 0, 0, 0.2),
0 0 56px rgba(0, 0, 0, 0.15);

.dops-notice__icon-wrapper {
border-radius: 0;
}

@include calypso-mixins.breakpoint( ">660px" ) {
display: flex;
overflow: hidden;
margin-bottom: 24px;
border-radius: 3px;

.dops-notice__icon-wrapper {
border-radius: 3px 0 0 3px;
}
}
}

.global-notices .dops-notice a.dops-notice__action {
/* A size-contained child reports no width in this shrink-to-fit
container, collapsing the toast. Only titled notices query it. */
container-type: normal;

@include calypso-mixins.breakpoint( ">660px" ) {
font-size: 14px;
padding: 13px 16px;
}
}
/* The container is click-through, so each notice opts back in. */
pointer-events: auto;

.global-notices .dops-notice__dismiss {
flex-shrink: 0;
/* `@wordpress/ui`'s medium elevation. Copied, not referenced: its
`--_wp-ui-elevation-md` is redeclared per component, never published. */
box-shadow:
0 2px 3px rgba(0, 0, 0, 0.05),
0 4px 5px rgba(0, 0, 0, 0.04),
0 12px 12px rgba(0, 0, 0, 0.03),
0 16px 16px rgba(0, 0, 0, 0.02);

@include calypso-mixins.breakpoint( ">660px" ) {
padding: 13px 16px 0;
margin-bottom: 24px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const DeprecationNotice = ( { dismissNotice, message, link, linkText, title } )
return (
<SimpleNotice
status="is-warning"
title={ title }
dismissText={ __( 'Dismiss', 'jetpack' ) }
onDismissClick={ dismissNotice }
>
{ title && <div style={ { fontWeight: 600 } }>{ title }</div> }
<div>{ message }</div>
{ link && (
<Link openInNewTab href={ link }>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class DismissableNotices extends Component {
};

render() {
return <div>{ this.renderNotices() }</div>;
return this.renderNotices() || null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export default class ErrorNoticeCycleConnection extends Component {
showDismiss={ false }
text={ this.props.text }
status={ 'is-error' }
icon={ 'link-break' }
display={ this.props.display }
>
<NoticeActionReconnect
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getRedirectUrl } from '@automattic/jetpack-components';
import { createInterpolateElement } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { Stack } from '@wordpress/ui';
import PropTypes from 'prop-types';
import { Component } from 'react';
import { connect } from 'react-redux';
Expand Down Expand Up @@ -190,7 +191,7 @@ class JetpackNotices extends Component {
const isUserConnectScreen = this.props.location.pathname.startsWith( '/connect-user' );

return (
<div aria-live="polite">
<Stack direction="column" gap="xl" aria-live="polite">
<NoticesList />
{ this.props.siteConnectionStatus &&
this.props.userCanConnectSite &&
Expand Down Expand Up @@ -255,7 +256,7 @@ class JetpackNotices extends Component {
linkText={ link.label }
/>
) ) }
</div>
</Stack>
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { __ } from '@wordpress/i18n';
import { Stack } from '@wordpress/ui';
import PropTypes from 'prop-types';
import { Component, Fragment } from 'react';
import SimpleNotice from 'components/notice';
Expand Down Expand Up @@ -33,7 +34,6 @@ export default class JetpackConnectionErrors extends Component {
<SimpleNotice
text={ message }
status={ 'is-error' }
icon={ 'link-break' }
showDismiss={ false }
display={ this.props.display }
>
Expand All @@ -49,7 +49,6 @@ export default class JetpackConnectionErrors extends Component {
<SimpleNotice
text={ message }
status={ 'is-error' }
icon={ 'link-break' }
showDismiss={ false }
display={ this.props.display }
/>
Expand All @@ -66,11 +65,7 @@ export default class JetpackConnectionErrors extends Component {
// Add secondary action if available
if ( errorData.secondary_action_url && errorData.secondary_action_label ) {
actions.push(
<NoticeAction
key="secondary"
href={ errorData.secondary_action_url }
variant="secondary"
>
<NoticeAction key="secondary" href={ errorData.secondary_action_url }>
{ errorData.secondary_action_label }
</NoticeAction>
);
Expand All @@ -80,7 +75,6 @@ export default class JetpackConnectionErrors extends Component {
<SimpleNotice
text={ message }
status={ 'is-error' }
icon={ 'link-break' }
showDismiss={ false }
display={ this.props.display }
>
Expand Down Expand Up @@ -128,6 +122,10 @@ export default class JetpackConnectionErrors extends Component {
}
}

return Object.values( errorsToDisplay ).map( error => this.renderOne( error ) );
return (
<Stack direction="column" gap="xl">
{ Object.values( errorsToDisplay ).map( error => this.renderOne( error ) ) }
</Stack>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,10 @@ const NoticeActionReconnect = props => {
analytics.tracks.recordEvent( 'jetpack_termination_error_notice_view', getEventProps() );
}, [ getEventProps ] );

return (
<NoticeAction icon={ props.icon } onClick={ handleDisconnectClick }>
{ props.children }
</NoticeAction>
);
return <NoticeAction onClick={ handleDisconnectClick }>{ props.children }</NoticeAction>;
};

NoticeActionReconnect.propTypes = {
icon: PropTypes.string,
errorCode: PropTypes.string,
errorData: PropTypes.object,
action: PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ class JetpackStateNotices extends Component {
};

render() {
return <div>{ this.state.showNotice ? this.renderContent() : null }</div>;
return this.state.showNotice ? this.renderContent() ?? null : null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ export class StaticWarning extends Component {

render() {
return (
<SimpleNotice showDismiss={ false } status="is-warning">
<div>
#HEADER_TEXT#
<br />
#TEXT#
</div>
<SimpleNotice showDismiss={ false } status="is-warning" title="#HEADER_TEXT#">
<div>#TEXT#</div>
</SimpleNotice>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ describe( 'DevVersionNotice', () => {
expect(
screen.getByText( 'You are currently running a development version of Jetpack.' )
).toBeInTheDocument();
expect( screen.getByRole( 'link', { name: 'Submit Beta feedback' } ) ).toBeInTheDocument();
// The accessible name carries the design system's "(opens in a new tab)" suffix.
expect( screen.getByRole( 'link', { name: /Submit Beta feedback/ } ) ).toBeInTheDocument();
} );
} );
80 changes: 31 additions & 49 deletions projects/plugins/jetpack/_inc/client/components/notice/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Notice } from '@wordpress/ui';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { Component } from 'react';
import Gridicon from 'components/gridicon';
import onKeyDownCallback from 'utils/onkeydown-callback';

import './style.scss';

const noop = () => {};

Expand All @@ -30,8 +27,9 @@ export default class SimpleNotice extends Component {
PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ),
PropTypes.arrayOf( PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ) ),
] ),
icon: PropTypes.string,
title: PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ),
onDismissClick: PropTypes.func,
dismissText: PropTypes.string,
className: PropTypes.string,
display: PropTypes.bool,
};
Expand All @@ -50,28 +48,19 @@ export default class SimpleNotice extends Component {
}
}

getIcon = () => {
let icon;

getIntent = () => {
switch ( this.props.status ) {
case 'is-info':
icon = 'info';
break;
return 'info';
case 'is-success':
icon = 'checkmark';
break;
return 'success';
case 'is-error':
icon = 'notice';
break;
return 'error';
case 'is-warning':
icon = 'notice';
break;
return 'warning';
default:
icon = 'info';
break;
return 'neutral';
}

return icon;
};

clearText = text => {
Expand All @@ -85,45 +74,38 @@ export default class SimpleNotice extends Component {
const {
children,
className,
icon,
isCompact,
onDismissClick,
showDismiss = ! isCompact, // by default, show on normal notices, don't show on compact ones
status,
text,
title,
dismissText,
display,
} = this.props;
const classes = clsx( 'dops-notice', status, className, {
'is-compact': isCompact,
'is-dismissable': showDismiss,
'is-hidden': ! display,
} );

// `text` marks the caller as using the two-slot form, where children are the
// actions. Without it, children are the body.
const body = text ? this.clearText( text ) : children;
const actions = text ? children : null;

return (
<div className={ classes }>
<span className="dops-notice__icon-wrapper">
<Gridicon className="dops-notice__icon" icon={ icon || this.getIcon() } size={ 24 } />
</span>
<span className="dops-notice__content">
<span className="dops-notice__text">{ text ? this.clearText( text ) : children }</span>
</span>
{ text ? children : null }
{ showDismiss && (
<span
role="button"
onKeyDown={ onKeyDownCallback( onDismissClick ) }
tabIndex="0"
className="dops-notice__dismiss"
onClick={ onDismissClick }
>
<Gridicon icon="cross" size={ 24 } />
<span className="dops-notice__screen-reader-text screen-reader-text">
{ dismissText }
</span>
</span>
<Notice.Root
intent={ this.getIntent() }
// `Notice.Root`'s own class is a CSS-module hash, so page styles need
// `jp-notice`. `is-hidden` keeps `display` hiding the notice rather than
// unmounting it: children like NoticeActionReconnect track on mount.
className={ clsx( 'jp-notice', className, { 'is-hidden': ! display } ) }
// The legacy notice never announced. Several of these are permanent, and
// the ones that should announce already sit in an aria-live container.
spokenMessage={ null }
>
{ title ? <Notice.Title>{ title }</Notice.Title> : null }
{ ( body || body === 0 ) && (
<Notice.Description render={ <div /> }>{ body }</Notice.Description>
) }
</div>
{ actions ? <Notice.Actions>{ actions }</Notice.Actions> : null }
{ showDismiss && <Notice.CloseIcon label={ dismissText } onClick={ onDismissClick } /> }
</Notice.Root>
);
}
}
Loading
Loading