Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -7,12 +7,10 @@ import {
Col,
Container,
GlobalNotices,
Notice,
} from '@automattic/jetpack-components';
import { isSimpleSite } from '@automattic/jetpack-script-data';
import { useViewportMatch } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import clsx from 'clsx';
import { Notice } from '@wordpress/ui';
import { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';
/*
Expand All @@ -39,7 +37,9 @@ import OnboardingTour from '../onboarding-tour';
import buildOptionalMenuItems from './build-optional-menu-items';
import styles from './styles.module.scss';

const GlobalNotice = ( { message, title, options } ) => {
// Named export for testability: it has its own mount effect (Tracks view event) worth
// covering without mounting the whole screen.
export const GlobalNotice = ( { message, title, options } ) => {
const { recordEvent } = useAnalytics();
useEffect( () => {
const tracksArgs = options?.tracksArgs || {};
Expand All @@ -50,23 +50,28 @@ const GlobalNotice = ( { message, title, options } ) => {
} );
}, [ options.id, recordEvent, options?.tracksArgs ] );

const isBiggerThanMedium = useViewportMatch( 'large' );

const actionButtons = options.actions?.map( action => {
return (
<ActionButton key={ action.key || action.label } customClass={ styles.cta } { ...action } />
);
} );

// Watchers pass `hideCloseButton: false` plus `onClose` to override this default.
const hideCloseButton = options.hideCloseButton ?? true;

return (
<div
className={ clsx( styles.notice, {
[ styles[ 'bigger-than-medium' ] ]: isBiggerThanMedium,
} ) }
>
<Notice hideCloseButton={ true } { ...options } title={ title } actions={ actionButtons }>
<div className={ styles.message }>{ message }</div>
</Notice>
<div className={ styles.notice }>
<Notice.Root intent={ options.level || 'info' }>
{ title && <Notice.Title>{ title }</Notice.Title> }
{ /* `render={ <div /> }`: `message` can carry block content, but Description defaults to a `span`. */ }
<Notice.Description className={ styles.message } render={ <div /> }>
{ message }
</Notice.Description>
{ actionButtons && actionButtons.length > 0 && (
<Notice.Actions>{ actionButtons }</Notice.Actions>
) }
{ ! hideCloseButton && <Notice.CloseIcon onClick={ options.onClose } /> }
</Notice.Root>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,6 @@
> div {
position: relative;
margin: 0;
font-size: 16px;
line-height: 24px;
background-color: var(--jp-white);

// notice content
:global(.components-notice__content) {
display: flex;
margin: 0;
padding: 12px 4px;
flex-direction: column;
align-items: flex-start;

@media screen and ( min-width: 960px ) {
gap: 6rem;
}
}

// action button
:global(.is-link) {
Expand All @@ -61,54 +45,7 @@
white-space: nowrap;
}

// X close button
:global(.components-notice__dismiss) {
align-self: center;
}

:global(.components-notice__action) {
color: var(--jp-white);
font-weight: 600;
font-size: 16px;
line-height: 24px;
letter-spacing: -0.01em;
text-decoration: none;
cursor: pointer;
justify-content: center;
align-items: center;
padding: 8px 24px;
margin-left: 0;
margin-top: 24px;
background: #000;
border-radius: var(--jp-border-radius);
height: auto;

@media screen and ( min-width: 960px ) {
margin-left: calc(var(--spacing-base) * 2 + 24px); // 40px
}
}

&.bigger-than-medium {

:global(.components-notice__action) {
margin-left: 0;
margin-top: 0;
white-space: nowrap;
}

:global(.components-notice__content) {
flex-direction: row;
align-items: center;
}
}

@media screen and ( max-width: 600px ) {
padding-top: 24px;

// this is the selector for `.icon-wrapper` in the .notice component
> div:has(svg) {
position: initial;
}

:global(.components-button) {
white-space: normal;
Expand All @@ -123,7 +60,7 @@


.message {
margin-right: var(--spacing-base); // 8px
margin-inline-end: var(--spacing-base); // 8px
flex-grow: 1;
display: flex;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import useAnalytics from '../../../hooks/use-analytics';
import { GlobalNotice } from '../index';

jest.mock( '../../../hooks/use-analytics' );

const mockUseAnalytics = useAnalytics;
const recordEvent = jest.fn();

describe( 'GlobalNotice', () => {
beforeEach( () => {
jest.clearAllMocks();
mockUseAnalytics.mockReturnValue( { recordEvent } );
} );

it( 'records the view event once on mount', () => {
const { rerender } = render(
<GlobalNotice message="Body" title="Title" options={ { id: 'backup-failure' } } />
);

expect( recordEvent ).toHaveBeenCalledTimes( 1 );
expect( recordEvent ).toHaveBeenCalledWith( 'jetpack_myjetpack_global_notice_view', {
notice_id: 'backup-failure',
} );

// A re-render with the same notice id must not re-fire the view event.
rerender( <GlobalNotice message="Body" title="Title" options={ { id: 'backup-failure' } } /> );
expect( recordEvent ).toHaveBeenCalledTimes( 1 );
} );

it( 'hides the close button by default', () => {
render( <GlobalNotice message="Body" title="Title" options={ { id: 'seo' } } /> );

expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
} );

it( 'shows the close button and calls onClose when a watcher overrides it', async () => {
const onClose = jest.fn();
render(
<GlobalNotice
message="Body"
title="Title"
options={ { id: 'backup-failure', hideCloseButton: false, onClose } }
/>
);

await userEvent.click( screen.getByRole( 'button' ) );
expect( onClose ).toHaveBeenCalledTimes( 1 );
} );
} );
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
/**
* External dependencies
*/
import {
AdminPage,
Col,
Container,
AiIcon,
getRedirectUrl,
Notice,
} from '@automattic/jetpack-components';
import { AdminPage, Col, Container, AiIcon, getRedirectUrl } from '@automattic/jetpack-components';
import { Button, Card } from '@wordpress/components';
import { createInterpolateElement } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { Icon, plus, help, check } from '@wordpress/icons';
import { Link } from '@wordpress/ui';
import { Link, Notice } from '@wordpress/ui';
import clsx from 'clsx';
import debugFactory from 'debug';
import { useCallback, useState, useEffect } from 'react';
Expand Down Expand Up @@ -311,22 +304,22 @@ export default function () {
<div className={ styles[ 'product-interstitial__section-wrapper' ] }>
{ showNotice && (
<div className={ styles[ 'product-interstitial__ai-notice' ] }>
<Notice
actions={
tierPlansEnabled
? [
<Button key="upgrade" isPrimary onClick={ upgradeClickHandler }>
{ showRenewalNotice ? renewalNoticeCta : upgradeNoticeCta }
</Button>,
]
: {}
}
onClose={ onNoticeClose }
level={ showRenewalNotice ? 'warning' : 'error' }
title={ showRenewalNotice ? renewalNoticeTitle : upgradeNoticeTitle }
>
{ showRenewalNotice ? renewalNoticeBody : upgradeNoticeBody }
</Notice>
<Notice.Root intent={ showRenewalNotice ? 'warning' : 'error' }>
<Notice.Title>
{ showRenewalNotice ? renewalNoticeTitle : upgradeNoticeTitle }
</Notice.Title>
<Notice.Description>
{ showRenewalNotice ? renewalNoticeBody : upgradeNoticeBody }
</Notice.Description>
{ tierPlansEnabled && (
<Notice.Actions>
<Button key="upgrade" isPrimary onClick={ upgradeClickHandler }>
{ showRenewalNotice ? renewalNoticeCta : upgradeNoticeCta }
</Button>
</Notice.Actions>
) }
<Notice.CloseIcon onClick={ onNoticeClose } />
</Notice.Root>
</div>
) }
<h2 className={ styles[ 'product-interstitial__section-heading' ] }>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Button, Notice } from '@automattic/jetpack-components';
import { Button } from '@automattic/jetpack-components';
import { __ } from '@wordpress/i18n';
import { Notice } from '@wordpress/ui';
import { useCallback, useEffect } from 'react';
import useSeoOptIn from '../../data/use-seo-opt-in';
import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state';
Expand Down Expand Up @@ -54,16 +55,15 @@ export default function SeoOptInCard() {
];

return (
<Notice
level="info"
hideCloseButton={ true }
title={ __( 'A fresh way to manage your SEO', 'jetpack-my-jetpack' ) }
actions={ actions }
>
{ __(
'Jetpack SEO now has a dedicated dashboard to manage sitemaps, search-engine visibility, social previews, and site verification — all in one place. Switch over whenever you like; your existing settings come with you.',
'jetpack-my-jetpack'
) }
</Notice>
<Notice.Root intent="info">
<Notice.Title>{ __( 'A fresh way to manage your SEO', 'jetpack-my-jetpack' ) }</Notice.Title>
<Notice.Description>
{ __(
'Jetpack SEO now has a dedicated dashboard to manage sitemaps, search-engine visibility, social previews, and site verification — all in one place. Switch over whenever you like; your existing settings come with you.',
'jetpack-my-jetpack'
) }
</Notice.Description>
<Notice.Actions>{ actions }</Notice.Actions>
</Notice.Root>
);
}
4 changes: 4 additions & 0 deletions projects/packages/my-jetpack/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Notices: Restyle My Jetpack dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/backup/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/boost/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: enhancement

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/protect/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/search/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/social/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/stats/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
4 changes: 4 additions & 0 deletions projects/plugins/videopress/changelog/myjp-notice-wpui
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

My Jetpack: Restyle dashboard notices to match the WordPress design system.
Loading