@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
@@ -0,0 +1,57 @@
1
+ @use "@automattic/jetpack-base-styles/gutenberg-base-styles" as gb;
2
+
3
+ // Shared by CheckboxCard and RadioCard, which differ only in their control.
4
+
5
+ @mixin grid {
6
+ display: grid;
7
+ grid-template-columns: repeat(2, minmax(0, 1fr));
8
+ column-gap: var(--wpds-dimension-gap-md);
9
+ row-gap: var(--wpds-dimension-gap-2xl);
10
+
11
+ @media (max-width: 720px) {
12
+ grid-template-columns: minmax(0, 1fr);
13
+ }
14
+ }
15
+
16
+ // $selected matches the card while its control is selected.
17
+ @mixin card($selected) {
18
+
19
+ &#{$selected} {
20
+ border-color: var(--wpds-color-stroke-interactive-brand-active);
21
+ }
22
+
23
+ &:hover:not(#{$selected}) {
24
+ border-color: var(--wpds-color-stroke-surface-neutral-strong);
25
+ }
26
+
27
+ &:has(:focus-visible) {
28
+
29
+ @include gb.outset-ring__focus();
30
+ }
31
+
32
+ @media (prefers-reduced-motion: no-preference) {
33
+ transition: border-color 0.15s ease-out;
34
+ }
35
+ }
36
+
37
+ @mixin surface {
38
+ position: relative;
39
+ }
40
+
41
+ @mixin illustration {
42
+ display: flex;
43
+ aspect-ratio: 294 / 192;
44
+ background: var(--wpds-color-background-surface-neutral-weak);
45
+ cursor: var(--wpds-cursor-control);
46
+
47
+ > svg {
48
+ inline-size: 100%;
49
+ block-size: 100%;
50
+ }
51
+ }
52
+
53
+ @mixin control {
54
+ position: absolute;
55
+ inset-block-start: var(--wpds-dimension-padding-md);
56
+ inset-inline-start: var(--wpds-dimension-padding-md);
57
+ }
@@ -1,16 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createInterpolateElement } from '@wordpress/element';
3
3
  import { __, sprintf } from '@wordpress/i18n';
4
- import { Link } from '@wordpress/ui';
4
+ import { Link, Text } from '@wordpress/ui';
5
5
  import clsx from 'clsx';
6
6
  // Deep import rather than the package barrel (`../../index.ts`): the barrel
7
7
  // pulls in heavy, asset-importing components (e.g. boost-score-graph) that the
8
8
  // wp-build esbuild pipeline can't bundle, which breaks any wp-build dashboard
9
9
  // that deep-imports pricing-table (which renders TermsOfService).
10
10
  import getRedirectUrl from '../../tools/jp-redirect/index.js';
11
- import Text from '../text/index.js';
12
11
  import './styles.scss';
13
- const TermsOfService = ({ className, multipleButtons, agreeButtonLabel, isTextOnly, ...textProps }) => {
12
+ const TermsOfService = ({ className, variant = 'body-lg', multipleButtons, agreeButtonLabel, isTextOnly, }) => {
14
13
  const getTOSContent = () => {
15
14
  if (isTextOnly) {
16
15
  return _jsx(TermsOfServiceTextOnly, {});
@@ -20,7 +19,7 @@ const TermsOfService = ({ className, multipleButtons, agreeButtonLabel, isTextOn
20
19
  }
21
20
  return _jsx(SingleButtonText, { agreeButtonLabel: agreeButtonLabel });
22
21
  };
23
- return (_jsx(Text, { className: clsx(className, 'terms-of-service'), ...textProps, children: getTOSContent() }));
22
+ return (_jsx(Text, { variant: variant, render: _jsx("p", {}), className: clsx(className, 'terms-of-service'), children: getTOSContent() }));
24
23
  };
25
24
  const MultipleButtonsText = ({ multipleButtonsLabels }) => {
26
25
  if (Array.isArray(multipleButtonsLabels) && multipleButtonsLabels.length > 1) {
@@ -1,6 +1,9 @@
1
- .terms-of-service {
2
- font-size: var(--font-body);
1
+ // No specificity, so any consumer class sets the colour without a tie.
2
+ :where(.terms-of-service) {
3
3
  color: var(--jp-black);
4
+ }
5
+
6
+ .terms-of-service {
4
7
 
5
8
  .terms-of-service__link {
6
9
  white-space: nowrap;
@@ -1,4 +1,5 @@
1
- import { TextProps } from '../text/types.ts';
1
+ import type { Text } from '@wordpress/ui';
2
+ import type { ComponentProps } from 'react';
2
3
  type MultipleButtonsProps = {
3
4
  /**
4
5
  * Indicates whether there are multiple buttons present that would imply agreement if clicked.
@@ -29,5 +30,14 @@ type OnlyTextProps = {
29
30
  multipleButtons?: undefined;
30
31
  agreeButtonLabel?: undefined;
31
32
  };
32
- export type TermsOfServiceProps = Pick<TextProps, 'variant' | 'm' | 'mt' | 'mr' | 'mb' | 'ml' | 'mx' | 'my' | 'p' | 'pt' | 'pr' | 'pb' | 'pl' | 'px' | 'py' | 'className' | 'component'> & (MultipleButtonsProps | SingleButtonProps | OnlyTextProps);
33
+ export type TermsOfServiceProps = {
34
+ /**
35
+ * HTML class.
36
+ */
37
+ className?: string;
38
+ /**
39
+ * The `@wordpress/ui` Text variant. Defaults to `body-lg`.
40
+ */
41
+ variant?: ComponentProps<typeof Text>['variant'];
42
+ } & (MultipleButtonsProps | SingleButtonProps | OnlyTextProps);
33
43
  export {};
package/build/index.d.ts CHANGED
@@ -11,12 +11,15 @@ export { default as Gravatar } from './components/gravatar/index.tsx';
11
11
  export type { GravatarProps } from './components/gravatar/index.tsx';
12
12
  export { default as CheckboxCard, CheckboxCardGrid } from './components/checkbox-card/index.tsx';
13
13
  export type { CheckboxCardProps, CheckboxCardGridProps } from './components/checkbox-card/types.ts';
14
+ export { default as RadioCard, RadioCardGrid } from './components/radio-card/index.tsx';
15
+ export type { RadioCardProps, RadioCardGridProps } from './components/radio-card/types.ts';
14
16
  export { default as IconTooltip } from './components/icon-tooltip/index.tsx';
15
17
  export { default as ActionButton } from './components/action-button/index.tsx';
16
18
  export { default as PricingCard } from './components/pricing-card/index.tsx';
17
19
  export { default as AdminSection } from './components/admin-section/basic/index.tsx';
18
20
  export { default as AdminSectionHero } from './components/admin-section/hero/index.tsx';
19
21
  export { default as AdminPage } from './components/admin-page/index.tsx';
22
+ export { default as PageActionButton } from './components/admin-page/page-action-button.tsx';
20
23
  export { default as DecorativeCard } from './components/decorative-card/index.tsx';
21
24
  export { default as Col } from './components/layout/col/index.tsx';
22
25
  export { default as Testimonials } from './components/testimonials/index.tsx';
@@ -26,7 +29,6 @@ export { default as JitmSlot } from './components/jitm-slot/index.tsx';
26
29
  export * from './components/icons/index.tsx';
27
30
  export { default as ThemeProvider } from './components/theme-provider/index.tsx';
28
31
  export { default as Text, H2, H3, Title } from './components/text/index.tsx';
29
- export { default as NumberControl } from './components/number-control/index.tsx';
30
32
  export { default as QRCode } from './components/qr-code/index.tsx';
31
33
  export { default as Button } from './components/button/index.tsx';
32
34
  export type { ButtonProps } from './components/button/types.ts';
@@ -45,6 +47,8 @@ export { default as ActionPopover } from './components/action-popover/index.tsx'
45
47
  export { default as UpsellBanner } from './components/upsell-banner/index.tsx';
46
48
  export { getUserLocale, cleanLocale } from './lib/locale/index.ts';
47
49
  export { default as StatCard } from './components/stat-card/index.tsx';
50
+ export { default as PeriodSelector } from './components/period-selector/index.tsx';
51
+ export type { PeriodPreset, PeriodSelectorProps } from './components/period-selector/types.ts';
48
52
  export { default as DiffViewer } from './components/diff-viewer/index.tsx';
49
53
  export { default as MarkedLines } from './components/marked-lines/index.tsx';
50
54
  export { default as DetailsViewer } from './components/details-viewer/index.tsx';
package/build/index.js CHANGED
@@ -23,12 +23,14 @@ export { default as AutomatticForAgenciesLogo } from './components/automattic-fo
23
23
  export { default as JetpackFooter } from './components/jetpack-footer/index.js';
24
24
  export { default as Gravatar } from './components/gravatar/index.js';
25
25
  export { default as CheckboxCard, CheckboxCardGrid } from './components/checkbox-card/index.js';
26
+ export { default as RadioCard, RadioCardGrid } from './components/radio-card/index.js';
26
27
  export { default as IconTooltip } from './components/icon-tooltip/index.js';
27
28
  export { default as ActionButton } from './components/action-button/index.js';
28
29
  export { default as PricingCard } from './components/pricing-card/index.js';
29
30
  export { default as AdminSection } from './components/admin-section/basic/index.js';
30
31
  export { default as AdminSectionHero } from './components/admin-section/hero/index.js';
31
32
  export { default as AdminPage } from './components/admin-page/index.js';
33
+ export { default as PageActionButton } from './components/admin-page/page-action-button.js';
32
34
  export { default as DecorativeCard } from './components/decorative-card/index.js';
33
35
  export { default as Col } from './components/layout/col/index.js';
34
36
  export { default as Testimonials } from './components/testimonials/index.js';
@@ -38,7 +40,6 @@ export { default as JitmSlot } from './components/jitm-slot/index.js';
38
40
  export * from './components/icons/index.js';
39
41
  export { default as ThemeProvider } from './components/theme-provider/index.js';
40
42
  export { default as Text, H2, H3, Title } from './components/text/index.js';
41
- export { default as NumberControl } from './components/number-control/index.js';
42
43
  export { default as QRCode } from './components/qr-code/index.js';
43
44
  export { default as Button } from './components/button/index.js';
44
45
  export { default as LoadingPlaceholder } from './components/loading-placeholder/index.js';
@@ -56,6 +57,7 @@ export { default as ActionPopover } from './components/action-popover/index.js';
56
57
  export { default as UpsellBanner } from './components/upsell-banner/index.js';
57
58
  export { getUserLocale, cleanLocale } from './lib/locale/index.js';
58
59
  export { default as StatCard } from './components/stat-card/index.js';
60
+ export { default as PeriodSelector } from './components/period-selector/index.js';
59
61
  export { default as DiffViewer } from './components/diff-viewer/index.js';
60
62
  export { default as MarkedLines } from './components/marked-lines/index.js';
61
63
  export { default as DetailsViewer } from './components/details-viewer/index.js';
@@ -36,13 +36,19 @@ const AdminPage: FC< AdminPageProps > = ( {
36
36
  actions,
37
37
  globalControls,
38
38
  breadcrumbs,
39
+ ariaLabel,
39
40
  tabs,
40
41
  showBottomBorder = true,
41
42
  unwrapped = false,
42
43
  } ) => {
44
+ // Only set what was passed: the client is shared, so an empty default would wipe what the page set.
43
45
  useEffect( () => {
44
- restApi.setApiRoot( apiRoot );
45
- restApi.setApiNonce( apiNonce );
46
+ if ( apiRoot ) {
47
+ restApi.setApiRoot( apiRoot );
48
+ }
49
+ if ( apiNonce ) {
50
+ restApi.setApiNonce( apiNonce );
51
+ }
46
52
  }, [ apiRoot, apiNonce ] );
47
53
 
48
54
  // `jp-admin-page` is a stable, non-hashed hook for global stylesheets and
@@ -80,6 +86,7 @@ const AdminPage: FC< AdminPageProps > = ( {
80
86
  visual={ logo || <JetpackLogo showText={ false } height={ 20 } /> }
81
87
  breadcrumbs={ breadcrumbs }
82
88
  title={ title }
89
+ ariaLabel={ ariaLabel }
83
90
  subTitle={ subTitle }
84
91
  actions={
85
92
  globalControls ? (
@@ -104,7 +111,7 @@ const AdminPage: FC< AdminPageProps > = ( {
104
111
  { unwrapped ? (
105
112
  children
106
113
  ) : (
107
- <Container fluid horizontalSpacing={ 0 }>
114
+ <Container className="jp-admin-page__content" fluid horizontalSpacing={ 0 }>
108
115
  <Col>{ children }</Col>
109
116
  </Container>
110
117
  ) }
@@ -146,3 +153,4 @@ const AdminPage: FC< AdminPageProps > = ( {
146
153
  };
147
154
 
148
155
  export default AdminPage;
156
+ export { default as PageActionButton } from './page-action-button.tsx';
@@ -0,0 +1,69 @@
1
+ import { useMergeRefs, useViewportMatch } from '@wordpress/compose';
2
+ import { Button, IconButton } from '@wordpress/ui';
3
+ import { forwardRef, useCallback, useRef } from 'react';
4
+ import type { ComponentProps, ForwardRefExoticComponent, ForwardedRef, RefAttributes } from 'react';
5
+
6
+ type PageActionButtonProps = ComponentProps< typeof IconButton >;
7
+
8
+ /**
9
+ * A page action for `AdminPage`'s `actions`: a labelled button, or an icon button on narrow screens.
10
+ *
11
+ * The label stays the button's accessible name, and becomes its tooltip when only the icon shows.
12
+ *
13
+ * @param props - Any `Button` prop, passed to whichever button renders, plus the ones below.
14
+ * @param props.label - The button's text, or its tooltip when only the icon shows.
15
+ * @param props.icon - The icon shown on narrow screens.
16
+ * @param props.shortcut - Shown in the icon button's tooltip, so on narrow screens only.
17
+ * @param props.positioner - Positions the icon button's tooltip, so on narrow screens only.
18
+ * @param ref - Forwarded to the rendered button.
19
+ * @return The button.
20
+ */
21
+ function PageActionButton(
22
+ { label, icon, shortcut, positioner, ...buttonProps }: PageActionButtonProps,
23
+ ref: ForwardedRef< HTMLButtonElement >
24
+ ) {
25
+ // On phones, page actions show only their icon so the header's title keeps its room.
26
+ const isNarrowScreen = useViewportMatch( 'mobile', '<' );
27
+
28
+ // Crossing the breakpoint swaps in a new button: move focus to it if the old one had it.
29
+ const button = useRef< HTMLButtonElement | null >( null );
30
+ const hadFocus = useRef( false );
31
+ const keepFocus = useCallback( ( node: HTMLButtonElement | null ) => {
32
+ const previous = button.current;
33
+ if ( node ) {
34
+ if ( hadFocus.current ) {
35
+ node.focus();
36
+ }
37
+ hadFocus.current = false;
38
+ } else {
39
+ hadFocus.current = previous?.ownerDocument.activeElement === previous;
40
+ }
41
+ button.current = node;
42
+ }, [] );
43
+ const mergedRef = useMergeRefs( [ ref, keepFocus ] );
44
+
45
+ if ( isNarrowScreen ) {
46
+ return (
47
+ <IconButton
48
+ ref={ mergedRef }
49
+ label={ label }
50
+ icon={ icon }
51
+ shortcut={ shortcut }
52
+ positioner={ positioner }
53
+ { ...buttonProps }
54
+ />
55
+ );
56
+ }
57
+
58
+ return (
59
+ <Button ref={ mergedRef } { ...buttonProps }>
60
+ { label }
61
+ </Button>
62
+ );
63
+ }
64
+
65
+ const ForwardedPageActionButton: ForwardRefExoticComponent<
66
+ PageActionButtonProps & RefAttributes< HTMLButtonElement >
67
+ > = forwardRef( PageActionButton );
68
+
69
+ export default ForwardedPageActionButton;
@@ -51,6 +51,11 @@ export type AdminPageProps = {
51
51
  */
52
52
  breadcrumbs?: ReactNode;
53
53
 
54
+ /**
55
+ * Accessible name of the page region. Defaults to `title`, so pass it when the header shows `breadcrumbs` instead.
56
+ */
57
+ ariaLabel?: string;
58
+
54
59
  /**
55
60
  * Tab navigation displayed below the title/tagline in the unified header.
56
61
  */
@@ -4,14 +4,16 @@ import type { ReactElement } from 'react';
4
4
  /**
5
5
  * Automattic Icon Logo component
6
6
  *
7
+ * The Automattic logo is black or white only: pass white for both colors on dark backgrounds.
8
+ *
7
9
  * @param {object} props - Component props
8
- * @param {string} props.innerColor - Color code for the line in the middle of the logo.
9
- * @param {string} props.outerColor - Color code for the logo's outer
10
+ * @param {string} props.innerColor - Color code for the logo's ring.
11
+ * @param {string} props.outerColor - Color code for the slash inside the ring.
10
12
  * @return {ReactElement} Component template
11
13
  */
12
14
  export default function AutomatticIconLogo( {
13
- innerColor = '#00A3E0',
14
- outerColor = '#FFFFFF',
15
+ innerColor = '#000000',
16
+ outerColor = '#000000',
15
17
  }: {
16
18
  innerColor: string;
17
19
  outerColor: string;
@@ -22,20 +24,20 @@ export default function AutomatticIconLogo( {
22
24
  fill="none"
23
25
  fillRule="evenodd"
24
26
  height="43"
25
- viewBox="0 0 47 43"
27
+ viewBox="0 0 171 156"
26
28
  width="47"
27
29
  xmlns="http://www.w3.org/2000/svg"
28
30
  >
29
31
  <Path
30
32
  fillRule="evenodd"
31
33
  clipRule="evenodd"
32
- 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"
34
+ 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"
33
35
  fill={ innerColor }
34
36
  />
35
37
  <Path
36
38
  fillRule="evenodd"
37
39
  clipRule="evenodd"
38
- 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"
40
+ 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"
39
41
  fill={ outerColor }
40
42
  />
41
43
  </SVG>
@@ -1,11 +1,19 @@
1
+ import { getScoreTier } from '@automattic/jetpack-boost-score-api';
1
2
  import { __ } from '@wordpress/i18n';
2
3
  import { Icon, desktop, mobile } from '@wordpress/icons';
3
4
  import clsx from 'clsx';
4
5
  import type { BoostScoreBarProps } from './types.ts';
6
+ import type { ScoreTier } from '@automattic/jetpack-boost-score-api';
5
7
  import type { FunctionComponent } from 'react';
6
8
 
7
9
  import './style.scss';
8
10
 
11
+ const TIER_FILLS: Record< ScoreTier, string > = {
12
+ good: 'fill-good',
13
+ medium: 'fill-mediocre',
14
+ poor: 'fill-bad',
15
+ };
16
+
9
17
  export const BoostScoreBar: FunctionComponent< BoostScoreBarProps > = ( {
10
18
  score,
11
19
  isLoading,
@@ -38,15 +46,7 @@ export const BoostScoreBar: FunctionComponent< BoostScoreBarProps > = ( {
38
46
  return 'fill-loading';
39
47
  }
40
48
 
41
- if ( score > 70 ) {
42
- return 'fill-good';
43
- }
44
-
45
- if ( score > 50 ) {
46
- return 'fill-mediocre';
47
- }
48
-
49
- return 'fill-bad';
49
+ return TIER_FILLS[ getScoreTier( score ) ];
50
50
  };
51
51
 
52
52
  return (
@@ -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
  }
@@ -29,6 +29,15 @@ const menu = [
29
29
  <JetpackFooter menu={ menu } className="my-footer" />
30
30
  ```
31
31
 
32
+ ### Script-module builds
33
+
34
+ Builds that can't bundle Sass imported from a dependency, such as wp-build script modules, can import the component and its stylesheet separately. Use the two together, since `unstyled` ships no styles:
35
+
36
+ ```js
37
+ import '@automattic/jetpack-components/jetpack-footer/style.scss';
38
+ import JetpackFooter from '@automattic/jetpack-components/jetpack-footer/unstyled';
39
+ ```
40
+
32
41
  ## Props
33
42
 
34
43
  - `className`: String - (default: `jetpack-footer`) the additional class name set on the element.
@@ -1,114 +1,3 @@
1
- import { isWpcomPlatformSite, getAdminUrl, getScriptData } from '@automattic/jetpack-script-data';
2
- import { __ } from '@wordpress/i18n';
3
- import { Stack, Text, Link } from '@wordpress/ui';
4
- import clsx from 'clsx';
5
- import { getRedirectUrl } from '../../index.ts';
6
- import AutomatticBylineLogo from '../automattic-byline-logo/index.tsx';
7
1
  import './style.scss';
8
- import JetpackLogo from '../jetpack-logo/index.tsx';
9
- import type { JetpackFooterProps, JetpackFooterMenuItem } from './types.ts';
10
- import type { FC } from 'react';
11
2
 
12
- declare global {
13
- interface Window {
14
- JetpackNetworkAdminData?: {
15
- sitesUrl: string;
16
- settingsUrl: string;
17
- };
18
- }
19
- }
20
-
21
- /**
22
- * JetpackFooter component displays a tiny Jetpack logo with the product name on the left and the Automattic Airline "by line" on the right.
23
- *
24
- * @param {JetpackFooterProps} props - Component properties.
25
- * @return {ReactNode} JetpackFooter component.
26
- */
27
- const JetpackFooter: FC< JetpackFooterProps > = ( { className, menu, ...otherProps } ) => {
28
- let items: JetpackFooterMenuItem[] = [];
29
-
30
- if (
31
- getScriptData()?.myJetpack?.isAvailable === true &&
32
- ! isWpcomPlatformSite() &&
33
- ! window?.JetpackNetworkAdminData
34
- ) {
35
- items = [
36
- {
37
- label: __( 'Features', 'jetpack-components' ),
38
- href: getAdminUrl( 'admin.php?page=my-jetpack#/features' ),
39
- },
40
- {
41
- label: __( 'Help', 'jetpack-components' ),
42
- href: getAdminUrl( 'admin.php?page=my-jetpack#/help' ),
43
- },
44
- ...items,
45
- ];
46
- }
47
-
48
- if ( menu ) {
49
- items = [ ...items, ...menu ];
50
- }
51
-
52
- return (
53
- <Stack
54
- render={ <footer /> }
55
- className={ clsx( 'jetpack-footer', className ) }
56
- aria-label={ __( 'Jetpack', 'jetpack-components' ) }
57
- role="contentinfo"
58
- direction="row"
59
- justify="flex-start"
60
- align="center"
61
- wrap="wrap"
62
- gap="xl"
63
- { ...otherProps }
64
- >
65
- <Stack className="jetpack-footer__logo" direction="row" gap="sm" align="center">
66
- <JetpackLogo showText={ false } height={ 16 } aria-hidden="true" />
67
- <Text variant="body-md">Jetpack</Text>
68
- </Stack>
69
- <Stack render={ <ul /> } direction="row" gap="lg" wrap="wrap">
70
- { items.map( item => {
71
- return (
72
- <li key={ item.label }>
73
- <Text
74
- variant="body-md"
75
- className="jetpack-footer__menu-item"
76
- render={
77
- ! item.href ? (
78
- <Link
79
- render={ <span /> }
80
- tabIndex={ 0 }
81
- title={ item.title || '' }
82
- onClick={ item.onClick || undefined }
83
- onKeyDown={ item.onKeyDown || undefined }
84
- role="button"
85
- />
86
- ) : (
87
- <Link
88
- href={ item.href }
89
- title={ item.title || '' }
90
- onClick={ item.onClick || undefined }
91
- onKeyDown={ item.onKeyDown || undefined }
92
- />
93
- )
94
- }
95
- >
96
- { item.label }
97
- </Text>
98
- </li>
99
- );
100
- } ) }
101
- </Stack>
102
- <a
103
- className="jetpack-footer__a8c"
104
- href={ getRedirectUrl( 'a8c-about' ) }
105
- rel="noopener noreferrer"
106
- target="_blank"
107
- >
108
- <AutomatticBylineLogo height={ 8 } />
109
- </a>
110
- </Stack>
111
- );
112
- };
113
-
114
- export default JetpackFooter;
3
+ export { default } from './jetpack-footer.tsx';