@automattic/jetpack-components 5.0.1 → 6.1.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 (52) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/build/components/admin-page/index.d.ts +1 -0
  3. package/build/components/admin-page/index.js +10 -4
  4. package/build/components/admin-page/page-action-button.d.ts +5 -0
  5. package/build/components/admin-page/page-action-button.js +44 -0
  6. package/build/components/admin-page/types.d.ts +4 -0
  7. package/build/components/automattic-icon-logo/index.d.ts +4 -2
  8. package/build/components/automattic-icon-logo/index.js +6 -4
  9. package/build/components/boost-score-bar/index.js +7 -7
  10. package/build/components/checkbox-card/style.scss +10 -38
  11. package/build/components/jetpack-footer/index.d.ts +1 -18
  12. package/build/components/jetpack-footer/index.js +1 -39
  13. package/build/components/jetpack-footer/jetpack-footer.d.ts +18 -0
  14. package/build/components/jetpack-footer/jetpack-footer.js +39 -0
  15. package/build/components/period-selector/index.d.ts +9 -0
  16. package/build/components/period-selector/index.js +21 -0
  17. package/build/components/period-selector/types.d.ts +28 -0
  18. package/build/components/period-selector/types.js +1 -0
  19. package/build/components/radio-card/index.d.ts +19 -0
  20. package/build/components/radio-card/index.js +29 -0
  21. package/build/components/radio-card/style.scss +26 -0
  22. package/build/components/radio-card/types.d.ts +41 -0
  23. package/build/components/radio-card/types.js +1 -0
  24. package/build/components/selectable-card/_selectable-card.scss +57 -0
  25. package/build/components/terms-of-service/index.js +3 -4
  26. package/build/components/terms-of-service/styles.scss +5 -2
  27. package/build/components/terms-of-service/types.d.ts +12 -2
  28. package/build/index.d.ts +5 -1
  29. package/build/index.js +3 -1
  30. package/components/admin-page/index.tsx +11 -3
  31. package/components/admin-page/page-action-button.tsx +69 -0
  32. package/components/admin-page/types.ts +5 -0
  33. package/components/automattic-icon-logo/index.tsx +9 -7
  34. package/components/boost-score-bar/index.tsx +9 -9
  35. package/components/checkbox-card/style.scss +10 -38
  36. package/components/jetpack-footer/README.md +9 -0
  37. package/components/jetpack-footer/index.tsx +1 -112
  38. package/components/jetpack-footer/jetpack-footer.tsx +113 -0
  39. package/components/period-selector/index.tsx +84 -0
  40. package/components/period-selector/types.ts +29 -0
  41. package/components/radio-card/index.tsx +72 -0
  42. package/components/radio-card/style.scss +26 -0
  43. package/components/radio-card/types.ts +43 -0
  44. package/components/selectable-card/_selectable-card.scss +57 -0
  45. package/components/terms-of-service/index.tsx +3 -4
  46. package/components/terms-of-service/styles.scss +5 -2
  47. package/components/terms-of-service/types.ts +13 -22
  48. package/index.ts +5 -1
  49. package/package.json +23 -14
  50. package/build/components/number-control/index.d.ts +0 -28
  51. package/build/components/number-control/index.js +0 -17
  52. package/components/number-control/index.tsx +0 -45
package/CHANGELOG.md CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Components package releases.
4
4
 
5
+ ## [6.1.0] - 2026-10-28
6
+ ### Added
7
+ - Add a PeriodSelector component: a dropdown of period presets with optional previous and next buttons. [#54149]
8
+ - Add a RadioCard component. [#54212]
9
+ - AdminPage: Add a `jp-admin-page__content` class to the content container. [#54105]
10
+ - PeriodSelector: Add a disabled prop. [#54202]
11
+ - Period Selector: Add a label that screen readers announce with the selected period. [#54209]
12
+
13
+ ### Changed
14
+ - AutomatticIconLogo: Use the black Automattic symbol by default, without blue. [#54208]
15
+ - Update package dependencies. [#54118]
16
+
17
+ ## [6.0.0] - 2026-10-26
18
+ ### Added
19
+ - AdminPage: Add an ariaLabel prop that names the page region when the header shows breadcrumbs instead of a title. [#54055]
20
+ - AdminPage: Add a page action button that shows only its icon on screens 480px wide or narrower. [#53921]
21
+ - JetpackFooter: Add an entry without the stylesheet, and export the stylesheet on its own, so script-module builds can bundle the footer. [#53971]
22
+ - Support React 19. [#53882]
23
+
24
+ ### Changed
25
+ - TermsOfService: Render with Text from @wordpress/ui. The `variant` prop now takes a @wordpress/ui Text variant, and the legacy Text spacing and `component` props are removed. [#53892]
26
+ - Update package dependencies. [#53951]
27
+
28
+ ### Removed
29
+ - Remove the `NumberControl` component. Use `InputControl` from `@wordpress/ui` instead. [#54046]
30
+
31
+ ### Fixed
32
+ - AdminPage: Stop clearing the shared REST API nonce and root when a page doesn't pass them. [#53397]
33
+
5
34
  ## [5.0.1] - 2026-10-20
6
35
  ### Changed
7
36
  - Internal updates.
@@ -2019,6 +2048,8 @@
2019
2048
  ### Changed
2020
2049
  - Update node version requirement to 14.16.1
2021
2050
 
2051
+ [6.1.0]: https://github.com/Automattic/jetpack-components/compare/6.0.0...6.1.0
2052
+ [6.0.0]: https://github.com/Automattic/jetpack-components/compare/5.0.1...6.0.0
2022
2053
  [5.0.1]: https://github.com/Automattic/jetpack-components/compare/5.0.0...5.0.1
2023
2054
  [5.0.0]: https://github.com/Automattic/jetpack-components/compare/4.0.4...5.0.0
2024
2055
  [4.0.4]: https://github.com/Automattic/jetpack-components/compare/4.0.3...4.0.4
@@ -10,3 +10,4 @@ import type { FC } from 'react';
10
10
  */
11
11
  declare const AdminPage: FC<AdminPageProps>;
12
12
  export default AdminPage;
13
+ export { default as PageActionButton } from './page-action-button.tsx';
@@ -17,10 +17,15 @@ import styles from './style.module.scss';
17
17
  * @param {AdminPageProps} props - Component properties.
18
18
  * @return {ReactNode} AdminPage component.
19
19
  */
20
- const AdminPage = ({ children, className, showHeader = true, showFooter = true, showBackground = true, sandboxedDomain = '', apiRoot = '', apiNonce = '', optionalMenuItems, header, title, subTitle, logo, actions, globalControls, breadcrumbs, tabs, showBottomBorder = true, unwrapped = false, }) => {
20
+ const AdminPage = ({ children, className, showHeader = true, showFooter = true, showBackground = true, sandboxedDomain = '', apiRoot = '', apiNonce = '', optionalMenuItems, header, title, subTitle, logo, actions, globalControls, breadcrumbs, ariaLabel, tabs, showBottomBorder = true, unwrapped = false, }) => {
21
+ // Only set what was passed: the client is shared, so an empty default would wipe what the page set.
21
22
  useEffect(() => {
22
- restApi.setApiRoot(apiRoot);
23
- restApi.setApiNonce(apiNonce);
23
+ if (apiRoot) {
24
+ restApi.setApiRoot(apiRoot);
25
+ }
26
+ if (apiNonce) {
27
+ restApi.setApiNonce(apiNonce);
28
+ }
24
29
  }, [apiRoot, apiNonce]);
25
30
  // `jp-admin-page` is a stable, non-hashed hook for global stylesheets and
26
31
  // shared SCSS mixins (notably `jetpack-admin-page-layout` in
@@ -44,7 +49,7 @@ const AdminPage = ({ children, className, showHeader = true, showFooter = true,
44
49
  }, []);
45
50
  // When title or breadcrumbs are provided, use admin-ui Page for the full page layout.
46
51
  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: globalControls ? (_jsxs(_Fragment, { children: [actions, actions && (_jsx("span", { role: "separator", "aria-orientation": "vertical", className: styles['global-controls-separator'] })), globalControls] })) : (actions), showSidebarToggle: false, children: [tabs, unwrapped ? (children) : (_jsx(Container, { fluid: true, horizontalSpacing: 0, children: _jsx(Col, { children: children }) })), showFooter && _jsx(JetpackFooter, { menu: optionalMenuItems })] }) }));
52
+ 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, ariaLabel: ariaLabel, subTitle: subTitle, actions: globalControls ? (_jsxs(_Fragment, { children: [actions, actions && (_jsx("span", { role: "separator", "aria-orientation": "vertical", className: styles['global-controls-separator'] })), globalControls] })) : (actions), showSidebarToggle: false, children: [tabs, unwrapped ? (children) : (_jsx(Container, { className: "jp-admin-page__content", fluid: true, horizontalSpacing: 0, children: _jsx(Col, { children: children }) })), showFooter && _jsx(JetpackFooter, { menu: optionalMenuItems })] }) }));
48
53
  }
49
54
  // Legacy path: no title provided, render the classic header.
50
55
  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,
@@ -52,3 +57,4 @@ const AdminPage = ({ children, className, showHeader = true, showFooter = true,
52
57
  role: "button", tabIndex: 0, title: `Sandboxing via ${sandboxedDomain}. Click to test connection.`, children: "API Sandboxed" }))] }) })), _jsx(Container, { fluid: true, horizontalSpacing: 0, children: _jsx(Col, { children: children }) }), showFooter && _jsx(JetpackFooter, { menu: optionalMenuItems })] }));
53
58
  };
54
59
  export default AdminPage;
60
+ export { default as PageActionButton } from './page-action-button.js';
@@ -0,0 +1,5 @@
1
+ import { IconButton } from '@wordpress/ui';
2
+ import type { ComponentProps, ForwardRefExoticComponent, RefAttributes } from 'react';
3
+ type PageActionButtonProps = ComponentProps<typeof IconButton>;
4
+ declare const ForwardedPageActionButton: ForwardRefExoticComponent<PageActionButtonProps & RefAttributes<HTMLButtonElement>>;
5
+ export default ForwardedPageActionButton;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMergeRefs, useViewportMatch } from '@wordpress/compose';
3
+ import { Button, IconButton } from '@wordpress/ui';
4
+ import { forwardRef, useCallback, useRef } from 'react';
5
+ /**
6
+ * A page action for `AdminPage`'s `actions`: a labelled button, or an icon button on narrow screens.
7
+ *
8
+ * The label stays the button's accessible name, and becomes its tooltip when only the icon shows.
9
+ *
10
+ * @param props - Any `Button` prop, passed to whichever button renders, plus the ones below.
11
+ * @param props.label - The button's text, or its tooltip when only the icon shows.
12
+ * @param props.icon - The icon shown on narrow screens.
13
+ * @param props.shortcut - Shown in the icon button's tooltip, so on narrow screens only.
14
+ * @param props.positioner - Positions the icon button's tooltip, so on narrow screens only.
15
+ * @param ref - Forwarded to the rendered button.
16
+ * @return The button.
17
+ */
18
+ function PageActionButton({ label, icon, shortcut, positioner, ...buttonProps }, ref) {
19
+ // On phones, page actions show only their icon so the header's title keeps its room.
20
+ const isNarrowScreen = useViewportMatch('mobile', '<');
21
+ // Crossing the breakpoint swaps in a new button: move focus to it if the old one had it.
22
+ const button = useRef(null);
23
+ const hadFocus = useRef(false);
24
+ const keepFocus = useCallback((node) => {
25
+ const previous = button.current;
26
+ if (node) {
27
+ if (hadFocus.current) {
28
+ node.focus();
29
+ }
30
+ hadFocus.current = false;
31
+ }
32
+ else {
33
+ hadFocus.current = previous?.ownerDocument.activeElement === previous;
34
+ }
35
+ button.current = node;
36
+ }, []);
37
+ const mergedRef = useMergeRefs([ref, keepFocus]);
38
+ if (isNarrowScreen) {
39
+ return (_jsx(IconButton, { ref: mergedRef, label: label, icon: icon, shortcut: shortcut, positioner: positioner, ...buttonProps }));
40
+ }
41
+ return (_jsx(Button, { ref: mergedRef, ...buttonProps, children: label }));
42
+ }
43
+ const ForwardedPageActionButton = forwardRef(PageActionButton);
44
+ export default ForwardedPageActionButton;
@@ -41,6 +41,10 @@ export type AdminPageProps = {
41
41
  * Breadcrumb navigation displayed next to the title in the unified header.
42
42
  */
43
43
  breadcrumbs?: ReactNode;
44
+ /**
45
+ * Accessible name of the page region. Defaults to `title`, so pass it when the header shows `breadcrumbs` instead.
46
+ */
47
+ ariaLabel?: string;
44
48
  /**
45
49
  * Tab navigation displayed below the title/tagline in the unified header.
46
50
  */
@@ -2,9 +2,11 @@ import type { ReactElement } from 'react';
2
2
  /**
3
3
  * Automattic Icon Logo component
4
4
  *
5
+ * The Automattic logo is black or white only: pass white for both colors on dark backgrounds.
6
+ *
5
7
  * @param {object} props - Component props
6
- * @param {string} props.innerColor - Color code for the line in the middle of the logo.
7
- * @param {string} props.outerColor - Color code for the logo's outer
8
+ * @param {string} props.innerColor - Color code for the logo's ring.
9
+ * @param {string} props.outerColor - Color code for the slash inside the ring.
8
10
  * @return {ReactElement} Component template
9
11
  */
10
12
  export default function AutomatticIconLogo({ innerColor, outerColor, }: {
@@ -3,11 +3,13 @@ import { Path, SVG } from '@wordpress/components';
3
3
  /**
4
4
  * Automattic Icon Logo component
5
5
  *
6
+ * The Automattic logo is black or white only: pass white for both colors on dark backgrounds.
7
+ *
6
8
  * @param {object} props - Component props
7
- * @param {string} props.innerColor - Color code for the line in the middle of the logo.
8
- * @param {string} props.outerColor - Color code for the logo's outer
9
+ * @param {string} props.innerColor - Color code for the logo's ring.
10
+ * @param {string} props.outerColor - Color code for the slash inside the ring.
9
11
  * @return {ReactElement} Component template
10
12
  */
11
- export default function AutomatticIconLogo({ innerColor = '#00A3E0', outerColor = '#FFFFFF', }) {
12
- return (_jsxs(SVG, { clipRule: "evenodd", fill: "none", fillRule: "evenodd", height: "43", viewBox: "0 0 47 43", width: "47", xmlns: "http://www.w3.org/2000/svg", children: [_jsx(Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M39.3164 20.9589C39.3164 13.2129 33.656 6.30469 23.4214 6.30469C13.1867 6.30469 7.58624 13.2129 7.58624 20.9589V21.9206C7.58624 29.6677 13.1867 36.6942 23.4214 36.6942C33.656 36.6942 39.3164 29.6677 39.3164 21.9206V20.9589ZM23.4214 43C9.21187 43 0 32.7913 0 22.1604V20.8407C0 10.0285 9.21187 0 23.4214 0C37.6919 0 46.9038 10.0285 46.9038 20.8407V22.1604C46.9038 32.7913 37.6919 43 23.4214 43Z", fill: innerColor }), _jsx(Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M28.4548 14.1897C29.752 15.0401 30.1158 16.8077 29.2739 18.1343L22.712 28.4682C21.8691 29.7959 20.1348 30.1799 18.8397 29.3295C17.5446 28.477 17.1765 26.7137 18.0205 25.386L24.5824 15.0522C25.4253 13.7256 27.1597 13.3404 28.4548 14.1897Z", fill: outerColor })] }));
13
+ export default function AutomatticIconLogo({ innerColor = '#000000', outerColor = '#000000', }) {
14
+ return (_jsxs(SVG, { clipRule: "evenodd", fill: "none", fillRule: "evenodd", height: "43", viewBox: "0 0 171 156", width: "47", xmlns: "http://www.w3.org/2000/svg", children: [_jsx(Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M85.398 155.632C34.1992 155.632 1.01562 118.788 1.01562 80.3993V75.6007C1.01562 36.5611 34.1992 0.368164 85.398 0.368164C136.597 0.368164 169.984 36.5611 169.984 75.6007V80.3993C169.984 118.666 136.8 155.632 85.398 155.632ZM145.191 76.048C145.191 48.0696 122.323 23.1819 85.398 23.1819C48.4731 23.1819 25.9304 48.1103 25.9304 76.048V79.5047C25.9304 107.483 48.7984 132.859 85.398 132.859C121.998 132.859 145.191 107.483 145.191 79.5047V76.048Z", fill: innerColor }), _jsx(Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M62.7978 91.4965L87.6364 53.0284C91.1593 47.5724 98.4381 46.0054 103.894 49.5283L103.928 49.5504C109.384 53.0732 110.951 60.352 107.428 65.808L82.5899 104.276C79.067 109.732 71.7882 111.299 66.3322 107.776L66.298 107.754C60.842 104.231 59.275 96.9525 62.7978 91.4965Z", fill: outerColor })] }));
13
15
  }
@@ -1,8 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getScoreTier } from '@automattic/jetpack-boost-score-api';
2
3
  import { __ } from '@wordpress/i18n';
3
4
  import { Icon, desktop, mobile } from '@wordpress/icons';
4
5
  import clsx from 'clsx';
5
6
  import './style.scss';
7
+ const TIER_FILLS = {
8
+ good: 'fill-good',
9
+ medium: 'fill-mediocre',
10
+ poor: 'fill-bad',
11
+ };
6
12
  export const BoostScoreBar = ({ score, isLoading, showPrevScores, active, prevScore = 0, scoreBarType, noBoostScoreTooltip, }) => {
7
13
  if (!active) {
8
14
  return null;
@@ -22,13 +28,7 @@ export const BoostScoreBar = ({ score, isLoading, showPrevScores, active, prevSc
22
28
  if (isLoading && !score) {
23
29
  return 'fill-loading';
24
30
  }
25
- if (score > 70) {
26
- return 'fill-good';
27
- }
28
- if (score > 50) {
29
- return 'fill-mediocre';
30
- }
31
- return 'fill-bad';
31
+ return TIER_FILLS[getScoreTier(score)];
32
32
  };
33
33
  return (_jsxs("div", { className: clsx('jb-score-bar', `jb-score-bar--${scoreBarType}`), children: [_jsxs("div", { className: "jb-score-bar__label", children: [getIcon(), _jsx("div", { children: scoreBarType === 'desktop' ? desktopIconLabel : mobileIconLabel })] }), _jsx("div", { className: "jb-score-bar__bounds", children: _jsxs("div", { className: clsx('jb-score-bar__filler', getFillColor()), style: { width: `${score}%` }, children: [isLoading ? (_jsx("div", { className: "jb-score-bar__loading", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 128 128", children: _jsxs("g", { children: [_jsx("path", { d: "M64 0a7 7 0 11-7 7 7 7 0 017-7zm29.86 12.2a2.8 2.8 0 11-3.83 1.02 2.8 2.8 0 013.83-1.02zm22.16 21.68a3.15 3.15 0 11-4.3-1.15 3.15 3.15 0 014.3 1.15zm.87 60.53a4.2 4.2 0 11-1.57-5.7 4.2 4.2 0 011.54 5.73zm7.8-30.5a3.85 3.85 0 11-3.85-3.85 3.85 3.85 0 013.85 3.84zm-30 53.2a4.55 4.55 0 111.66-6.23 4.55 4.55 0 01-1.67 6.22zM64 125.9a4.9 4.9 0 114.9-4.9 4.9 4.9 0 01-4.9 4.9zm-31.06-8.22a5.25 5.25 0 117.17-1.93 5.25 5.25 0 01-7.14 1.93zM9.9 95.1a5.6 5.6 0 117.65 2.06A5.6 5.6 0 019.9 95.1zM1.18 63.9a5.95 5.95 0 115.95 5.94 5.95 5.95 0 01-5.96-5.94zm8.1-31.6a6.3 6.3 0 112.32 8.6 6.3 6.3 0 01-2.3-8.6zM32.25 8.87a6.65 6.65 0 11-2.44 9.1 6.65 6.65 0 012.46-9.1z" }), _jsx("animateTransform", { attributeName: "transform", type: "rotate", values: "0 64 64;30 64 64;60 64 64;90 64 64;120 64 64;150 64 64;180 64 64;210 64 64;240 64 64;270 64 64;300 64 64;330 64 64", calcMode: "discrete", dur: "1080ms", repeatCount: "indefinite" })] }) }) })) : (_jsx("div", { className: "jb-score-bar__score", children: score })), showPrevScores && !!prevScore && prevScore < score && (_jsxs("div", { className: "jb-score-bar__no_boost_score", style: { left: `min(${prevScoreOffset}%, calc( 100% - var(--clearance-space))` }, children: [prevScore, noBoostScoreTooltip && (_jsx("div", { role: "tooltip", className: "jb-score-bar__no_boost_score_tooltip", children: noBoostScoreTooltip }))] }))] }) })] }));
34
34
  };
@@ -1,54 +1,26 @@
1
- @use "@automattic/jetpack-base-styles/gutenberg-base-styles" as gb;
1
+ @use "../selectable-card/selectable-card";
2
2
 
3
3
  .jp-checkbox-card-grid {
4
- display: grid;
5
- grid-template-columns: repeat(2, minmax(0, 1fr));
6
- column-gap: var(--wpds-dimension-gap-md);
7
- row-gap: var(--wpds-dimension-gap-2xl);
8
-
9
- @media (max-width: 720px) {
10
- grid-template-columns: minmax(0, 1fr);
11
- }
4
+
5
+ @include selectable-card.grid;
12
6
  }
13
7
 
14
8
  .jp-checkbox-card {
15
9
 
16
- &[data-checked="true"] {
17
- border-color: var(--wpds-color-stroke-interactive-brand-active);
18
- }
19
-
20
- &:hover:not([data-checked="true"]) {
21
- border-color: var(--wpds-color-stroke-surface-neutral-strong);
22
- }
23
-
24
- &:has(:focus-visible) {
25
-
26
- @include gb.outset-ring__focus();
27
- }
28
-
29
- @media (prefers-reduced-motion: no-preference) {
30
- transition: border-color 0.15s ease-out;
31
- }
10
+ @include selectable-card.card('[data-checked="true"]');
32
11
  }
33
12
 
34
13
  .jp-checkbox-card__surface {
35
- position: relative;
14
+
15
+ @include selectable-card.surface;
36
16
  }
37
17
 
38
18
  .jp-checkbox-card__illustration {
39
- display: flex;
40
- aspect-ratio: 294 / 192;
41
- background: var(--wpds-color-background-surface-neutral-weak);
42
- cursor: var(--wpds-cursor-control);
43
-
44
- > svg {
45
- inline-size: 100%;
46
- block-size: 100%;
47
- }
19
+
20
+ @include selectable-card.illustration;
48
21
  }
49
22
 
50
23
  .jp-checkbox-card__checkbox {
51
- position: absolute;
52
- inset-block-start: var(--wpds-dimension-padding-md);
53
- inset-inline-start: var(--wpds-dimension-padding-md);
24
+
25
+ @include selectable-card.control;
54
26
  }
@@ -1,19 +1,2 @@
1
1
  import './style.scss';
2
- import type { JetpackFooterProps } from './types.ts';
3
- import type { FC } from 'react';
4
- declare global {
5
- interface Window {
6
- JetpackNetworkAdminData?: {
7
- sitesUrl: string;
8
- settingsUrl: string;
9
- };
10
- }
11
- }
12
- /**
13
- * JetpackFooter component displays a tiny Jetpack logo with the product name on the left and the Automattic Airline "by line" on the right.
14
- *
15
- * @param {JetpackFooterProps} props - Component properties.
16
- * @return {ReactNode} JetpackFooter component.
17
- */
18
- declare const JetpackFooter: FC<JetpackFooterProps>;
19
- export default JetpackFooter;
2
+ export { default } from './jetpack-footer.tsx';
@@ -1,40 +1,2 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { isWpcomPlatformSite, getAdminUrl, getScriptData } from '@automattic/jetpack-script-data';
3
- import { __ } from '@wordpress/i18n';
4
- import { Stack, Text, Link } from '@wordpress/ui';
5
- import clsx from 'clsx';
6
- import { getRedirectUrl } from '../../index.js';
7
- import AutomatticBylineLogo from '../automattic-byline-logo/index.js';
8
1
  import './style.scss';
9
- import JetpackLogo from '../jetpack-logo/index.js';
10
- /**
11
- * JetpackFooter component displays a tiny Jetpack logo with the product name on the left and the Automattic Airline "by line" on the right.
12
- *
13
- * @param {JetpackFooterProps} props - Component properties.
14
- * @return {ReactNode} JetpackFooter component.
15
- */
16
- const JetpackFooter = ({ className, menu, ...otherProps }) => {
17
- let items = [];
18
- if (getScriptData()?.myJetpack?.isAvailable === true &&
19
- !isWpcomPlatformSite() &&
20
- !window?.JetpackNetworkAdminData) {
21
- items = [
22
- {
23
- label: __('Features', 'jetpack-components'),
24
- href: getAdminUrl('admin.php?page=my-jetpack#/features'),
25
- },
26
- {
27
- label: __('Help', 'jetpack-components'),
28
- href: getAdminUrl('admin.php?page=my-jetpack#/help'),
29
- },
30
- ...items,
31
- ];
32
- }
33
- if (menu) {
34
- items = [...items, ...menu];
35
- }
36
- return (_jsxs(Stack, { render: _jsx("footer", {}), className: clsx('jetpack-footer', className), "aria-label": __('Jetpack', 'jetpack-components'), role: "contentinfo", direction: "row", justify: "flex-start", align: "center", wrap: "wrap", gap: "xl", ...otherProps, children: [_jsxs(Stack, { className: "jetpack-footer__logo", direction: "row", gap: "sm", align: "center", children: [_jsx(JetpackLogo, { showText: false, height: 16, "aria-hidden": "true" }), _jsx(Text, { variant: "body-md", children: "Jetpack" })] }), _jsx(Stack, { render: _jsx("ul", {}), direction: "row", gap: "lg", wrap: "wrap", children: items.map(item => {
37
- return (_jsx("li", { children: _jsx(Text, { variant: "body-md", className: "jetpack-footer__menu-item", render: !item.href ? (_jsx(Link, { render: _jsx("span", {}), tabIndex: 0, title: item.title || '', onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined, role: "button" })) : (_jsx(Link, { href: item.href, title: item.title || '', onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })), children: item.label }) }, item.label));
38
- }) }), _jsx("a", { className: "jetpack-footer__a8c", href: getRedirectUrl('a8c-about'), rel: "noopener noreferrer", target: "_blank", children: _jsx(AutomatticBylineLogo, { height: 8 }) })] }));
39
- };
40
- export default JetpackFooter;
2
+ export { default } from './jetpack-footer.js';
@@ -0,0 +1,18 @@
1
+ import type { JetpackFooterProps } from './types.ts';
2
+ import type { FC } from 'react';
3
+ declare global {
4
+ interface Window {
5
+ JetpackNetworkAdminData?: {
6
+ sitesUrl: string;
7
+ settingsUrl: string;
8
+ };
9
+ }
10
+ }
11
+ /**
12
+ * JetpackFooter component displays a tiny Jetpack logo with the product name on the left and the Automattic Airline "by line" on the right.
13
+ *
14
+ * @param {JetpackFooterProps} props - Component properties.
15
+ * @return {ReactNode} JetpackFooter component.
16
+ */
17
+ declare const JetpackFooter: FC<JetpackFooterProps>;
18
+ export default JetpackFooter;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isWpcomPlatformSite, getAdminUrl, getScriptData } from '@automattic/jetpack-script-data';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { Stack, Text, Link } from '@wordpress/ui';
5
+ import clsx from 'clsx';
6
+ import getRedirectUrl from '../../tools/jp-redirect/index.js';
7
+ import AutomatticBylineLogo from '../automattic-byline-logo/index.js';
8
+ import JetpackLogo from '../jetpack-logo/index.js';
9
+ /**
10
+ * JetpackFooter component displays a tiny Jetpack logo with the product name on the left and the Automattic Airline "by line" on the right.
11
+ *
12
+ * @param {JetpackFooterProps} props - Component properties.
13
+ * @return {ReactNode} JetpackFooter component.
14
+ */
15
+ const JetpackFooter = ({ className, menu, ...otherProps }) => {
16
+ let items = [];
17
+ if (getScriptData()?.myJetpack?.isAvailable === true &&
18
+ !isWpcomPlatformSite() &&
19
+ !window?.JetpackNetworkAdminData) {
20
+ items = [
21
+ {
22
+ label: __('Features', 'jetpack-components'),
23
+ href: getAdminUrl('admin.php?page=my-jetpack#/features'),
24
+ },
25
+ {
26
+ label: __('Help', 'jetpack-components'),
27
+ href: getAdminUrl('admin.php?page=my-jetpack#/help'),
28
+ },
29
+ ...items,
30
+ ];
31
+ }
32
+ if (menu) {
33
+ items = [...items, ...menu];
34
+ }
35
+ return (_jsxs(Stack, { render: _jsx("footer", {}), className: clsx('jetpack-footer', className), "aria-label": __('Jetpack', 'jetpack-components'), role: "contentinfo", direction: "row", justify: "flex-start", align: "center", wrap: "wrap", gap: "xl", ...otherProps, children: [_jsxs(Stack, { className: "jetpack-footer__logo", direction: "row", gap: "sm", align: "center", children: [_jsx(JetpackLogo, { showText: false, height: 16, "aria-hidden": "true" }), _jsx(Text, { variant: "body-md", children: "Jetpack" })] }), _jsx(Stack, { render: _jsx("ul", {}), direction: "row", gap: "lg", wrap: "wrap", children: items.map(item => {
36
+ return (_jsx("li", { children: _jsx(Text, { variant: "body-md", className: "jetpack-footer__menu-item", render: !item.href ? (_jsx(Link, { render: _jsx("span", {}), tabIndex: 0, title: item.title || '', onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined, role: "button" })) : (_jsx(Link, { href: item.href, title: item.title || '', onClick: item.onClick || undefined, onKeyDown: item.onKeyDown || undefined })), children: item.label }) }, item.label));
37
+ }) }), _jsx("a", { className: "jetpack-footer__a8c", href: getRedirectUrl('a8c-about'), rel: "noopener noreferrer", target: "_blank", children: _jsx(AutomatticBylineLogo, { height: 8 }) })] }));
38
+ };
39
+ export default JetpackFooter;
@@ -0,0 +1,9 @@
1
+ import type { PeriodSelectorProps } from './types.ts';
2
+ import type { JSX } from 'react';
3
+ /**
4
+ * A dropdown of period presets, with optional buttons to step to the previous or next period.
5
+ *
6
+ * @param {PeriodSelectorProps} props - Component props.
7
+ * @return {JSX.Element} The selector.
8
+ */
9
+ export default function PeriodSelector<Value extends string>({ presets, value, label, onChange, onPrevious, onNext, isPreviousDisabled, isNextDisabled, disabled, className, }: PeriodSelectorProps<Value>): JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { __, isRTL, sprintf } from '@wordpress/i18n';
3
+ import { calendar, chevronLeft, chevronRight } from '@wordpress/icons';
4
+ import { Button, IconButton, Menu, Stack } from '@wordpress/ui';
5
+ /**
6
+ * A dropdown of period presets, with optional buttons to step to the previous or next period.
7
+ *
8
+ * @param {PeriodSelectorProps} props - Component props.
9
+ * @return {JSX.Element} The selector.
10
+ */
11
+ export default function PeriodSelector({ presets, value, label, onChange, onPrevious, onNext, isPreviousDisabled = false, isNextDisabled = false, disabled = false, className, }) {
12
+ const selected = presets.find(preset => preset.value === value);
13
+ const selectedLabel = selected?.label ?? __('Select period', 'jetpack-components');
14
+ const [previousIcon, nextIcon] = isRTL()
15
+ ? [chevronRight, chevronLeft]
16
+ : [chevronLeft, chevronRight];
17
+ return (_jsxs(Stack, { direction: "row", align: "center", gap: "xs", className: className, children: [_jsxs(Menu.Root, { children: [_jsxs(Menu.Trigger, { render: _jsx(Button, { variant: "outline", tone: "neutral", size: "compact" }), disabled: disabled, "aria-label": label &&
18
+ sprintf(
19
+ /* translators: %1$s: What the control picks, such as "Date range". %2$s: The selected period, such as "Last 30 days". */
20
+ __('%1$s: %2$s', 'jetpack-components'), label, selectedLabel), children: [_jsx(Button.Icon, { icon: calendar }), selectedLabel] }), _jsx(Menu.Popup, { positioner: _jsx(Menu.Positioner, { align: "end" }), "aria-label": label, children: _jsx(Menu.RadioGroup, { value: value, onValueChange: onChange, children: presets.map(preset => (_jsx(Menu.RadioItem, { value: preset.value, closeOnClick: true, children: _jsx(Menu.ItemLabel, { children: preset.label }) }, preset.value))) }) })] }), onPrevious && (_jsx(IconButton, { variant: "outline", tone: "neutral", size: "compact", icon: previousIcon, label: __('Previous period', 'jetpack-components'), onClick: onPrevious, disabled: disabled || isPreviousDisabled })), onNext && (_jsx(IconButton, { variant: "outline", tone: "neutral", size: "compact", icon: nextIcon, label: __('Next period', 'jetpack-components'), onClick: onNext, disabled: disabled || isNextDisabled }))] }));
21
+ }
@@ -0,0 +1,28 @@
1
+ export type PeriodPreset<Value extends string = string> = {
2
+ /** Identifies the preset; passed to `onChange`. */
3
+ value: Value;
4
+ /** The preset's name, such as "Last 7 days". */
5
+ label: string;
6
+ };
7
+ export type PeriodSelectorProps<Value extends string = string> = {
8
+ /** The presets the dropdown offers, in order. */
9
+ presets: PeriodPreset<Value>[];
10
+ /** Names what the control picks, such as "Date range"; screen readers hear it before the selected period. */
11
+ label?: string;
12
+ /** The selected preset's value. */
13
+ value: Value;
14
+ /** Called with the chosen preset's value. */
15
+ onChange: (value: Value) => void;
16
+ /** Steps to the previous period; the button shows only when set. */
17
+ onPrevious?: () => void;
18
+ /** Steps to the next period; the button shows only when set. */
19
+ onNext?: () => void;
20
+ /** Whether there is no previous period to step to. */
21
+ isPreviousDisabled?: boolean;
22
+ /** Whether there is no next period to step to, such as on the latest one. */
23
+ isNextDisabled?: boolean;
24
+ /** Whether the dropdown and both step buttons are disabled. */
25
+ disabled?: boolean;
26
+ /** Class name for the wrapper. */
27
+ className?: string;
28
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import './style.scss';
2
+ import type { RadioCardGridProps, RadioCardProps } from './types.ts';
3
+ import type { JSX } from 'react';
4
+ /**
5
+ * Lays out radio cards in two columns, or one on narrow screens.
6
+ *
7
+ * @param {RadioCardGridProps} props - Component props.
8
+ * @return {JSX.Element} The grid.
9
+ */
10
+ export declare function RadioCardGrid({ children, className }: RadioCardGridProps): JSX.Element;
11
+ /**
12
+ * A card with a radio, picked by a click on its illustration. Render it in a `RadioGroup` inside a `Field.Root`.
13
+ *
14
+ * @param {RadioCardProps} props - Component props.
15
+ * @return {JSX.Element} The card.
16
+ */
17
+ export default function RadioCard({ id, value, title, illustration, previewUrl, onPreviewClick, disabled, }: RadioCardProps): JSX.Element;
18
+ export { RadioCard };
19
+ export type { RadioCardProps, RadioCardGridProps };
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback } from '@wordpress/element';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { Card, Field, Link, Radio, Stack, Text } from '@wordpress/ui';
5
+ import clsx from 'clsx';
6
+ import './style.scss';
7
+ /**
8
+ * Lays out radio cards in two columns, or one on narrow screens.
9
+ *
10
+ * @param {RadioCardGridProps} props - Component props.
11
+ * @return {JSX.Element} The grid.
12
+ */
13
+ export function RadioCardGrid({ children, className }) {
14
+ return _jsx("div", { className: clsx('jp-radio-card-grid', className), children: children });
15
+ }
16
+ /**
17
+ * A card with a radio, picked by a click on its illustration. Render it in a `RadioGroup` inside a `Field.Root`.
18
+ *
19
+ * @param {RadioCardProps} props - Component props.
20
+ * @return {JSX.Element} The card.
21
+ */
22
+ export default function RadioCard({ id, value, title, illustration, previewUrl, onPreviewClick, disabled, }) {
23
+ const titleId = `${id}__title`;
24
+ const handlePreviewClick = useCallback(() => {
25
+ onPreviewClick?.(value);
26
+ }, [value, onPreviewClick]);
27
+ return (_jsxs(Field.Item, { disabled: disabled, children: [_jsx(Card.Root, { className: "jp-radio-card", children: _jsx(Card.Content, { children: _jsxs(Card.FullBleed, { className: "jp-radio-card__surface", children: [_jsx(Field.Label, { className: "jp-radio-card__illustration", children: illustration }), _jsx(Radio, { id: id, className: "jp-radio-card__radio", value: value, disabled: disabled, "aria-labelledby": titleId })] }) }) }), _jsxs(Stack, { gap: "xs", direction: "column", children: [_jsx(Text, { id: titleId, variant: "body-sm", children: title }), previewUrl && (_jsx(Link, { href: previewUrl, onClick: handlePreviewClick, children: __('Preview and edit', 'jetpack-components') }))] })] }));
28
+ }
29
+ export { RadioCard };
@@ -0,0 +1,26 @@
1
+ @use "../selectable-card/selectable-card";
2
+
3
+ .jp-radio-card-grid {
4
+
5
+ @include selectable-card.grid;
6
+ }
7
+
8
+ .jp-radio-card {
9
+
10
+ @include selectable-card.card( ":has(.jp-radio-card__radio[data-checked])" );
11
+ }
12
+
13
+ .jp-radio-card__surface {
14
+
15
+ @include selectable-card.surface;
16
+ }
17
+
18
+ .jp-radio-card__illustration {
19
+
20
+ @include selectable-card.illustration;
21
+ }
22
+
23
+ .jp-radio-card__radio {
24
+
25
+ @include selectable-card.control;
26
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from 'react';
2
+ export type RadioCardProps = {
3
+ /**
4
+ * Id of the radio; the title's id is built from it.
5
+ */
6
+ id: string;
7
+ /**
8
+ * The value the surrounding `RadioGroup` takes when this card is picked. Also passed to `onPreviewClick`.
9
+ */
10
+ value: string;
11
+ /**
12
+ * Caption under the card, and the radio's accessible name.
13
+ */
14
+ title: string;
15
+ /**
16
+ * Picture filling the card, drawn at a 294×192 aspect ratio.
17
+ */
18
+ illustration?: ReactNode;
19
+ /**
20
+ * Shows a "Preview and edit" link to this URL under the title.
21
+ */
22
+ previewUrl?: string;
23
+ /**
24
+ * Called with the card's `value` when the "Preview and edit" link is clicked.
25
+ */
26
+ onPreviewClick?: (value: string) => void;
27
+ /**
28
+ * Disables the radio.
29
+ */
30
+ disabled?: boolean;
31
+ };
32
+ export type RadioCardGridProps = {
33
+ /**
34
+ * The `RadioCard`s to lay out.
35
+ */
36
+ children: ReactNode;
37
+ /**
38
+ * Extra class for the grid.
39
+ */
40
+ className?: string;
41
+ };
@@ -0,0 +1 @@
1
+ export {};