@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.
- package/CHANGELOG.md +26 -0
- package/build/components/admin-page/style.module.scss +2 -2
- package/build/components/copy-to-clipboard/index.js +1 -1
- package/build/components/decorative-card/style.scss +4 -1
- package/build/components/diff-viewer/index.d.ts +2 -1
- package/build/components/global-notices/global-notices.d.ts +4 -0
- package/build/components/global-notices/global-notices.js +4 -0
- package/build/components/global-notices/use-global-notices.d.ts +3 -0
- package/build/components/global-notices/use-global-notices.js +3 -0
- package/build/components/gravatar/index.d.ts +1 -0
- package/build/components/icon-tooltip/index.js +37 -15
- package/build/components/icon-tooltip/style.scss +0 -8
- package/build/components/icon-tooltip/types.d.ts +4 -0
- package/build/components/jetpack-footer/style.scss +2 -5
- package/build/components/jitm-slot/index.d.ts +15 -0
- package/build/components/jitm-slot/index.js +52 -0
- package/build/components/jitm-slot/style.scss +16 -0
- package/build/components/jitm-slot/types.d.ts +10 -0
- package/build/components/text/style.module.scss +4 -4
- package/build/components/text/types.d.ts +4 -4
- package/build/components/theme-provider/index.js +1 -1
- package/build/index.d.ts +1 -2
- package/build/index.js +1 -2
- package/components/admin-page/style.module.scss +2 -2
- package/components/copy-to-clipboard/index.tsx +2 -2
- package/components/decorative-card/style.scss +4 -1
- package/components/diff-viewer/index.tsx +1 -0
- package/components/global-notices/README.md +25 -0
- package/components/global-notices/global-notices.tsx +4 -0
- package/components/global-notices/use-global-notices.ts +3 -0
- package/components/gravatar/index.tsx +1 -0
- package/components/icon-tooltip/index.tsx +49 -18
- package/components/icon-tooltip/style.scss +0 -8
- package/components/icon-tooltip/types.ts +5 -0
- package/components/jetpack-footer/style.scss +2 -5
- package/components/jitm-slot/index.tsx +67 -0
- package/components/jitm-slot/style.scss +16 -0
- package/components/jitm-slot/types.ts +10 -0
- package/components/text/style.module.scss +4 -4
- package/components/text/types.ts +4 -4
- package/components/theme-provider/index.tsx +1 -1
- package/index.ts +1 -2
- package/package.json +12 -7
- package/build/components/notice/index.d.ts +0 -29
- package/build/components/notice/index.js +0 -35
- package/build/components/notice/style.module.scss +0 -120
- package/build/components/zendesk-chat/constants.d.ts +0 -1
- package/build/components/zendesk-chat/constants.js +0 -2
- package/build/components/zendesk-chat/index.d.ts +0 -3
- package/build/components/zendesk-chat/index.js +0 -27
- package/build/components/zendesk-chat/types.d.ts +0 -11
- package/components/notice/index.tsx +0 -93
- package/components/notice/style.module.scss +0 -120
- package/components/zendesk-chat/README.md +0 -25
- package/components/zendesk-chat/constants.ts +0 -2
- package/components/zendesk-chat/index.tsx +0 -34
- package/components/zendesk-chat/types.ts +0 -14
- /package/build/components/{zendesk-chat → jitm-slot}/types.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
### This is a list detailing changes for the Jetpack RNA Components package releases.
|
|
4
4
|
|
|
5
|
+
## [4.0.0] - 2026-10-05
|
|
6
|
+
### Changed
|
|
7
|
+
- Update package dependencies. [#52999]
|
|
8
|
+
|
|
9
|
+
### Removed
|
|
10
|
+
- Remove the Notice component; use Notice from @wordpress/ui instead. [#52932]
|
|
11
|
+
- Remove the unused ZendeskChat component. [#52074]
|
|
12
|
+
|
|
13
|
+
## [3.3.0] - 2026-09-28
|
|
14
|
+
### Added
|
|
15
|
+
- Add a `JitmSlot` component that renders the Jetpack in-dashboard message slot and keeps the message across route changes. [#52641]
|
|
16
|
+
- IconTooltip: Add a `label` prop to give the icon trigger an accessible name. [#52800]
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- IconTooltip: Keep focus on the trigger when a tooltip opens, and announce its content to screen readers. [#52736]
|
|
20
|
+
- IconTooltip: Show the trigger's focus ring the way other WordPress buttons do. [#52736]
|
|
21
|
+
- Use logical CSS properties so layouts mirror in right-to-left languages. [#52749]
|
|
22
|
+
|
|
23
|
+
### Deprecated
|
|
24
|
+
- Deprecate `GlobalNotices` and `useGlobalNotices`. Use `SnackbarNotices` from `@wordpress/notices` instead. [#52193]
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- IconTooltip: Close a tooltip opened on hover when Escape is pressed, without moving focus. [#52799]
|
|
28
|
+
|
|
5
29
|
## [3.2.0] - 2026-09-23
|
|
6
30
|
### Added
|
|
7
31
|
- IconTooltip: Add a `trigger` prop that renders a text trigger in place of the icon, and a `closeOnClickOutside` prop to keep a tooltip open while the visitor works elsewhere on the page. [#52704]
|
|
@@ -1963,6 +1987,8 @@
|
|
|
1963
1987
|
### Changed
|
|
1964
1988
|
- Update node version requirement to 14.16.1
|
|
1965
1989
|
|
|
1990
|
+
[4.0.0]: https://github.com/Automattic/jetpack-components/compare/3.3.0...4.0.0
|
|
1991
|
+
[3.3.0]: https://github.com/Automattic/jetpack-components/compare/3.2.0...3.3.0
|
|
1966
1992
|
[3.2.0]: https://github.com/Automattic/jetpack-components/compare/3.1.2...3.2.0
|
|
1967
1993
|
[3.1.2]: https://github.com/Automattic/jetpack-components/compare/3.1.1...3.1.2
|
|
1968
1994
|
[3.1.1]: https://github.com/Automattic/jetpack-components/compare/3.1.0...3.1.1
|
|
@@ -105,9 +105,9 @@
|
|
|
105
105
|
// We override only the bits that need to differ from those defaults.
|
|
106
106
|
float: none;
|
|
107
107
|
text-align: end;
|
|
108
|
-
background: var(--wpds-color-background-surface-neutral-strong
|
|
108
|
+
background: var(--wpds-color-background-surface-neutral-strong);
|
|
109
109
|
font-style: italic;
|
|
110
|
-
color: var(--wpds-color-foreground-content-neutral-weak
|
|
110
|
+
color: var(--wpds-color-foreground-content-neutral-weak);
|
|
111
111
|
border-bottom: none;
|
|
112
112
|
|
|
113
113
|
@media (max-width: 659px) {
|
|
@@ -6,7 +6,7 @@ import Button from '../button/index.js';
|
|
|
6
6
|
import { ClipboardIcon, CheckmarkIcon } from '../icons/index.js';
|
|
7
7
|
export const CopyToClipboard = ({ buttonStyle = 'icon', textToCopy, onCopy, ...buttonProps }) => {
|
|
8
8
|
const [hasCopied, setHasCopied] = useState(false);
|
|
9
|
-
const copyTimer = useRef();
|
|
9
|
+
const copyTimer = useRef(undefined);
|
|
10
10
|
const copyRef = useCopyToClipboard(textToCopy, () => {
|
|
11
11
|
if (copyTimer.current) {
|
|
12
12
|
clearTimeout(copyTimer.current);
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
display: block;
|
|
30
30
|
position: absolute;
|
|
31
31
|
top: 24px;
|
|
32
|
-
|
|
32
|
+
inset-inline-start: 24px;
|
|
33
33
|
width: 38px;
|
|
34
34
|
height: 8px;
|
|
35
35
|
background-image: url('data:image/svg+xml,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
&__icon-container {
|
|
44
44
|
position: absolute;
|
|
45
45
|
top: 50%;
|
|
46
|
+
// Kept physical: translate(-50%) shifts by a fixed direction, not a
|
|
47
|
+
// logical one, so left must stay paired with it or the box goes
|
|
48
|
+
// off-center by its own width under RTL without rtlcss.
|
|
46
49
|
left: 50%;
|
|
47
50
|
transform: translate(-50%, -50%);
|
|
48
51
|
width: 80px;
|
|
@@ -4,6 +4,10 @@ export type GlobalNoticesProps = {
|
|
|
4
4
|
/**
|
|
5
5
|
* Renders the global notices.
|
|
6
6
|
*
|
|
7
|
+
* @deprecated Use `SnackbarNotices` from `@wordpress/notices` instead. Pages
|
|
8
|
+
* rendered by `@wordpress/boot` already mount one, so mounting this as well
|
|
9
|
+
* shows every notice twice.
|
|
10
|
+
*
|
|
7
11
|
* @param {GlobalNoticesProps} props - Component props.
|
|
8
12
|
*
|
|
9
13
|
* @return {import('react').ReactNode} The rendered notices list.
|
|
@@ -5,6 +5,10 @@ import { useGlobalNotices } from './use-global-notices.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Renders the global notices.
|
|
7
7
|
*
|
|
8
|
+
* @deprecated Use `SnackbarNotices` from `@wordpress/notices` instead. Pages
|
|
9
|
+
* rendered by `@wordpress/boot` already mount one, so mounting this as well
|
|
10
|
+
* shows every notice twice.
|
|
11
|
+
*
|
|
8
12
|
* @param {GlobalNoticesProps} props - Component props.
|
|
9
13
|
*
|
|
10
14
|
* @return {import('react').ReactNode} The rendered notices list.
|
|
@@ -4,6 +4,9 @@ export type TGlobalNotices = ReturnType<NoticesStore['getActions']> & ReturnType
|
|
|
4
4
|
/**
|
|
5
5
|
* The global notices hook.
|
|
6
6
|
*
|
|
7
|
+
* @deprecated Dispatch to `@wordpress/notices` directly, passing
|
|
8
|
+
* `{ type: 'snackbar' }` — this hook only defaulted that option.
|
|
9
|
+
*
|
|
7
10
|
* @return {TGlobalNotices} The global notices selectors and actions.
|
|
8
11
|
*/
|
|
9
12
|
export declare function useGlobalNotices(): TGlobalNotices;
|
|
@@ -3,6 +3,9 @@ import { store as noticesStore } from '@wordpress/notices';
|
|
|
3
3
|
/**
|
|
4
4
|
* The global notices hook.
|
|
5
5
|
*
|
|
6
|
+
* @deprecated Dispatch to `@wordpress/notices` directly, passing
|
|
7
|
+
* `{ type: 'snackbar' }` — this hook only defaulted that option.
|
|
8
|
+
*
|
|
6
9
|
* @return {TGlobalNotices} The global notices selectors and actions.
|
|
7
10
|
*/
|
|
8
11
|
export function useGlobalNotices() {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { speak } from '@wordpress/a11y';
|
|
2
3
|
import { Popover } from '@wordpress/components';
|
|
3
4
|
import { focus } from '@wordpress/dom';
|
|
4
5
|
import { Icon, info } from '@wordpress/icons';
|
|
@@ -23,7 +24,7 @@ const placementsToPositions = (placement) => {
|
|
|
23
24
|
* @param {IconTooltipProps} props - Props
|
|
24
25
|
* @return {ReactElement} - JSX element
|
|
25
26
|
*/
|
|
26
|
-
const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', placement = 'bottom-end', animate = true, iconCode = info, iconSize = 18, offset = 10, title, children, popoverAnchorStyle = 'icon', trigger, onTriggerClick, closeOnClickOutside = true, forceShow = false, hoverShow = false, wide = false, inline = true, shift = false, }) => {
|
|
27
|
+
const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', placement = 'bottom-end', animate = true, iconCode = info, label, iconSize = 18, offset = 10, title, children, popoverAnchorStyle = 'icon', trigger, onTriggerClick, closeOnClickOutside = true, forceShow = false, hoverShow = false, wide = false, inline = true, shift = false, }) => {
|
|
27
28
|
const POPOVER_HELPER_WIDTH = 124;
|
|
28
29
|
const [isVisible, setIsVisible] = useState(false);
|
|
29
30
|
const [hoverTimeout, setHoverTimeout] = useState(null);
|
|
@@ -43,13 +44,16 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
43
44
|
openedByHover.current = false;
|
|
44
45
|
setIsVisible(!isVisible);
|
|
45
46
|
}, [isVisible, setIsVisible, onTriggerClick]);
|
|
46
|
-
// Focus can stay on
|
|
47
|
+
// Focus can stay on the trigger while its tooltip is open, so it handles the dialog keys too.
|
|
47
48
|
const handleTriggerKeyDown = useCallback((event) => {
|
|
49
|
+
// A held key repeats activation, so only its first press toggles.
|
|
50
|
+
if (event.repeat && (event.key === 'Enter' || event.key === ' ')) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
48
54
|
// A link only activates on Enter; a button also activates on Space.
|
|
49
|
-
if (event.key === ' ') {
|
|
50
|
-
|
|
51
|
-
toggleTooltip(event);
|
|
52
|
-
}
|
|
55
|
+
if (hasTextTrigger && event.key === ' ') {
|
|
56
|
+
toggleTooltip(event);
|
|
53
57
|
return;
|
|
54
58
|
}
|
|
55
59
|
if (!isVisible) {
|
|
@@ -70,7 +74,7 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
70
74
|
hideTooltip();
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
|
-
}, [isVisible, hideTooltip, toggleTooltip]);
|
|
77
|
+
}, [hasTextTrigger, isVisible, hideTooltip, toggleTooltip]);
|
|
74
78
|
const isAnchorWrapper = popoverAnchorStyle === 'wrapper';
|
|
75
79
|
const isForcedToShow = isAnchorWrapper && forceShow;
|
|
76
80
|
const handlePopoverKeyDown = useCallback((event) => {
|
|
@@ -104,9 +108,6 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
104
108
|
focusAfterClose.current = destination ?? null;
|
|
105
109
|
hideTooltip();
|
|
106
110
|
}, [hideTooltip]);
|
|
107
|
-
const focusOnOpen = isForcedToShow || (hasTextTrigger && !openedByHover.current)
|
|
108
|
-
? 'firstElement'
|
|
109
|
-
: !openedByHover.current;
|
|
110
111
|
const args = {
|
|
111
112
|
// To be compatible with deprecating prop `position`.
|
|
112
113
|
position: placementsToPositions(placement),
|
|
@@ -116,9 +117,9 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
116
117
|
resize: false,
|
|
117
118
|
flip: false,
|
|
118
119
|
offset, // The distance (in px) between the anchor and the popover.
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
focusOnMount:
|
|
120
|
+
// Focus stays on the trigger, which handles the dialog keys; a caller-controlled popover
|
|
121
|
+
// has no trigger, so it focuses its first button instead.
|
|
122
|
+
focusOnMount: isForcedToShow ? 'firstElement' : false,
|
|
122
123
|
// Tab moves through the popover in document order rather than cycling inside it, and
|
|
123
124
|
// handlePopoverKeyDown decides where it lands on the way out.
|
|
124
125
|
constrainTabbing: false,
|
|
@@ -126,7 +127,7 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
126
127
|
ref: popoverRef,
|
|
127
128
|
onClose: () => {
|
|
128
129
|
const popover = popoverRef.current;
|
|
129
|
-
if (
|
|
130
|
+
if (popover?.contains(popover.ownerDocument.activeElement)) {
|
|
130
131
|
focusAfterClose.current = triggerRef.current;
|
|
131
132
|
}
|
|
132
133
|
hideTooltip();
|
|
@@ -160,6 +161,20 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
160
161
|
doc?.addEventListener('pointerdown', handlePointerDown);
|
|
161
162
|
return () => doc?.removeEventListener('pointerdown', handlePointerDown);
|
|
162
163
|
}, [isVisible, isForcedToShow, closeOnClickOutside, hideTooltip]);
|
|
164
|
+
// Hover leaves focus elsewhere, so Escape is watched directly to keep the content dismissible.
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (!isVisible || !openedByHover.current) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const doc = wrapperRef.current?.ownerDocument;
|
|
170
|
+
const handleKeyDown = (event) => {
|
|
171
|
+
if (event.key === 'Escape') {
|
|
172
|
+
hideTooltip();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
doc?.addEventListener('keydown', handleKeyDown);
|
|
176
|
+
return () => doc?.removeEventListener('keydown', handleKeyDown);
|
|
177
|
+
}, [isVisible, hideTooltip]);
|
|
163
178
|
useEffect(() => {
|
|
164
179
|
if (isForcedToShow || isVisible) {
|
|
165
180
|
return;
|
|
@@ -168,6 +183,13 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
168
183
|
focusAfterClose.current = null;
|
|
169
184
|
destination?.focus();
|
|
170
185
|
}, [isForcedToShow, isVisible]);
|
|
186
|
+
// Focus stays on the trigger, so a screen reader hears the popover through an announcement.
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
const popover = popoverRef.current;
|
|
189
|
+
if (isVisible && !openedByHover.current && popover) {
|
|
190
|
+
speak(popover.innerText ?? popover.textContent, 'polite');
|
|
191
|
+
}
|
|
192
|
+
}, [isVisible]);
|
|
171
193
|
const handleMouseEnter = useCallback(() => {
|
|
172
194
|
if (hoverShow) {
|
|
173
195
|
if (hoverTimeout) {
|
|
@@ -188,6 +210,6 @@ const IconTooltip = ({ className = '', popoverClassName, iconClassName = '', pla
|
|
|
188
210
|
}
|
|
189
211
|
}, [hoverShow]);
|
|
190
212
|
const helper = (_jsx("div", { className: clsx('icon-tooltip-helper', { 'is-wide': wide }), style: iconShiftBySize, children: (isForcedToShow || isVisible) && (_jsx(Popover, { ...args, children: _jsxs("div", { children: [title && _jsx("div", { className: "icon-tooltip-title", children: title }), _jsx("div", { className: "icon-tooltip-content", children: children })] }) })) }));
|
|
191
|
-
return (_jsxs("div", { ref: wrapperRef, className: wrapperClassNames, "data-testid": "icon-tooltip_wrapper", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [hasTextTrigger && (_jsxs(_Fragment, { children: [_jsx("a", { ref: triggerRef, href: "#", role: "button", className: "icon-tooltip-trigger", "aria-expanded": isVisible, onClick: toggleTooltip, onKeyDown: handleTriggerKeyDown, children: trigger }), _jsx("span", { className: "icon-tooltip-anchor", children: _jsx("span", { children: helper }) })] })), !hasTextTrigger && !isAnchorWrapper && (_jsx(Button, { variant: "link", "aria-expanded": isVisible, onClick: toggleTooltip, children: _jsx(Icon, { className: iconClassName, icon: iconCode, size: iconSize }) })), !hasTextTrigger && helper] }));
|
|
213
|
+
return (_jsxs("div", { ref: wrapperRef, className: wrapperClassNames, "data-testid": "icon-tooltip_wrapper", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [hasTextTrigger && (_jsxs(_Fragment, { children: [_jsx("a", { ref: triggerRef, href: "#", role: "button", className: "icon-tooltip-trigger", "aria-expanded": isVisible, onClick: toggleTooltip, onKeyDown: handleTriggerKeyDown, children: trigger }), _jsx("span", { className: "icon-tooltip-anchor", children: _jsx("span", { children: helper }) })] })), !hasTextTrigger && !isAnchorWrapper && (_jsx(Button, { ref: triggerRef, variant: "link", "aria-label": label, "aria-expanded": isVisible, onClick: toggleTooltip, onKeyDown: handleTriggerKeyDown, children: _jsx(Icon, { className: iconClassName, icon: iconCode, size: iconSize }) })), !hasTextTrigger && helper] }));
|
|
192
214
|
};
|
|
193
215
|
export default IconTooltip;
|
|
@@ -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;
|
|
@@ -31,6 +31,10 @@ export type IconTooltipProps = {
|
|
|
31
31
|
* The icon to display. Accepts icon components from `@wordpress/icons`.
|
|
32
32
|
*/
|
|
33
33
|
iconCode?: ComponentProps<typeof Icon>['icon'];
|
|
34
|
+
/**
|
|
35
|
+
* The accessible name of the icon button. Ignored when `trigger` is set, since its text names it.
|
|
36
|
+
*/
|
|
37
|
+
label?: string;
|
|
34
38
|
/**
|
|
35
39
|
* The title of Popover.
|
|
36
40
|
*/
|
|
@@ -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
|
|
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
|
|
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,15 @@
|
|
|
1
|
+
import './style.scss';
|
|
2
|
+
import type { JitmSlotProps } from './types.ts';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Hosts the element the JITM script re-parents its card into.
|
|
6
|
+
*
|
|
7
|
+
* The JITM script places its card once per page load, so the element outlives any
|
|
8
|
+
* one slot: an unmounted slot parks it, hidden, and the next slot takes it back.
|
|
9
|
+
* `className` is for width and alignment; `style.scss` owns spacing.
|
|
10
|
+
*
|
|
11
|
+
* @param {JitmSlotProps} props - Component props.
|
|
12
|
+
* @return {JSX.Element} The slot.
|
|
13
|
+
*/
|
|
14
|
+
declare const JitmSlot: FC<JitmSlotProps>;
|
|
15
|
+
export default JitmSlot;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
4
|
+
import './style.scss';
|
|
5
|
+
const NOTICES_ID = 'jp-admin-notices';
|
|
6
|
+
/**
|
|
7
|
+
* Returns the page's one `#jp-admin-notices`, creating it on first use.
|
|
8
|
+
*
|
|
9
|
+
* Found through the DOM, not a module variable: each wp-build route bundles its own copy
|
|
10
|
+
* of this module.
|
|
11
|
+
*
|
|
12
|
+
* @return {HTMLElement} The element.
|
|
13
|
+
*/
|
|
14
|
+
const getNotices = () => {
|
|
15
|
+
let element = document.getElementById(NOTICES_ID);
|
|
16
|
+
if (!element) {
|
|
17
|
+
element = document.createElement('div');
|
|
18
|
+
element.id = NOTICES_ID;
|
|
19
|
+
}
|
|
20
|
+
return element;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Hosts the element the JITM script re-parents its card into.
|
|
24
|
+
*
|
|
25
|
+
* The JITM script places its card once per page load, so the element outlives any
|
|
26
|
+
* one slot: an unmounted slot parks it, hidden, and the next slot takes it back.
|
|
27
|
+
* `className` is for width and alignment; `style.scss` owns spacing.
|
|
28
|
+
*
|
|
29
|
+
* @param {JitmSlotProps} props - Component props.
|
|
30
|
+
* @return {JSX.Element} The slot.
|
|
31
|
+
*/
|
|
32
|
+
const JitmSlot = ({ className, inset }) => {
|
|
33
|
+
const ref = useRef(null);
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
const host = ref.current;
|
|
36
|
+
if (!host) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const element = getNotices();
|
|
40
|
+
element.hidden = false;
|
|
41
|
+
host.appendChild(element);
|
|
42
|
+
return () => {
|
|
43
|
+
// A slot that mounted before this one unmounted may already hold it.
|
|
44
|
+
if (element.parentNode === host) {
|
|
45
|
+
element.hidden = true;
|
|
46
|
+
document.body.appendChild(element);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}, []);
|
|
50
|
+
return (_jsx("div", { ref: ref, className: clsx('jp-jitm-slot', { 'jp-jitm-slot--inset': inset }, className), "data-testid": "jp-jitm-slot" }));
|
|
51
|
+
};
|
|
52
|
+
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
|
-
"
|
|
92
|
+
"inline-end": "r",
|
|
93
93
|
"bottom": "b",
|
|
94
|
-
"
|
|
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}-
|
|
110
|
-
#{$prop}-
|
|
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} {
|
|
@@ -7,11 +7,11 @@ export type TextProps = {
|
|
|
7
7
|
m?: SpacingValue;
|
|
8
8
|
/** margin-top */
|
|
9
9
|
mt?: SpacingValue;
|
|
10
|
-
/** margin-right */
|
|
10
|
+
/** margin-inline-end (right in LTR) */
|
|
11
11
|
mr?: SpacingValue;
|
|
12
12
|
/** margin-bottom */
|
|
13
13
|
mb?: SpacingValue;
|
|
14
|
-
/** margin-left */
|
|
14
|
+
/** margin-inline-start (left in LTR) */
|
|
15
15
|
ml?: SpacingValue;
|
|
16
16
|
/** margin left and right */
|
|
17
17
|
mx?: SpacingValue;
|
|
@@ -21,11 +21,11 @@ export type TextProps = {
|
|
|
21
21
|
p?: SpacingValue;
|
|
22
22
|
/** padding-top */
|
|
23
23
|
pt?: SpacingValue;
|
|
24
|
-
/** padding-right */
|
|
24
|
+
/** padding-inline-end (right in LTR) */
|
|
25
25
|
pr?: SpacingValue;
|
|
26
26
|
/** padding-bottom */
|
|
27
27
|
pb?: SpacingValue;
|
|
28
|
-
/** padding-left */
|
|
28
|
+
/** padding-inline-start (left in LTR) */
|
|
29
29
|
pl?: SpacingValue;
|
|
30
30
|
/** padding left and right */
|
|
31
31
|
px?: SpacingValue;
|
|
@@ -105,7 +105,7 @@ const setup = (root, id, withGlobalStyles) => {
|
|
|
105
105
|
* @return {ReactNode} ThemeProvider component.
|
|
106
106
|
*/
|
|
107
107
|
const ThemeProvider = ({ children = null, targetDom, id, withGlobalStyles = true, }) => {
|
|
108
|
-
const themeWrapperRef = useRef();
|
|
108
|
+
const themeWrapperRef = useRef(undefined);
|
|
109
109
|
// Check whether the theme provider instance is already registered.
|
|
110
110
|
const isAlreadyProvided = globalThemeInstances?.[id]?.provided;
|
|
111
111
|
useLayoutEffect(() => {
|
package/build/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { default as Col } from './components/layout/col/index.tsx';
|
|
|
20
20
|
export { default as Testimonials } from './components/testimonials/index.tsx';
|
|
21
21
|
export { default as Container } from './components/layout/container/index.tsx';
|
|
22
22
|
export { default as CopyToClipboard } from './components/copy-to-clipboard/index.tsx';
|
|
23
|
+
export { default as JitmSlot } from './components/jitm-slot/index.tsx';
|
|
23
24
|
export * from './components/icons/index.tsx';
|
|
24
25
|
export { default as ThemeProvider } from './components/theme-provider/index.tsx';
|
|
25
26
|
export { default as Text, H2, H3, Title } from './components/text/index.tsx';
|
|
@@ -36,11 +37,9 @@ export { default as ProductPrice } from './components/product-price/index.tsx';
|
|
|
36
37
|
export { default as DotPager } from './components/dot-pager/index.tsx';
|
|
37
38
|
export { default as DonutMeter } from './components/donut-meter/index.tsx';
|
|
38
39
|
export { default as RecordMeterBar } from './components/record-meter-bar/index.tsx';
|
|
39
|
-
export { default as Notice } from './components/notice/index.tsx';
|
|
40
40
|
export { default as Popover } from './components/popover/index.tsx';
|
|
41
41
|
export { default as IndeterminateProgressBar } from './components/indeterminate-progress-bar/index.tsx';
|
|
42
42
|
export { default as ActionPopover } from './components/action-popover/index.tsx';
|
|
43
|
-
export { default as ZendeskChat } from './components/zendesk-chat/index.tsx';
|
|
44
43
|
export { default as ProgressBar } from './components/progress-bar/index.tsx';
|
|
45
44
|
export { default as UpsellBanner } from './components/upsell-banner/index.tsx';
|
|
46
45
|
export { getUserLocale, cleanLocale } from './lib/locale/index.ts';
|
package/build/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export { default as Col } from './components/layout/col/index.js';
|
|
|
33
33
|
export { default as Testimonials } from './components/testimonials/index.js';
|
|
34
34
|
export { default as Container } from './components/layout/container/index.js';
|
|
35
35
|
export { default as CopyToClipboard } from './components/copy-to-clipboard/index.js';
|
|
36
|
+
export { default as JitmSlot } from './components/jitm-slot/index.js';
|
|
36
37
|
export * from './components/icons/index.js';
|
|
37
38
|
export { default as ThemeProvider } from './components/theme-provider/index.js';
|
|
38
39
|
export { default as Text, H2, H3, Title } from './components/text/index.js';
|
|
@@ -48,11 +49,9 @@ export { default as ProductPrice } from './components/product-price/index.js';
|
|
|
48
49
|
export { default as DotPager } from './components/dot-pager/index.js';
|
|
49
50
|
export { default as DonutMeter } from './components/donut-meter/index.js';
|
|
50
51
|
export { default as RecordMeterBar } from './components/record-meter-bar/index.js';
|
|
51
|
-
export { default as Notice } from './components/notice/index.js';
|
|
52
52
|
export { default as Popover } from './components/popover/index.js';
|
|
53
53
|
export { default as IndeterminateProgressBar } from './components/indeterminate-progress-bar/index.js';
|
|
54
54
|
export { default as ActionPopover } from './components/action-popover/index.js';
|
|
55
|
-
export { default as ZendeskChat } from './components/zendesk-chat/index.js';
|
|
56
55
|
export { default as ProgressBar } from './components/progress-bar/index.js';
|
|
57
56
|
export { default as UpsellBanner } from './components/upsell-banner/index.js';
|
|
58
57
|
export { getUserLocale, cleanLocale } from './lib/locale/index.js';
|
|
@@ -105,9 +105,9 @@
|
|
|
105
105
|
// We override only the bits that need to differ from those defaults.
|
|
106
106
|
float: none;
|
|
107
107
|
text-align: end;
|
|
108
|
-
background: var(--wpds-color-background-surface-neutral-strong
|
|
108
|
+
background: var(--wpds-color-background-surface-neutral-strong);
|
|
109
109
|
font-style: italic;
|
|
110
|
-
color: var(--wpds-color-foreground-content-neutral-weak
|
|
110
|
+
color: var(--wpds-color-foreground-content-neutral-weak);
|
|
111
111
|
border-bottom: none;
|
|
112
112
|
|
|
113
113
|
@media (max-width: 659px) {
|
|
@@ -4,7 +4,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
4
4
|
import Button from '../button/index.tsx';
|
|
5
5
|
import { ClipboardIcon, CheckmarkIcon } from '../icons/index.tsx';
|
|
6
6
|
import { CopyToClipboardProps } from './types.ts';
|
|
7
|
-
import type { FC } from 'react';
|
|
7
|
+
import type { JSX, FC } from 'react';
|
|
8
8
|
|
|
9
9
|
export const CopyToClipboard: FC< CopyToClipboardProps > = ( {
|
|
10
10
|
buttonStyle = 'icon',
|
|
@@ -14,7 +14,7 @@ export const CopyToClipboard: FC< CopyToClipboardProps > = ( {
|
|
|
14
14
|
} ) => {
|
|
15
15
|
const [ hasCopied, setHasCopied ] = useState( false );
|
|
16
16
|
|
|
17
|
-
const copyTimer = useRef< ReturnType< typeof setTimeout > | undefined >();
|
|
17
|
+
const copyTimer = useRef< ReturnType< typeof setTimeout > | undefined >( undefined );
|
|
18
18
|
|
|
19
19
|
const copyRef = useCopyToClipboard( textToCopy, () => {
|
|
20
20
|
if ( copyTimer.current ) {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
display: block;
|
|
30
30
|
position: absolute;
|
|
31
31
|
top: 24px;
|
|
32
|
-
|
|
32
|
+
inset-inline-start: 24px;
|
|
33
33
|
width: 38px;
|
|
34
34
|
height: 8px;
|
|
35
35
|
background-image: url('data:image/svg+xml,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
&__icon-container {
|
|
44
44
|
position: absolute;
|
|
45
45
|
top: 50%;
|
|
46
|
+
// Kept physical: translate(-50%) shifts by a fixed direction, not a
|
|
47
|
+
// logical one, so left must stay paired with it or the box goes
|
|
48
|
+
// off-center by its own width under RTL without rtlcss.
|
|
46
49
|
left: 50%;
|
|
47
50
|
transform: translate(-50%, -50%);
|
|
48
51
|
width: 80px;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
GlobalNotices
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
> **Deprecated.** Use `SnackbarNotices` from `@wordpress/notices` instead, and
|
|
5
|
+
> dispatch to that store directly with `{ type: 'snackbar' }`. Pages rendered by
|
|
6
|
+
> `@wordpress/boot` already mount a `SnackbarNotices`, so mounting this one too
|
|
7
|
+
> shows every notice twice. Retained as a compatibility shim for downstream
|
|
8
|
+
> consumers and will be removed in the next major version.
|
|
9
|
+
|
|
10
|
+
Renders the snackbar notices held in the `@wordpress/notices` store.
|
|
11
|
+
|
|
12
|
+
## General Usage:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { GlobalNotices } from '@automattic/jetpack-components';
|
|
16
|
+
render() {
|
|
17
|
+
return (
|
|
18
|
+
<GlobalNotices />
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
- `maxVisibleNotices`: how many notices to show at once. Defaults to 3.
|
|
@@ -9,6 +9,10 @@ export type GlobalNoticesProps = {
|
|
|
9
9
|
/**
|
|
10
10
|
* Renders the global notices.
|
|
11
11
|
*
|
|
12
|
+
* @deprecated Use `SnackbarNotices` from `@wordpress/notices` instead. Pages
|
|
13
|
+
* rendered by `@wordpress/boot` already mount one, so mounting this as well
|
|
14
|
+
* shows every notice twice.
|
|
15
|
+
*
|
|
12
16
|
* @param {GlobalNoticesProps} props - Component props.
|
|
13
17
|
*
|
|
14
18
|
* @return {import('react').ReactNode} The rendered notices list.
|
|
@@ -9,6 +9,9 @@ export type TGlobalNotices = ReturnType< NoticesStore[ 'getActions' ] > &
|
|
|
9
9
|
/**
|
|
10
10
|
* The global notices hook.
|
|
11
11
|
*
|
|
12
|
+
* @deprecated Dispatch to `@wordpress/notices` directly, passing
|
|
13
|
+
* `{ type: 'snackbar' }` — this hook only defaulted that option.
|
|
14
|
+
*
|
|
12
15
|
* @return {TGlobalNotices} The global notices selectors and actions.
|
|
13
16
|
*/
|
|
14
17
|
export function useGlobalNotices(): TGlobalNotices {
|
|
@@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
8
8
|
import clsx from 'clsx';
|
|
9
9
|
import { sha256 } from 'js-sha256';
|
|
10
10
|
import './style.scss';
|
|
11
|
+
import type { JSX } from 'react';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Gravatar `defaultImage` styles, mirroring https://docs.gravatar.com/sdk/images/#default-image
|