@automattic/jetpack-components 3.2.0 → 4.0.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 (58) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/build/components/admin-page/style.module.scss +2 -2
  3. package/build/components/copy-to-clipboard/index.js +1 -1
  4. package/build/components/decorative-card/style.scss +4 -1
  5. package/build/components/diff-viewer/index.d.ts +2 -1
  6. package/build/components/global-notices/global-notices.d.ts +4 -0
  7. package/build/components/global-notices/global-notices.js +4 -0
  8. package/build/components/global-notices/use-global-notices.d.ts +3 -0
  9. package/build/components/global-notices/use-global-notices.js +3 -0
  10. package/build/components/gravatar/index.d.ts +1 -0
  11. package/build/components/icon-tooltip/index.js +37 -15
  12. package/build/components/icon-tooltip/style.scss +0 -8
  13. package/build/components/icon-tooltip/types.d.ts +4 -0
  14. package/build/components/jetpack-footer/style.scss +2 -5
  15. package/build/components/jitm-slot/index.d.ts +15 -0
  16. package/build/components/jitm-slot/index.js +52 -0
  17. package/build/components/jitm-slot/style.scss +16 -0
  18. package/build/components/jitm-slot/types.d.ts +10 -0
  19. package/build/components/text/style.module.scss +4 -4
  20. package/build/components/text/types.d.ts +4 -4
  21. package/build/components/theme-provider/index.js +1 -1
  22. package/build/index.d.ts +1 -2
  23. package/build/index.js +1 -2
  24. package/components/admin-page/style.module.scss +2 -2
  25. package/components/copy-to-clipboard/index.tsx +2 -2
  26. package/components/decorative-card/style.scss +4 -1
  27. package/components/diff-viewer/index.tsx +1 -0
  28. package/components/global-notices/README.md +25 -0
  29. package/components/global-notices/global-notices.tsx +4 -0
  30. package/components/global-notices/use-global-notices.ts +3 -0
  31. package/components/gravatar/index.tsx +1 -0
  32. package/components/icon-tooltip/index.tsx +49 -18
  33. package/components/icon-tooltip/style.scss +0 -8
  34. package/components/icon-tooltip/types.ts +5 -0
  35. package/components/jetpack-footer/style.scss +2 -5
  36. package/components/jitm-slot/index.tsx +67 -0
  37. package/components/jitm-slot/style.scss +16 -0
  38. package/components/jitm-slot/types.ts +10 -0
  39. package/components/text/style.module.scss +4 -4
  40. package/components/text/types.ts +4 -4
  41. package/components/theme-provider/index.tsx +1 -1
  42. package/index.ts +1 -2
  43. package/package.json +12 -7
  44. package/build/components/notice/index.d.ts +0 -29
  45. package/build/components/notice/index.js +0 -35
  46. package/build/components/notice/style.module.scss +0 -120
  47. package/build/components/zendesk-chat/constants.d.ts +0 -1
  48. package/build/components/zendesk-chat/constants.js +0 -2
  49. package/build/components/zendesk-chat/index.d.ts +0 -3
  50. package/build/components/zendesk-chat/index.js +0 -27
  51. package/build/components/zendesk-chat/types.d.ts +0 -11
  52. package/components/notice/index.tsx +0 -93
  53. package/components/notice/style.module.scss +0 -120
  54. package/components/zendesk-chat/README.md +0 -25
  55. package/components/zendesk-chat/constants.ts +0 -2
  56. package/components/zendesk-chat/index.tsx +0 -34
  57. package/components/zendesk-chat/types.ts +0 -14
  58. /package/build/components/{zendesk-chat → jitm-slot}/types.js +0 -0
@@ -1,3 +1,4 @@
1
+ import { speak } from '@wordpress/a11y';
1
2
  import { Popover } from '@wordpress/components';
2
3
  import { focus } from '@wordpress/dom';
3
4
  import { Icon, info } from '@wordpress/icons';
@@ -43,6 +44,7 @@ const IconTooltip: FC< IconTooltipProps > = ( {
43
44
  placement = 'bottom-end',
44
45
  animate = true,
45
46
  iconCode = info,
47
+ label,
46
48
  iconSize = 18,
47
49
  offset = 10,
48
50
  title,
@@ -62,7 +64,7 @@ const IconTooltip: FC< IconTooltipProps > = ( {
62
64
  const [ hoverTimeout, setHoverTimeout ] = useState( null );
63
65
  const wrapperRef = useRef< HTMLDivElement >( null );
64
66
  const popoverRef = useRef< HTMLDivElement >( null );
65
- const triggerRef = useRef< HTMLAnchorElement >( null );
67
+ const triggerRef = useRef< HTMLElement >( null );
66
68
  const hasTextTrigger = trigger !== undefined;
67
69
  // Where focus should land after Tab leaves the tooltip. The effect below applies it, rather
68
70
  // than the handler, because Popover puts focus back on the trigger as it unmounts.
@@ -80,14 +82,17 @@ const IconTooltip: FC< IconTooltipProps > = ( {
80
82
  [ isVisible, setIsVisible, onTriggerClick ]
81
83
  );
82
84
 
83
- // Focus can stay on a text trigger while its tooltip is open, so it handles the dialog keys too.
85
+ // Focus can stay on the trigger while its tooltip is open, so it handles the dialog keys too.
84
86
  const handleTriggerKeyDown = useCallback(
85
- ( event: KeyboardEvent< HTMLAnchorElement > ) => {
87
+ ( event: KeyboardEvent< HTMLElement > ) => {
88
+ // A held key repeats activation, so only its first press toggles.
89
+ if ( event.repeat && ( event.key === 'Enter' || event.key === ' ' ) ) {
90
+ event.preventDefault();
91
+ return;
92
+ }
86
93
  // A link only activates on Enter; a button also activates on Space.
87
- if ( event.key === ' ' ) {
88
- if ( ! event.repeat ) {
89
- toggleTooltip( event );
90
- }
94
+ if ( hasTextTrigger && event.key === ' ' ) {
95
+ toggleTooltip( event );
91
96
  return;
92
97
  }
93
98
  if ( ! isVisible ) {
@@ -107,7 +112,7 @@ const IconTooltip: FC< IconTooltipProps > = ( {
107
112
  }
108
113
  }
109
114
  },
110
- [ isVisible, hideTooltip, toggleTooltip ]
115
+ [ hasTextTrigger, isVisible, hideTooltip, toggleTooltip ]
111
116
  );
112
117
 
113
118
  const isAnchorWrapper = popoverAnchorStyle === 'wrapper';
@@ -151,10 +156,6 @@ const IconTooltip: FC< IconTooltipProps > = ( {
151
156
  [ hideTooltip ]
152
157
  );
153
158
 
154
- const focusOnOpen =
155
- isForcedToShow || ( hasTextTrigger && ! openedByHover.current )
156
- ? 'firstElement'
157
- : ! openedByHover.current;
158
159
  const args = {
159
160
  // To be compatible with deprecating prop `position`.
160
161
  position: placementsToPositions( placement ),
@@ -164,9 +165,9 @@ const IconTooltip: FC< IconTooltipProps > = ( {
164
165
  resize: false,
165
166
  flip: false,
166
167
  offset, // The distance (in px) between the anchor and the popover.
167
- // Focusing the popover itself puts Escape in reach even with nothing tabbable inside. A text
168
- // trigger or caller-controlled popover moves focus to its first link, if it has one.
169
- focusOnMount: focusOnOpen,
168
+ // Focus stays on the trigger, which handles the dialog keys; a caller-controlled popover
169
+ // has no trigger, so it focuses its first button instead.
170
+ focusOnMount: isForcedToShow ? 'firstElement' : false,
170
171
  // Tab moves through the popover in document order rather than cycling inside it, and
171
172
  // handlePopoverKeyDown decides where it lands on the way out.
172
173
  constrainTabbing: false,
@@ -174,7 +175,7 @@ const IconTooltip: FC< IconTooltipProps > = ( {
174
175
  ref: popoverRef,
175
176
  onClose: () => {
176
177
  const popover = popoverRef.current;
177
- if ( hasTextTrigger && popover?.contains( popover.ownerDocument.activeElement ) ) {
178
+ if ( popover?.contains( popover.ownerDocument.activeElement ) ) {
178
179
  focusAfterClose.current = triggerRef.current;
179
180
  }
180
181
  hideTooltip();
@@ -215,6 +216,21 @@ const IconTooltip: FC< IconTooltipProps > = ( {
215
216
  return () => doc?.removeEventListener( 'pointerdown', handlePointerDown );
216
217
  }, [ isVisible, isForcedToShow, closeOnClickOutside, hideTooltip ] );
217
218
 
219
+ // Hover leaves focus elsewhere, so Escape is watched directly to keep the content dismissible.
220
+ useEffect( () => {
221
+ if ( ! isVisible || ! openedByHover.current ) {
222
+ return;
223
+ }
224
+ const doc = wrapperRef.current?.ownerDocument;
225
+ const handleKeyDown = ( event: globalThis.KeyboardEvent ) => {
226
+ if ( event.key === 'Escape' ) {
227
+ hideTooltip();
228
+ }
229
+ };
230
+ doc?.addEventListener( 'keydown', handleKeyDown );
231
+ return () => doc?.removeEventListener( 'keydown', handleKeyDown );
232
+ }, [ isVisible, hideTooltip ] );
233
+
218
234
  useEffect( () => {
219
235
  if ( isForcedToShow || isVisible ) {
220
236
  return;
@@ -224,6 +240,14 @@ const IconTooltip: FC< IconTooltipProps > = ( {
224
240
  destination?.focus();
225
241
  }, [ isForcedToShow, isVisible ] );
226
242
 
243
+ // Focus stays on the trigger, so a screen reader hears the popover through an announcement.
244
+ useEffect( () => {
245
+ const popover = popoverRef.current;
246
+ if ( isVisible && ! openedByHover.current && popover ) {
247
+ speak( popover.innerText ?? popover.textContent, 'polite' );
248
+ }
249
+ }, [ isVisible ] );
250
+
227
251
  const handleMouseEnter = useCallback( () => {
228
252
  if ( hoverShow ) {
229
253
  if ( hoverTimeout ) {
@@ -270,7 +294,7 @@ const IconTooltip: FC< IconTooltipProps > = ( {
270
294
  <>
271
295
  { /* A link, not a button, so it keeps the host page's link styling. */ }
272
296
  <a
273
- ref={ triggerRef }
297
+ ref={ triggerRef as React.RefObject< HTMLAnchorElement > }
274
298
  href="#"
275
299
  role="button"
276
300
  className="icon-tooltip-trigger"
@@ -286,7 +310,14 @@ const IconTooltip: FC< IconTooltipProps > = ( {
286
310
  </>
287
311
  ) }
288
312
  { ! hasTextTrigger && ! isAnchorWrapper && (
289
- <Button variant="link" aria-expanded={ isVisible } onClick={ toggleTooltip }>
313
+ <Button
314
+ ref={ triggerRef }
315
+ variant="link"
316
+ aria-label={ label }
317
+ aria-expanded={ isVisible }
318
+ onClick={ toggleTooltip }
319
+ onKeyDown={ handleTriggerKeyDown }
320
+ >
290
321
  <Icon className={ iconClassName } icon={ iconCode } size={ iconSize } />
291
322
  </Button>
292
323
  ) }
@@ -107,14 +107,6 @@ $arrow-color: var(--jp-gray);
107
107
  .icon-tooltip-container {
108
108
  // Recover events
109
109
  pointer-events: all;
110
- // Takes focus on open, but its box is wider than the visible content,
111
- // so the ring goes on the content instead.
112
- outline: none;
113
-
114
- &:focus-visible .components-popover__content {
115
- outline: var(--wp-admin-border-width-focus, 2px) solid var(--wp-admin-theme-color, #3858e9);
116
- }
117
-
118
110
  // __content styles here covers both the inline and portal modes
119
111
  .components-popover__content {
120
112
  padding: 24px;
@@ -41,6 +41,11 @@ export type IconTooltipProps = {
41
41
  */
42
42
  iconCode?: ComponentProps< typeof Icon >[ 'icon' ];
43
43
 
44
+ /**
45
+ * The accessible name of the icon button. Ignored when `trigger` is set, since its text names it.
46
+ */
47
+ label?: string;
48
+
44
49
  /**
45
50
  * The title of Popover.
46
51
  */
@@ -1,10 +1,7 @@
1
- @use "sass:meta";
2
- @include meta.load-css("pkg:@wordpress/theme/design-tokens.css");
3
-
4
1
  .jetpack-footer {
5
- border-top: var(--wpds-border-width-xs, 1px) solid var(--wpds-color-stroke-surface-neutral-weak, #e0e0e0);
2
+ border-top: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);
6
3
  box-sizing: border-box;
7
- font-size: var(--wpds-typography-font-size-md, 13px);
4
+ font-size: var(--wpds-typography-font-size-md);
8
5
  padding: var(--wpds-dimension-padding-xl) var(--wpds-dimension-padding-2xl);
9
6
  width: 100%;
10
7
  display: flex;
@@ -0,0 +1,67 @@
1
+ import clsx from 'clsx';
2
+ import { useLayoutEffect, useRef } from 'react';
3
+ import './style.scss';
4
+ import type { JitmSlotProps } from './types.ts';
5
+ import type { JSX, FC } from 'react';
6
+
7
+ const NOTICES_ID = 'jp-admin-notices';
8
+
9
+ /**
10
+ * Returns the page's one `#jp-admin-notices`, creating it on first use.
11
+ *
12
+ * Found through the DOM, not a module variable: each wp-build route bundles its own copy
13
+ * of this module.
14
+ *
15
+ * @return {HTMLElement} The element.
16
+ */
17
+ const getNotices = (): HTMLElement => {
18
+ let element = document.getElementById( NOTICES_ID );
19
+ if ( ! element ) {
20
+ element = document.createElement( 'div' );
21
+ element.id = NOTICES_ID;
22
+ }
23
+ return element;
24
+ };
25
+
26
+ /**
27
+ * Hosts the element the JITM script re-parents its card into.
28
+ *
29
+ * The JITM script places its card once per page load, so the element outlives any
30
+ * one slot: an unmounted slot parks it, hidden, and the next slot takes it back.
31
+ * `className` is for width and alignment; `style.scss` owns spacing.
32
+ *
33
+ * @param {JitmSlotProps} props - Component props.
34
+ * @return {JSX.Element} The slot.
35
+ */
36
+ const JitmSlot: FC< JitmSlotProps > = ( { className, inset } ) => {
37
+ const ref = useRef< HTMLDivElement >( null );
38
+
39
+ useLayoutEffect( () => {
40
+ const host = ref.current;
41
+ if ( ! host ) {
42
+ return;
43
+ }
44
+
45
+ const element = getNotices();
46
+ element.hidden = false;
47
+ host.appendChild( element );
48
+
49
+ return () => {
50
+ // A slot that mounted before this one unmounted may already hold it.
51
+ if ( element.parentNode === host ) {
52
+ element.hidden = true;
53
+ document.body.appendChild( element );
54
+ }
55
+ };
56
+ }, [] );
57
+
58
+ return (
59
+ <div
60
+ ref={ ref }
61
+ className={ clsx( 'jp-jitm-slot', { 'jp-jitm-slot--inset': inset }, className ) }
62
+ data-testid="jp-jitm-slot"
63
+ />
64
+ );
65
+ };
66
+
67
+ export default JitmSlot;
@@ -0,0 +1,16 @@
1
+ // The slot is always rendered and usually empty, so it takes no space
2
+ // of its own; spacing sits on the card so a dismissed one leaves no gap.
3
+ .jp-jitm-slot {
4
+ margin: 0;
5
+ padding: 0;
6
+
7
+ // `jitm-card` comes from packages/jitm/src/js/jetpack-jitm.js.
8
+ .jitm-card {
9
+ margin: var(--wpds-dimension-gap-xl) 0;
10
+ }
11
+ }
12
+
13
+ // Same specificity as the rule above, so it has to come after it.
14
+ .jp-jitm-slot--inset .jitm-card {
15
+ margin-inline: var(--wpds-dimension-padding-2xl);
16
+ }
@@ -0,0 +1,10 @@
1
+ export type JitmSlotProps = {
2
+ /**
3
+ * Layout the dashboard owns: width, column span, centering. Not spacing.
4
+ */
5
+ className?: string;
6
+ /**
7
+ * The slot sits outside the page's padded container, so the card takes the page gutter.
8
+ */
9
+ inset?: boolean;
10
+ };
@@ -89,9 +89,9 @@ $props: (
89
89
 
90
90
  $sides: (
91
91
  "top": "t",
92
- "right": "r",
92
+ "inline-end": "r",
93
93
  "bottom": "b",
94
- "left": "l",
94
+ "inline-start": "l",
95
95
  );
96
96
 
97
97
  @each $prop, $p in $props {
@@ -106,8 +106,8 @@ $sides: (
106
106
 
107
107
  /* mx, px */
108
108
  .#{$p}x-#{$size} {
109
- #{$prop}-left: calc(var(--spacing-base) * #{$size});
110
- #{$prop}-right: calc(var(--spacing-base) * #{$size});
109
+ #{$prop}-inline-start: calc(var(--spacing-base) * #{$size});
110
+ #{$prop}-inline-end: calc(var(--spacing-base) * #{$size});
111
111
  }
112
112
 
113
113
  .#{$p}y-#{$size} {
@@ -9,11 +9,11 @@ export type TextProps = {
9
9
  m?: SpacingValue;
10
10
  /** margin-top */
11
11
  mt?: SpacingValue;
12
- /** margin-right */
12
+ /** margin-inline-end (right in LTR) */
13
13
  mr?: SpacingValue;
14
14
  /** margin-bottom */
15
15
  mb?: SpacingValue;
16
- /** margin-left */
16
+ /** margin-inline-start (left in LTR) */
17
17
  ml?: SpacingValue;
18
18
  /** margin left and right */
19
19
  mx?: SpacingValue;
@@ -23,11 +23,11 @@ export type TextProps = {
23
23
  p?: SpacingValue;
24
24
  /** padding-top */
25
25
  pt?: SpacingValue;
26
- /** padding-right */
26
+ /** padding-inline-end (right in LTR) */
27
27
  pr?: SpacingValue;
28
28
  /** padding-bottom */
29
29
  pb?: SpacingValue;
30
- /** padding-left */
30
+ /** padding-inline-start (left in LTR) */
31
31
  pl?: SpacingValue;
32
32
  /** padding left and right */
33
33
  px?: SpacingValue;
@@ -121,7 +121,7 @@ const ThemeProvider: FC< ThemeProviderProps > = ( {
121
121
  id,
122
122
  withGlobalStyles = true,
123
123
  } ) => {
124
- const themeWrapperRef = useRef< HTMLDivElement >();
124
+ const themeWrapperRef = useRef< HTMLDivElement >( undefined );
125
125
 
126
126
  // Check whether the theme provider instance is already registered.
127
127
  const isAlreadyProvided = globalThemeInstances?.[ id ]?.provided;
package/index.ts CHANGED
@@ -35,6 +35,7 @@ export { default as Col } from './components/layout/col/index.tsx';
35
35
  export { default as Testimonials } from './components/testimonials/index.tsx';
36
36
  export { default as Container } from './components/layout/container/index.tsx';
37
37
  export { default as CopyToClipboard } from './components/copy-to-clipboard/index.tsx';
38
+ export { default as JitmSlot } from './components/jitm-slot/index.tsx';
38
39
  export * from './components/icons/index.tsx';
39
40
  export { default as ThemeProvider } from './components/theme-provider/index.tsx';
40
41
  export { default as Text, H2, H3, Title } from './components/text/index.tsx';
@@ -56,11 +57,9 @@ export { default as ProductPrice } from './components/product-price/index.tsx';
56
57
  export { default as DotPager } from './components/dot-pager/index.tsx';
57
58
  export { default as DonutMeter } from './components/donut-meter/index.tsx';
58
59
  export { default as RecordMeterBar } from './components/record-meter-bar/index.tsx';
59
- export { default as Notice } from './components/notice/index.tsx';
60
60
  export { default as Popover } from './components/popover/index.tsx';
61
61
  export { default as IndeterminateProgressBar } from './components/indeterminate-progress-bar/index.tsx';
62
62
  export { default as ActionPopover } from './components/action-popover/index.tsx';
63
- export { default as ZendeskChat } from './components/zendesk-chat/index.tsx';
64
63
  export { default as ProgressBar } from './components/progress-bar/index.tsx';
65
64
  export { default as UpsellBanner } from './components/upsell-banner/index.tsx';
66
65
  export { getUserLocale, cleanLocale } from './lib/locale/index.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/jetpack-components",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Jetpack Components Package",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/components/#readme",
6
6
  "bugs": {
@@ -46,6 +46,10 @@
46
46
  "types": "./build/components/jetpack-logo/index.d.ts",
47
47
  "default": "./build/components/jetpack-logo/index.js"
48
48
  },
49
+ "./jitm-slot": {
50
+ "types": "./build/components/jitm-slot/index.d.ts",
51
+ "default": "./build/components/jitm-slot/index.js"
52
+ },
49
53
  "./pricing-table": {
50
54
  "types": "./build/components/pricing-table/index.d.ts",
51
55
  "default": "./build/components/pricing-table/index.js"
@@ -72,12 +76,13 @@
72
76
  "extends @wordpress/browserslist-config"
73
77
  ],
74
78
  "dependencies": {
75
- "@automattic/jetpack-api": "^2.0.0",
76
- "@automattic/jetpack-boost-score-api": "^1.1.0",
79
+ "@automattic/jetpack-api": "^2.1.0",
80
+ "@automattic/jetpack-boost-score-api": "^1.2.0",
77
81
  "@automattic/jetpack-script-data": "^0.6.8",
78
- "@automattic/number-formatters": "^1.2.9",
82
+ "@automattic/number-formatters": "^1.2.10",
79
83
  "@babel/runtime": "^7",
80
84
  "@gravatar-com/hovercards": "0.16.0",
85
+ "@wordpress/a11y": "4.55.0",
81
86
  "@wordpress/admin-ui": "2.10.0",
82
87
  "@wordpress/browserslist-config": "6.55.0",
83
88
  "@wordpress/components": "40.1.0",
@@ -95,12 +100,12 @@
95
100
  "js-sha256": "0.11.1",
96
101
  "prop-types": "^15.7.2",
97
102
  "qrcode.react": "4.2.0",
98
- "social-logos": "^3.3.19",
103
+ "social-logos": "^3.4.0",
99
104
  "uplot": "1.6.31",
100
105
  "uplot-react": "1.1.4"
101
106
  },
102
107
  "devDependencies": {
103
- "@automattic/jetpack-base-styles": "^1.2.14",
108
+ "@automattic/jetpack-base-styles": "^1.2.16",
104
109
  "@babel/core": "7.29.7",
105
110
  "@jest/globals": "30.5.1",
106
111
  "@storybook/addon-docs": "10.4.6",
@@ -119,7 +124,7 @@
119
124
  "storybook": "10.4.6",
120
125
  "ts-dedent": "2.2.0",
121
126
  "typescript": "6.0.3",
122
- "webpack": "5.107.2",
127
+ "webpack": "5.111.1",
123
128
  "webpack-cli": "7.2.1"
124
129
  },
125
130
  "peerDependencies": {
@@ -1,29 +0,0 @@
1
- import type { FC, ReactNode } from 'react';
2
- type NoticeProps = {
3
- /** The severity of the alert. */
4
- level?: 'error' | 'warning' | 'info' | 'success';
5
- /** The title of the notice */
6
- title?: string;
7
- /** A list of action elements to show across the bottom */
8
- actions?: ReactNode[];
9
- /** Hide close button */
10
- hideCloseButton?: boolean;
11
- /** Method to call when the close button is clicked */
12
- onClose?: () => void;
13
- /** Children to be rendered inside the alert. */
14
- children: ReactNode;
15
- };
16
- /**
17
- * Notice component
18
- *
19
- * @param {object} props - The component properties.
20
- * @param {string} props.level - The notice level: error, warning, info, success.
21
- * @param {boolean} props.hideCloseButton - Whether to hide the close button.
22
- * @param {Function} props.onClose - The function to call when the close button is clicked.
23
- * @param {string} props.title - The title of the notice.
24
- * @param {ReactNode[]} props.actions - Actions to show across the bottom of the bar.
25
- * @param {Component} props.children - The notice content.
26
- * @return {ReactElement} The `Notice` component.
27
- */
28
- declare const Notice: FC<NoticeProps>;
29
- export default Notice;
@@ -1,35 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Icon, cautionFilled as warning, info, check, close } from '@wordpress/icons';
3
- import clsx from 'clsx';
4
- import styles from './style.module.scss';
5
- const getIconByLevel = (level) => {
6
- switch (level) {
7
- case 'error':
8
- return warning;
9
- case 'warning':
10
- return warning;
11
- case 'info':
12
- return info;
13
- case 'success':
14
- return check;
15
- default:
16
- return warning;
17
- }
18
- };
19
- /**
20
- * Notice component
21
- *
22
- * @param {object} props - The component properties.
23
- * @param {string} props.level - The notice level: error, warning, info, success.
24
- * @param {boolean} props.hideCloseButton - Whether to hide the close button.
25
- * @param {Function} props.onClose - The function to call when the close button is clicked.
26
- * @param {string} props.title - The title of the notice.
27
- * @param {ReactNode[]} props.actions - Actions to show across the bottom of the bar.
28
- * @param {Component} props.children - The notice content.
29
- * @return {ReactElement} The `Notice` component.
30
- */
31
- const Notice = ({ level = 'info', title, children, actions, hideCloseButton = false, onClose, }) => {
32
- const classes = clsx(styles.container, styles[`is-${level}`]);
33
- return (_jsxs("div", { className: classes, children: [_jsx("div", { className: styles['icon-wrapper'], children: _jsx(Icon, { icon: getIconByLevel(level), className: styles.icon }) }), _jsxs("div", { className: styles['main-content'], children: [title && _jsx("div", { className: styles.title, children: title }), children, actions && actions.length > 0 && (_jsx("div", { className: styles['action-bar'], children: actions.map((action, index) => (_jsx("div", { className: styles.action, children: action }, index))) }))] }), !hideCloseButton && (_jsx("button", { "aria-label": "close", className: styles['close-button'], onClick: onClose, children: _jsx(Icon, { icon: close }) }))] }));
34
- };
35
- export default Notice;
@@ -1,120 +0,0 @@
1
- .container {
2
- display: flex;
3
- align-items: flex-start;
4
-
5
- font-size: var(--font-body);
6
-
7
- border: 1px solid var(--jp-black);
8
- border-radius: 4px;
9
- border-color: var(--jp-gray-5);
10
- border-left-width: 6px;
11
-
12
- padding: 24px 31px 27px 18px;
13
- }
14
-
15
- .icon-wrapper {
16
- margin-right: 20px;
17
- width: calc(var(--spacing-base) * 3);
18
- height: calc(var(--spacing-base) * 3);
19
- }
20
-
21
- .close-button {
22
- padding: 0;
23
- background-color: transparent;
24
- border: none;
25
- cursor: pointer;
26
- outline: none;
27
-
28
- svg {
29
- display: block;
30
- }
31
- }
32
-
33
- // Mobile layout differences.
34
- @media screen and ( max-width: 600px ) {
35
-
36
- .container {
37
- position: relative;
38
- padding-top: 68px;
39
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.06);
40
- }
41
-
42
- .icon-wrapper {
43
- position: absolute;
44
- top: 24px;
45
- left: 24px;
46
- }
47
-
48
- .close-button {
49
- position: absolute;
50
- top: 24px;
51
- right: 24px;
52
- }
53
- }
54
-
55
- .main-content {
56
- flex-grow: 1;
57
- }
58
-
59
- .title {
60
- font-weight: 600;
61
- margin-bottom: 8px;
62
- }
63
-
64
- .action-bar {
65
- display: flex;
66
- align-items: center;
67
- margin-top: 20px;
68
- row-gap: 20px;
69
- flex-wrap: wrap;
70
-
71
- @media screen and ( max-width: 600px ) {
72
- justify-content: center;
73
- }
74
-
75
- a {
76
-
77
- &,
78
- &:hover,
79
- &:active,
80
- &:focus {
81
- color: var(--jp-black);
82
- }
83
- }
84
-
85
- > * {
86
- margin-right: 24px;
87
- }
88
- }
89
-
90
- .is-error {
91
- border-left-color: var(--jp-red);
92
-
93
- .icon {
94
- color: var(--jp-red);
95
- }
96
- }
97
-
98
- .is-warning {
99
- border-left-color: var(--jp-yellow-20);
100
-
101
- .icon {
102
- color: var(--jp-yellow-20);
103
- }
104
- }
105
-
106
- .is-info {
107
- border-left-color: var(--black);
108
-
109
- .icon {
110
- color: var(--black);
111
- }
112
- }
113
-
114
- .is-success {
115
- border-left-color: var(--jp-green);
116
-
117
- .icon {
118
- color: var(--jp-green);
119
- }
120
- }
@@ -1 +0,0 @@
1
- export declare const chatKey = "d729d42c-b547-4750-a6f6-8b30534a5f12";
@@ -1,2 +0,0 @@
1
- // This is not a secret, it can easily be found in the DOM
2
- export const chatKey = 'd729d42c-b547-4750-a6f6-8b30534a5f12';
@@ -1,3 +0,0 @@
1
- import type { ZendeskChatType } from './types.ts';
2
- export declare const ZendeskChat: ZendeskChatType;
3
- export default ZendeskChat;
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useCallback } from 'react';
3
- import { chatKey } from './constants.js';
4
- export const ZendeskChat = ({ jwt_token }) => {
5
- const authenticateUser = useCallback(() => {
6
- if (typeof window !== 'undefined' && typeof window.zE === 'function') {
7
- window.zE('messenger', 'loginUser', function (callback) {
8
- callback(jwt_token);
9
- });
10
- }
11
- }, [jwt_token]);
12
- useEffect(() => {
13
- const script = document.createElement('script');
14
- const container = document.getElementById('zendesk-chat-container');
15
- script.src = 'https://static.zdassets.com/ekr/snippet.js?key=' + encodeURIComponent(chatKey);
16
- script.type = 'text/javascript';
17
- script.id = 'ze-snippet';
18
- script.onload = () => {
19
- authenticateUser();
20
- };
21
- if (container) {
22
- container.appendChild(script);
23
- }
24
- }, [authenticateUser]);
25
- return _jsx("div", { "data-testid": "zendesk-chat-container", id: "zendesk-chat-container" });
26
- };
27
- export default ZendeskChat;
@@ -1,11 +0,0 @@
1
- import type { FC } from 'react';
2
- declare global {
3
- interface Window {
4
- zE: Function;
5
- }
6
- }
7
- interface ZendeskChatProps {
8
- jwt_token: string;
9
- }
10
- export type ZendeskChatType = FC<ZendeskChatProps>;
11
- export {};