@automattic/jetpack-components 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/build/components/admin-page/index.d.ts +0 -1
  3. package/build/components/admin-page/index.js +2 -9
  4. package/build/components/admin-page/style.module.scss +16 -3
  5. package/build/components/boost-score-bar/index.js +3 -3
  6. package/build/components/gravatar/index.d.ts +58 -0
  7. package/build/components/gravatar/index.js +68 -0
  8. package/build/components/gravatar/style.scss +7 -0
  9. package/build/components/icon-tooltip/index.js +3 -3
  10. package/build/components/icon-tooltip/types.d.ts +4 -3
  11. package/build/components/jetpack-footer/index.js +1 -1
  12. package/build/components/terms-of-service/index.js +10 -10
  13. package/build/components/testimonials/index.js +2 -2
  14. package/build/index.d.ts +2 -1
  15. package/build/index.js +1 -1
  16. package/components/admin-page/index.tsx +4 -16
  17. package/components/admin-page/style.module.scss +16 -3
  18. package/components/boost-score-bar/index.tsx +3 -3
  19. package/components/gravatar/index.tsx +138 -0
  20. package/components/gravatar/style.scss +7 -0
  21. package/components/icon-tooltip/index.tsx +3 -3
  22. package/components/icon-tooltip/types.ts +4 -3
  23. package/components/jetpack-footer/index.tsx +2 -3
  24. package/components/terms-of-service/index.tsx +12 -12
  25. package/components/testimonials/index.tsx +3 -3
  26. package/index.ts +2 -1
  27. package/package.json +22 -4
  28. package/build/components/gridicon/index.d.ts +0 -14
  29. package/build/components/gridicon/index.js +0 -173
  30. package/build/components/gridicon/style.scss +0 -16
  31. package/build/components/gridicon/types.d.ts +0 -39
  32. package/build/components/gridicon/types.js +0 -1
  33. package/components/gridicon/index.tsx +0 -334
  34. package/components/gridicon/style.scss +0 -16
  35. package/components/gridicon/types.ts +0 -48
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Components package releases.
4
4
 
5
+ ## [1.11.0] - 2026-05-11
6
+ ### Added
7
+ - AdminPage: Add admin-page and jetpack-footer sub-path exports for use in wp-build dashboards. [#48510]
8
+ - Gravatar: Add a component with hovercard support at the `./gravatar` subpath, shared across products that render subscriber or response avatars. [#48581]
9
+ - Global Notices: Add global-notices sub-path export for use in wp-build dashboards. [#48586]
10
+
11
+ ### Changed
12
+ - AdminPage: Update to @wordpress/admin-ui 2.0.0 and use the new `visual` prop to render the Jetpack logo alongside the page title. [#48410]
13
+ - Components: Use Link from `@wordpress/ui` instead of ExternalLink. [#48529]
14
+
15
+ ### Removed
16
+ - Components: Remove Gridicon component. Use Icon and named icon exports from `@wordpress/icons` instead. [#48537]
17
+
18
+ ### Fixed
19
+ - AdminPage: Pass a stable `jp-admin-page__page` className to admin-ui's Page so layout overrides survive admin-ui 2.0.0's switch to CSS Modules; pin the header heading level to `<h2>` and center the new `visual` slot to keep the Jetpack logo aligned with the title. [#48410]
20
+
5
21
  ## [1.10.0] - 2026-05-04
6
22
  ### Added
7
23
  - AdminPage: Add `unwrapped` prop to render children directly inside the admin-ui Page, skipping the default Container/Col grid wrap. Use for full-bleed pages (DataViews-based admin surfaces) that own their own scroll/layout model. [#48244]
@@ -1764,6 +1780,7 @@
1764
1780
  ### Changed
1765
1781
  - Update node version requirement to 14.16.1
1766
1782
 
1783
+ [1.11.0]: https://github.com/Automattic/jetpack-components/compare/1.10.0...1.11.0
1767
1784
  [1.10.0]: https://github.com/Automattic/jetpack-components/compare/1.9.0...1.10.0
1768
1785
  [1.9.0]: https://github.com/Automattic/jetpack-components/compare/1.8.3...1.9.0
1769
1786
  [1.8.3]: https://github.com/Automattic/jetpack-components/compare/1.8.2...1.8.3
@@ -1,4 +1,3 @@
1
- import '@wordpress/admin-ui/build-style/style.css';
2
1
  import type { AdminPageProps } from './types.ts';
3
2
  import type { FC } from 'react';
4
3
  /**
@@ -1,9 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import restApi from '@automattic/jetpack-api';
3
3
  import { Page } from '@wordpress/admin-ui';
4
- import '@wordpress/admin-ui/build-style/style.css';
5
- import { __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
6
- } from '@wordpress/components';
7
4
  import { __, sprintf } from '@wordpress/i18n';
8
5
  import clsx from 'clsx';
9
6
  import { useEffect, useCallback } from 'react';
@@ -45,13 +42,9 @@ const AdminPage = ({ children, className, showHeader = true, showFooter = true,
45
42
  __('There was an error testing Jetpack. Error: %s', 'jetpack-components'), error.message));
46
43
  }
47
44
  }, []);
48
- // Compose the title with logo for the admin-ui Page header.
49
- // Page's Header wraps this in an <h2> tag, so we just pass the content directly.
50
- const composedTitle = title ? (_jsxs(HStack, { spacing: 2, justify: "left", children: [logo || _jsx(JetpackLogo, { showText: false, height: 20 }), _jsx("span", { children: title })
51
- ] })) : undefined;
52
45
  // When title or breadcrumbs are provided, use admin-ui Page for the full page layout.
53
- if (showHeader && (composedTitle || breadcrumbs)) {
54
- return (_jsx("div", { className: rootClassName, children: _jsxs(Page, { ariaLabel: title, breadcrumbs: breadcrumbs, title: composedTitle, subTitle: subTitle, actions: actions, showSidebarToggle: false, children: [tabs, unwrapped ? (children) : (_jsx(Container, { fluid: true, horizontalSpacing: 0, children: _jsx(Col, { children: children }) })), showFooter && _jsx(JetpackFooter, { menu: optionalMenuItems })] }) }));
46
+ if (showHeader && (title || breadcrumbs)) {
47
+ return (_jsx("div", { className: rootClassName, children: _jsxs(Page, { className: "jp-admin-page__page", visual: logo || _jsx(JetpackLogo, { showText: false, height: 20 }), breadcrumbs: breadcrumbs, title: title, subTitle: subTitle, actions: actions, showSidebarToggle: false, children: [tabs, unwrapped ? (children) : (_jsx(Container, { fluid: true, horizontalSpacing: 0, children: _jsx(Col, { children: children }) })), showFooter && _jsx(JetpackFooter, { menu: optionalMenuItems })] }) }));
55
48
  }
56
49
  // Legacy path: no title provided, render the classic header.
57
50
  return (_jsxs("div", { className: rootClassName, children: [showHeader && (_jsx(Container, { horizontalSpacing: 5, children: _jsxs(Col, { className: clsx(styles['admin-page-header'], 'jp-admin-page-header'), children: [header ? header : _jsx(JetpackLogo, {}), sandboxedDomain && (_jsx("code", { className: styles['sandbox-domain-badge'], onClick: testConnection, onKeyDown: testConnection,
@@ -13,23 +13,36 @@
13
13
  // or when showBottomBorder is false.
14
14
  // Ideally admin-ui would expose a prop or CSS custom property for this:
15
15
  // https://github.com/WordPress/gutenberg/issues/75428
16
- &.without-bottom-border :global(.admin-ui-page__header) {
16
+
17
+ // Anchor: `.jp-admin-page__page` is the className we pass to admin-ui's
18
+ // <Page>; admin-ui 2.0.0 renders the header as a stable <header> element
19
+ // directly inside that page node — no class hooks anymore.
20
+ &.without-bottom-border :global(.jp-admin-page__page > header) {
17
21
  border-bottom: none;
18
22
  }
19
23
 
20
24
  // Disable sticky header until we make it work better across all pages.
21
25
  // JETPACK-1386
22
- :global(.admin-ui-page__header) {
26
+ :global(.jp-admin-page__page > header) {
23
27
  position: relative;
24
28
  z-index: 1;
25
29
  }
26
30
 
27
31
  // Normalize admin headers: implementation of admin-ui needs to
28
32
  // comprehend old wp-admin floating containers, such as Hello Dolly.
29
- :global(.admin-ui-page) {
33
+ :global(.jp-admin-page__page) {
30
34
  clear: both;
31
35
  }
32
36
 
37
+ // admin-ui 2.0.0's header `visual` slot is a 24px grid box but does not
38
+ // center its contents. Our JetpackLogo is 20px tall, so without this it
39
+ // pins to the top-left of the cell and looks misaligned vs. the title.
40
+ // admin-ui ships the slot as `<div aria-hidden="true">` in the header.
41
+ // TODO: remove once upstream centers contents in `.header-visual`.
42
+ :global(.jp-admin-page__page > header [aria-hidden="true"]) {
43
+ place-items: center;
44
+ }
45
+
33
46
  .admin-page-header {
34
47
  display: flex;
35
48
  align-items: center;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { __ } from '@wordpress/i18n';
3
+ import { Icon, desktop, mobile } from '@wordpress/icons';
3
4
  import clsx from 'clsx';
4
- import Gridicon from '../gridicon/index.js';
5
5
  import './style.scss';
6
6
  export const BoostScoreBar = ({ score, isLoading, showPrevScores, active, prevScore = 0, scoreBarType, noBoostScoreTooltip, }) => {
7
7
  if (!active) {
@@ -12,9 +12,9 @@ export const BoostScoreBar = ({ score, isLoading, showPrevScores, active, prevSc
12
12
  const mobileIconLabel = __('Mobile score', 'jetpack-components');
13
13
  const getIcon = () => {
14
14
  if (scoreBarType === 'desktop') {
15
- return _jsx(Gridicon, { icon: "computer", size: 24 });
15
+ return _jsx(Icon, { icon: desktop, size: 24 });
16
16
  }
17
- return _jsx(Gridicon, { icon: "phone", size: 24 });
17
+ return _jsx(Icon, { icon: mobile, size: 24 });
18
18
  };
19
19
  const getFillColor = () => {
20
20
  // This shows the loading color unless a score already exists.
@@ -0,0 +1,58 @@
1
+ import '@gravatar-com/hovercards/dist/style.css';
2
+ import './style.scss';
3
+ /**
4
+ * Gravatar `defaultImage` styles, mirroring https://docs.gravatar.com/sdk/images/#default-image
5
+ */
6
+ type DefaultImage = 'blank' | 'color' | 'identicon' | 'initials' | 'monsterid' | 'mp' | 'retro' | 'robohash' | 'wavatar';
7
+ export type GravatarProps = {
8
+ /**
9
+ * Style of the placeholder image when the email has no Gravatar profile.
10
+ * @default 'initials'
11
+ */
12
+ defaultImage?: DefaultImage;
13
+ /**
14
+ * Display name for accessibility (used as `alt` text and the hovercard label).
15
+ */
16
+ displayName?: string;
17
+ /**
18
+ * Email address to look up on Gravatar.
19
+ */
20
+ email: string;
21
+ /**
22
+ * Rendered avatar size in pixels.
23
+ * @default 48
24
+ */
25
+ size?: number;
26
+ /**
27
+ * Optional class name forwarded to the underlying `<img>` element. The
28
+ * default `jetpack-components-gravatar` class is always applied so consumers
29
+ * can target it directly.
30
+ */
31
+ className?: string;
32
+ /**
33
+ * Whether to attach a Gravatar profile hovercard to the avatar.
34
+ * @default true
35
+ */
36
+ useHovercard?: boolean;
37
+ };
38
+ /**
39
+ * Renders a Gravatar profile image with an optional Gravatar profile hovercard.
40
+ *
41
+ * If the email has no Gravatar profile, the configured `defaultImage` style is
42
+ * used (initials by default).
43
+ *
44
+ * Long-term, this is the seam for switching to a core or Gravatar-shipped
45
+ * component (see https://github.com/WordPress/gutenberg/issues/76836); until
46
+ * then, Forms and Newsletter share this implementation.
47
+ *
48
+ * @param props - The component props.
49
+ * @param props.defaultImage - Style of the placeholder image when the email has no Gravatar.
50
+ * @param props.displayName - Display name used for `alt` text + hovercard label.
51
+ * @param props.email - Email address to look up on Gravatar.
52
+ * @param props.size - Rendered avatar size in pixels.
53
+ * @param props.className - Optional class name forwarded to the underlying `<img>`.
54
+ * @param props.useHovercard - Whether to attach the Gravatar profile hovercard.
55
+ * @return The Gravatar avatar `<img>`, or null when no email is available.
56
+ */
57
+ export default function Gravatar({ defaultImage, displayName, email, size, className, useHovercard }: GravatarProps): JSX.Element | null;
58
+ export {};
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * External dependencies
4
+ */
5
+ import { Hovercards } from '@gravatar-com/hovercards';
6
+ import '@gravatar-com/hovercards/dist/style.css';
7
+ import { useEffect, useRef } from '@wordpress/element';
8
+ import { __ } from '@wordpress/i18n';
9
+ import clsx from 'clsx';
10
+ import { sha256 } from 'js-sha256';
11
+ import './style.scss';
12
+ /**
13
+ * Renders a Gravatar profile image with an optional Gravatar profile hovercard.
14
+ *
15
+ * If the email has no Gravatar profile, the configured `defaultImage` style is
16
+ * used (initials by default).
17
+ *
18
+ * Long-term, this is the seam for switching to a core or Gravatar-shipped
19
+ * component (see https://github.com/WordPress/gutenberg/issues/76836); until
20
+ * then, Forms and Newsletter share this implementation.
21
+ *
22
+ * @param props - The component props.
23
+ * @param props.defaultImage - Style of the placeholder image when the email has no Gravatar.
24
+ * @param props.displayName - Display name used for `alt` text + hovercard label.
25
+ * @param props.email - Email address to look up on Gravatar.
26
+ * @param props.size - Rendered avatar size in pixels.
27
+ * @param props.className - Optional class name forwarded to the underlying `<img>`.
28
+ * @param props.useHovercard - Whether to attach the Gravatar profile hovercard.
29
+ * @return The Gravatar avatar `<img>`, or null when no email is available.
30
+ */
31
+ export default function Gravatar({ defaultImage = 'initials', displayName, email, size = 48, className, useHovercard = true, }) {
32
+ const profileImageRef = useRef(null);
33
+ const hovercardRef = useRef(null);
34
+ useEffect(() => {
35
+ if (!useHovercard || !profileImageRef.current) {
36
+ return;
37
+ }
38
+ hovercardRef.current = new Hovercards({
39
+ // See https://github.com/Automattic/gravatar/tree/trunk/web/packages/hovercards#translations
40
+ i18n: {
41
+ 'Edit your profile →': __('Edit your profile →', 'jetpack-components'),
42
+ 'View profile →': __('View profile →', 'jetpack-components'),
43
+ Contact: __('Contact', 'jetpack-components'),
44
+ 'Send money': __('Send money', 'jetpack-components'),
45
+ 'Sorry, we are unable to load this Gravatar profile.': __('Sorry, we are unable to load this Gravatar profile.', 'jetpack-components'),
46
+ 'Gravatar not found.': __('Gravatar not found.', 'jetpack-components'),
47
+ 'This profile is private.': __('This profile is private.', 'jetpack-components'),
48
+ 'Too Many Requests.': __('Too many requests.', 'jetpack-components'),
49
+ 'Internal Server Error.': __('Internal server error.', 'jetpack-components'),
50
+ 'Is this you?': __('Is this you?', 'jetpack-components'),
51
+ 'Claim your free profile.': __('Claim your free profile.', 'jetpack-components'),
52
+ Email: __('Email', 'jetpack-components'),
53
+ 'Home Phone': __('Home phone', 'jetpack-components'),
54
+ 'Work Phone': __('Work phone', 'jetpack-components'),
55
+ 'Cell Phone': __('Cell phone', 'jetpack-components'),
56
+ 'Contact Form': __('Contact form', 'jetpack-components'),
57
+ Calendar: __('Calendar', 'jetpack-components'),
58
+ },
59
+ });
60
+ hovercardRef.current.attach(profileImageRef.current);
61
+ }, [useHovercard]);
62
+ if (!email) {
63
+ return null;
64
+ }
65
+ const hashedEmail = sha256(email);
66
+ const hovercardName = displayName ? `&name=${encodeURIComponent(displayName)}` : '';
67
+ return (_jsx("img", { ref: profileImageRef, className: clsx('jetpack-components-gravatar', className), alt: displayName || '', src: `https://secure.gravatar.com/avatar/${hashedEmail}?d=${defaultImage}${hovercardName}`, width: size, height: size, loading: "lazy" }));
68
+ }
@@ -0,0 +1,7 @@
1
+ .jetpack-components-gravatar {
2
+ background-color: var(--jp-gray-5, #dcdcde);
3
+ border-radius: 50%;
4
+ display: block;
5
+ flex-shrink: 0; // Prevent shrinking when Gravatar is inside flex containers.
6
+ overflow: hidden;
7
+ }
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Popover } from '@wordpress/components';
3
+ import { Icon, info } from '@wordpress/icons';
3
4
  import clsx from 'clsx';
4
5
  import { useCallback, useState } from 'react';
5
6
  import Button from '../button/index.js';
6
- import Gridicon from '../gridicon/index.js';
7
7
  import './style.scss';
8
8
  const placementsToPositions = (placement) => {
9
9
  const mapping = {
@@ -22,7 +22,7 @@ const placementsToPositions = (placement) => {
22
22
  * @param {IconTooltipProps} props - Props
23
23
  * @return {ReactElement} - JSX element
24
24
  */
25
- const IconTooltip = ({ className = '', iconClassName = '', placement = 'bottom-end', animate = true, iconCode = 'info-outline', iconSize = 18, offset = 10, title, children, popoverAnchorStyle = 'icon', forceShow = false, hoverShow = false, wide = false, inline = true, shift = false, }) => {
25
+ const IconTooltip = ({ className = '', iconClassName = '', placement = 'bottom-end', animate = true, iconCode = info, iconSize = 18, offset = 10, title, children, popoverAnchorStyle = 'icon', forceShow = false, hoverShow = false, wide = false, inline = true, shift = false, }) => {
26
26
  const POPOVER_HELPER_WIDTH = 124;
27
27
  const [isVisible, setIsVisible] = useState(false);
28
28
  const [hoverTimeout, setHoverTimeout] = useState(null);
@@ -70,7 +70,7 @@ const IconTooltip = ({ className = '', iconClassName = '', placement = 'bottom-e
70
70
  setHoverTimeout(id);
71
71
  }
72
72
  }, [hoverShow]);
73
- return (_jsxs("div", { className: wrapperClassNames, "data-testid": "icon-tooltip_wrapper", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [!isAnchorWrapper && (_jsx(Button, { variant: "link", onMouseDown: toggleTooltip, children: _jsx(Gridicon, { className: iconClassName, icon: iconCode, size: iconSize }) })), _jsx("div", { className: clsx('icon-tooltip-helper', { 'is-wide': wide }), style: iconShiftBySize, children: (isForcedToShow || isVisible) && (_jsx(Popover, { ...args, children: _jsxs("div", { children: [title && _jsx("div", { className: "icon-tooltip-title", children: title }), _jsx("div", { className: "icon-tooltip-content", children: children })
73
+ return (_jsxs("div", { className: wrapperClassNames, "data-testid": "icon-tooltip_wrapper", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [!isAnchorWrapper && (_jsx(Button, { variant: "link", onMouseDown: toggleTooltip, children: _jsx(Icon, { className: iconClassName, icon: iconCode, size: iconSize }) })), _jsx("div", { className: clsx('icon-tooltip-helper', { 'is-wide': wide }), style: iconShiftBySize, children: (isForcedToShow || isVisible) && (_jsx(Popover, { ...args, children: _jsxs("div", { children: [title && _jsx("div", { className: "icon-tooltip-title", children: title }), _jsx("div", { className: "icon-tooltip-content", children: children })
74
74
  ] }) })) })
75
75
  ] }));
76
76
  };
@@ -1,3 +1,4 @@
1
+ import type { IconType } from '@wordpress/components';
1
2
  import type { ReactNode } from 'react';
2
3
  export type Placement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end';
3
4
  export type Position = 'top left' | 'top center' | 'top right' | 'bottom left' | 'bottom center' | 'bottom right';
@@ -7,7 +8,7 @@ export type IconTooltipProps = {
7
8
  */
8
9
  className?: string;
9
10
  /**
10
- * The class name applied to Gridicon.
11
+ * The class name applied to the icon.
11
12
  */
12
13
  iconClassName?: string;
13
14
  /**
@@ -23,9 +24,9 @@ export type IconTooltipProps = {
23
24
  */
24
25
  animate?: boolean;
25
26
  /**
26
- * The icon code for Gridicon.
27
+ * The icon to display. Accepts icon components from `@wordpress/icons`.
27
28
  */
28
- iconCode?: string;
29
+ iconCode?: IconType;
29
30
  /**
30
31
  * The title of Popover.
31
32
  */
@@ -36,7 +36,7 @@ const JetpackFooter = ({ className, menu, ...otherProps }) => {
36
36
  _jsx(JetpackLogo, { showText: false, height: 16, "aria-hidden": "true" }), _jsx(Text, { variant: "body-md", children: "Jetpack" })
37
37
  ] }), _jsx(Stack, { render: _jsx("ul", {}), direction: "row", gap: "lg", wrap: "wrap", children: items.map(item => {
38
38
  const isButton = item.role === 'button';
39
- return (_jsx("li", { children: _jsx(Text, { variant: "body-sm", className: "jetpack-footer__menu-item", render: isButton ? (_jsx(Link, { render: _jsx(Text, { variant: "body-md", render: _jsx("span", {}) }), tone: "neutral", variant: "default", role: item.role, tabIndex: 0, onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })) : (_jsx(Link, { render: _jsx(Text, { variant: "body-md", render: _jsx("a", {}) }), tone: "neutral", variant: "default", href: item.href || '', title: item.title || '', role: item.role, onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })), children: item.label }) }, item.label));
39
+ return (_jsx("li", { children: _jsx(Text, { variant: "body-md", className: "jetpack-footer__menu-item", render: isButton ? (_jsx(Link, { render: _jsx("span", {}), tone: "neutral", variant: "default", role: item.role, tabIndex: 0, onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })) : (_jsx(Link, { tone: "neutral", variant: "default", href: item.href || '', title: item.title || '', role: item.role, onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })), children: item.label }) }, item.label));
40
40
  }) }), _jsx("a", { className: "jetpack-footer__a8c", href: getRedirectUrl('a8c-about'), rel: "noopener noreferrer", target: "_blank", children: _jsx(AutomatticBylineLogo, { height: 8 }) })
41
41
  ] }));
42
42
  };
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ExternalLink } from '@wordpress/components';
3
2
  import { createInterpolateElement } from '@wordpress/element';
4
3
  import { __, sprintf } from '@wordpress/i18n';
4
+ import { Link } from '@wordpress/ui';
5
5
  import clsx from 'clsx';
6
6
  import { getRedirectUrl } from '../../index.js';
7
7
  import Text from '../text/index.js';
@@ -24,25 +24,25 @@ const MultipleButtonsText = ({ multipleButtonsLabels }) => {
24
24
  /* translators: %1$s is button label 1 and %2$s is button label 2 */
25
25
  __('By clicking <strong>%1$s</strong> or <strong>%2$s</strong>, you agree to our <tosLink>Terms of Service</tosLink> and to <shareDetailsLink>sync your site‘s data</shareDetailsLink> with us.', 'jetpack-components'), multipleButtonsLabels[0], multipleButtonsLabels[1]), {
26
26
  strong: _jsx("strong", {}),
27
- tosLink: _jsx(Link, { slug: "wpcom-tos" }),
28
- shareDetailsLink: _jsx(Link, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
27
+ tosLink: _jsx(TosLink, { slug: "wpcom-tos" }),
28
+ shareDetailsLink: _jsx(TosLink, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
29
29
  });
30
30
  }
31
31
  return createInterpolateElement(__('By clicking the buttons above, you agree to our <tosLink>Terms of Service</tosLink> and to <shareDetailsLink>sync your site‘s data</shareDetailsLink> with us.', 'jetpack-components'), {
32
- tosLink: _jsx(Link, { slug: "wpcom-tos" }),
33
- shareDetailsLink: _jsx(Link, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
32
+ tosLink: _jsx(TosLink, { slug: "wpcom-tos" }),
33
+ shareDetailsLink: _jsx(TosLink, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
34
34
  });
35
35
  };
36
36
  const SingleButtonText = ({ agreeButtonLabel }) => createInterpolateElement(sprintf(
37
37
  /* translators: %s is a button label */
38
38
  __('By clicking <strong>%s</strong>, you agree to our <tosLink>Terms of Service</tosLink> and to <shareDetailsLink>sync your site‘s data</shareDetailsLink> with us.', 'jetpack-components'), agreeButtonLabel), {
39
39
  strong: _jsx("strong", {}),
40
- tosLink: _jsx(Link, { slug: "wpcom-tos" }),
41
- shareDetailsLink: _jsx(Link, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
40
+ tosLink: _jsx(TosLink, { slug: "wpcom-tos" }),
41
+ shareDetailsLink: _jsx(TosLink, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
42
42
  });
43
43
  const TermsOfServiceTextOnly = () => createInterpolateElement(__('By continuing you agree to our <tosLink>Terms of Service</tosLink> and to <shareDetailsLink>sync your site’s data</shareDetailsLink> with us. We’ll check if that email is linked to an existing WordPress.com account or create a new one instantly.', 'jetpack-components'), {
44
- tosLink: _jsx(Link, { slug: "wpcom-tos" }),
45
- shareDetailsLink: _jsx(Link, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
44
+ tosLink: _jsx(TosLink, { slug: "wpcom-tos" }),
45
+ shareDetailsLink: _jsx(TosLink, { slug: "jetpack-support-what-data-does-jetpack-sync" }),
46
46
  });
47
- const Link = ({ slug, children }) => (_jsx(ExternalLink, { className: "terms-of-service__link", href: getRedirectUrl(slug), children: children }));
47
+ const TosLink = ({ slug, children }) => (_jsx(Link, { openInNewTab: true, className: "terms-of-service__link", href: getRedirectUrl(slug), children: children }));
48
48
  export default TermsOfService;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useCallback } from '@wordpress/element';
3
- import Gridicon from '../gridicon/index.js';
3
+ import { Icon, chevronLeft, chevronRight } from '@wordpress/icons';
4
4
  import { Testimonial } from './testimonial.js';
5
5
  const Testimonials = ({ testimonials }) => {
6
6
  const [currentTestimonialIndex, setcurrentTestimonialIndex] = useState(0);
@@ -13,6 +13,6 @@ const Testimonials = ({ testimonials }) => {
13
13
  setcurrentTestimonialIndex(newIndex);
14
14
  }, [currentTestimonialIndex, testimonials]);
15
15
  const shouldShowArrows = testimonials.length > 1;
16
- return (_jsxs("div", { className: "testimonials", children: [shouldShowArrows && (_jsx("button", { "data-testid": "left-arrow", className: "testimonials__left-arrow", onClick: decrementTestimonial, children: _jsx(Gridicon, { icon: "chevron-left", size: 48 }) })), testimonials.map((testimonial, index) => (_jsx(Testimonial, { ...testimonial, hidden: currentTestimonialIndex !== index }, index))), shouldShowArrows && (_jsx("button", { "data-testid": "right-arrow", className: "testimonials__right-arrow", onClick: incrementTestimonial, children: _jsx(Gridicon, { icon: "chevron-right", size: 48 }) }))] }));
16
+ return (_jsxs("div", { className: "testimonials", children: [shouldShowArrows && (_jsx("button", { "data-testid": "left-arrow", className: "testimonials__left-arrow", onClick: decrementTestimonial, children: _jsx(Icon, { icon: chevronLeft, size: 48 }) })), testimonials.map((testimonial, index) => (_jsx(Testimonial, { ...testimonial, hidden: currentTestimonialIndex !== index }, index))), shouldShowArrows && (_jsx("button", { "data-testid": "right-arrow", className: "testimonials__right-arrow", onClick: incrementTestimonial, children: _jsx(Icon, { icon: chevronRight, size: 48 }) }))] }));
17
17
  };
18
18
  export default Testimonials;
package/build/index.d.ts CHANGED
@@ -12,7 +12,8 @@ export { default as AutomatticForAgenciesLogo } from './components/automattic-fo
12
12
  export { default as JetpackFooter } from './components/jetpack-footer/index.tsx';
13
13
  /** @deprecated Use `Spinner` from `@wordpress/components` instead. */
14
14
  export { default as Spinner } from './components/spinner/index.tsx';
15
- export { default as Gridicon } from './components/gridicon/index.tsx';
15
+ export { default as Gravatar } from './components/gravatar/index.tsx';
16
+ export type { GravatarProps } from './components/gravatar/index.tsx';
16
17
  export { default as IconTooltip } from './components/icon-tooltip/index.tsx';
17
18
  export { default as ActionButton } from './components/action-button/index.tsx';
18
19
  export { default as PricingCard } from './components/pricing-card/index.tsx';
package/build/index.js CHANGED
@@ -26,7 +26,7 @@ export { default as AutomatticForAgenciesLogo } from './components/automattic-fo
26
26
  export { default as JetpackFooter } from './components/jetpack-footer/index.js';
27
27
  /** @deprecated Use `Spinner` from `@wordpress/components` instead. */
28
28
  export { default as Spinner } from './components/spinner/index.js';
29
- export { default as Gridicon } from './components/gridicon/index.js';
29
+ export { default as Gravatar } from './components/gravatar/index.js';
30
30
  export { default as IconTooltip } from './components/icon-tooltip/index.js';
31
31
  export { default as ActionButton } from './components/action-button/index.js';
32
32
  export { default as PricingCard } from './components/pricing-card/index.js';
@@ -1,9 +1,5 @@
1
1
  import restApi from '@automattic/jetpack-api';
2
2
  import { Page } from '@wordpress/admin-ui';
3
- import '@wordpress/admin-ui/build-style/style.css';
4
- import {
5
- __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
6
- } from '@wordpress/components';
7
3
  import { __, sprintf } from '@wordpress/i18n';
8
4
  import clsx from 'clsx';
9
5
  import { useEffect, useCallback } from 'react';
@@ -74,23 +70,15 @@ const AdminPage: FC< AdminPageProps > = ( {
74
70
  }
75
71
  }, [] );
76
72
 
77
- // Compose the title with logo for the admin-ui Page header.
78
- // Page's Header wraps this in an <h2> tag, so we just pass the content directly.
79
- const composedTitle = title ? (
80
- <HStack spacing={ 2 } justify="left">
81
- { logo || <JetpackLogo showText={ false } height={ 20 } /> }
82
- <span>{ title }</span>
83
- </HStack>
84
- ) : undefined;
85
-
86
73
  // When title or breadcrumbs are provided, use admin-ui Page for the full page layout.
87
- if ( showHeader && ( composedTitle || breadcrumbs ) ) {
74
+ if ( showHeader && ( title || breadcrumbs ) ) {
88
75
  return (
89
76
  <div className={ rootClassName }>
90
77
  <Page
91
- ariaLabel={ title }
78
+ className="jp-admin-page__page"
79
+ visual={ logo || <JetpackLogo showText={ false } height={ 20 } /> }
92
80
  breadcrumbs={ breadcrumbs }
93
- title={ composedTitle }
81
+ title={ title }
94
82
  subTitle={ subTitle }
95
83
  actions={ actions }
96
84
  showSidebarToggle={ false }
@@ -13,23 +13,36 @@
13
13
  // or when showBottomBorder is false.
14
14
  // Ideally admin-ui would expose a prop or CSS custom property for this:
15
15
  // https://github.com/WordPress/gutenberg/issues/75428
16
- &.without-bottom-border :global(.admin-ui-page__header) {
16
+
17
+ // Anchor: `.jp-admin-page__page` is the className we pass to admin-ui's
18
+ // <Page>; admin-ui 2.0.0 renders the header as a stable <header> element
19
+ // directly inside that page node — no class hooks anymore.
20
+ &.without-bottom-border :global(.jp-admin-page__page > header) {
17
21
  border-bottom: none;
18
22
  }
19
23
 
20
24
  // Disable sticky header until we make it work better across all pages.
21
25
  // JETPACK-1386
22
- :global(.admin-ui-page__header) {
26
+ :global(.jp-admin-page__page > header) {
23
27
  position: relative;
24
28
  z-index: 1;
25
29
  }
26
30
 
27
31
  // Normalize admin headers: implementation of admin-ui needs to
28
32
  // comprehend old wp-admin floating containers, such as Hello Dolly.
29
- :global(.admin-ui-page) {
33
+ :global(.jp-admin-page__page) {
30
34
  clear: both;
31
35
  }
32
36
 
37
+ // admin-ui 2.0.0's header `visual` slot is a 24px grid box but does not
38
+ // center its contents. Our JetpackLogo is 20px tall, so without this it
39
+ // pins to the top-left of the cell and looks misaligned vs. the title.
40
+ // admin-ui ships the slot as `<div aria-hidden="true">` in the header.
41
+ // TODO: remove once upstream centers contents in `.header-visual`.
42
+ :global(.jp-admin-page__page > header [aria-hidden="true"]) {
43
+ place-items: center;
44
+ }
45
+
33
46
  .admin-page-header {
34
47
  display: flex;
35
48
  align-items: center;
@@ -1,6 +1,6 @@
1
1
  import { __ } from '@wordpress/i18n';
2
+ import { Icon, desktop, mobile } from '@wordpress/icons';
2
3
  import clsx from 'clsx';
3
- import Gridicon from '../gridicon/index.tsx';
4
4
  import type { BoostScoreBarProps } from './types.ts';
5
5
  import type { FunctionComponent } from 'react';
6
6
 
@@ -25,10 +25,10 @@ export const BoostScoreBar: FunctionComponent< BoostScoreBarProps > = ( {
25
25
 
26
26
  const getIcon = () => {
27
27
  if ( scoreBarType === 'desktop' ) {
28
- return <Gridicon icon="computer" size={ 24 } />;
28
+ return <Icon icon={ desktop } size={ 24 } />;
29
29
  }
30
30
 
31
- return <Gridicon icon="phone" size={ 24 } />;
31
+ return <Icon icon={ mobile } size={ 24 } />;
32
32
  };
33
33
 
34
34
  const getFillColor = () => {