@atlaskit/renderer 126.4.0 → 126.5.1
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 +19 -0
- package/dist/cjs/messages.js +10 -0
- package/dist/cjs/react/nodes/heading-anchor.js +14 -8
- package/dist/cjs/react/nodes/heading.js +137 -9
- package/dist/cjs/ui/Renderer/RendererStyleContainer.js +380 -166
- package/dist/cjs/ui/Renderer/index.js +1 -1
- package/dist/es2019/messages.js +10 -0
- package/dist/es2019/react/nodes/heading-anchor.js +15 -6
- package/dist/es2019/react/nodes/heading.js +138 -9
- package/dist/es2019/ui/Renderer/RendererStyleContainer.js +426 -130
- package/dist/es2019/ui/Renderer/index.js +1 -1
- package/dist/esm/messages.js +10 -0
- package/dist/esm/react/nodes/heading-anchor.js +14 -8
- package/dist/esm/react/nodes/heading.js +135 -9
- package/dist/esm/ui/Renderer/RendererStyleContainer.js +380 -166
- package/dist/esm/ui/Renderer/index.js +1 -1
- package/dist/types/messages.d.ts +10 -0
- package/dist/types/react/nodes/heading.d.ts +14 -2
- package/dist/types-ts4.5/messages.d.ts +10 -0
- package/dist/types-ts4.5/react/nodes/heading.d.ts +14 -2
- package/package.json +2 -2
|
@@ -70,7 +70,7 @@ var DEGRADED_SEVERITY_THRESHOLD = exports.DEGRADED_SEVERITY_THRESHOLD = 3000;
|
|
|
70
70
|
var TABLE_INFO_TIMEOUT = 10000;
|
|
71
71
|
var RENDER_EVENT_SAMPLE_RATE = 0.2;
|
|
72
72
|
var packageName = "@atlaskit/renderer";
|
|
73
|
-
var packageVersion = "126.
|
|
73
|
+
var packageVersion = "126.5.0";
|
|
74
74
|
var setAsQueryContainerStyles = (0, _react2.css)({
|
|
75
75
|
containerName: 'ak-renderer-wrapper',
|
|
76
76
|
containerType: 'inline-size'
|
package/dist/es2019/messages.js
CHANGED
|
@@ -8,6 +8,16 @@ export const headingAnchorLinkMessages = defineMessages({
|
|
|
8
8
|
defaultMessage: 'Copy link to heading',
|
|
9
9
|
description: 'Copy heading link to clipboard'
|
|
10
10
|
},
|
|
11
|
+
copyLinkToClipboard: {
|
|
12
|
+
id: 'fabric.editor.headingLink.copyAnchorLinkTo',
|
|
13
|
+
defaultMessage: 'Copy link to',
|
|
14
|
+
description: 'Copy heading link to clipboard. Will be used as part of a 2-part a11y label ("Copy link to", "{heading text}")'
|
|
15
|
+
},
|
|
16
|
+
copyHeadingLinkLabelledBy: {
|
|
17
|
+
id: 'fabric.editor.headingLink.copyAnchorLinkLabelledBy',
|
|
18
|
+
defaultMessage: '{copyLink} {heading}',
|
|
19
|
+
description: 'The order in which to read the parts of the aria-labelledby for the copy heading link button depending on the grammar of the language. {copyLink} will be replaced with the "Copy link to" text and {heading} will be replaced with the actual heading text'
|
|
20
|
+
},
|
|
11
21
|
copiedHeadingLinkToClipboard: {
|
|
12
22
|
id: 'fabric.editor.headingLink.copied',
|
|
13
23
|
defaultMessage: 'Copied!',
|
|
@@ -10,6 +10,7 @@ import React from 'react';
|
|
|
10
10
|
import { css, jsx } from '@emotion/react';
|
|
11
11
|
import { injectIntl } from 'react-intl-next';
|
|
12
12
|
import LinkIcon from '@atlaskit/icon/core/link';
|
|
13
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
13
14
|
import Tooltip from '@atlaskit/tooltip';
|
|
14
15
|
import { headingAnchorLinkMessages } from '../../messages';
|
|
15
16
|
export const HeadingAnchorWrapperClassName = 'heading-anchor-wrapper';
|
|
@@ -36,8 +37,8 @@ const copyAnchorButtonStyles = css({
|
|
|
36
37
|
// Ignored via go/ees005
|
|
37
38
|
// eslint-disable-next-line @repo/internal/react/no-class-components
|
|
38
39
|
class HeadingAnchor extends React.PureComponent {
|
|
39
|
-
constructor(
|
|
40
|
-
super(
|
|
40
|
+
constructor(props) {
|
|
41
|
+
super(props);
|
|
41
42
|
_defineProperty(this, "state", {
|
|
42
43
|
tooltipMessage: '',
|
|
43
44
|
isClicked: false
|
|
@@ -64,35 +65,43 @@ class HeadingAnchor extends React.PureComponent {
|
|
|
64
65
|
try {
|
|
65
66
|
await this.props.onCopyText();
|
|
66
67
|
this.setTooltipState(copiedHeadingLinkToClipboard, true);
|
|
67
|
-
} catch
|
|
68
|
+
} catch {
|
|
68
69
|
this.setTooltipState(failedToCopyHeadingLink);
|
|
69
70
|
}
|
|
70
71
|
});
|
|
71
72
|
_defineProperty(this, "resetMessage", () => {
|
|
72
|
-
|
|
73
|
+
const tooltip = expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? headingAnchorLinkMessages.copyLinkToClipboard : headingAnchorLinkMessages.copyHeadingLinkToClipboard;
|
|
74
|
+
this.setTooltipState(tooltip);
|
|
73
75
|
});
|
|
74
76
|
_defineProperty(this, "renderAnchorButton", () => {
|
|
75
77
|
const {
|
|
76
78
|
hideFromScreenReader = false,
|
|
77
79
|
headingId
|
|
78
80
|
} = this.props;
|
|
81
|
+
const labelledBy = expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? this.props.intl.formatMessage(headingAnchorLinkMessages.copyHeadingLinkLabelledBy, {
|
|
82
|
+
copyLink: this.copyLinkId,
|
|
83
|
+
heading: headingId
|
|
84
|
+
}) : headingId;
|
|
85
|
+
const tabIndex = expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? 0 : hideFromScreenReader ? undefined : -1;
|
|
79
86
|
return jsx("button", {
|
|
80
87
|
"data-testid": "anchor-button",
|
|
88
|
+
id: this.copyLinkId,
|
|
81
89
|
css: copyAnchorButtonStyles
|
|
82
90
|
// eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
|
|
83
91
|
,
|
|
84
92
|
onMouseLeave: this.resetMessage,
|
|
85
93
|
onClick: this.copyToClipboard,
|
|
86
94
|
"aria-hidden": hideFromScreenReader,
|
|
87
|
-
tabIndex:
|
|
95
|
+
tabIndex: tabIndex,
|
|
88
96
|
"aria-label": hideFromScreenReader ? undefined : this.state.tooltipMessage,
|
|
89
|
-
"aria-labelledby": hideFromScreenReader ? undefined :
|
|
97
|
+
"aria-labelledby": hideFromScreenReader ? undefined : labelledBy,
|
|
90
98
|
type: "button"
|
|
91
99
|
}, jsx(LinkIcon, {
|
|
92
100
|
label: this.getCopyAriaLabel(),
|
|
93
101
|
color: this.state.isClicked ? "var(--ds-icon-selected, #1868DB)" : "var(--ds-icon-subtle, #505258)"
|
|
94
102
|
}));
|
|
95
103
|
});
|
|
104
|
+
this.copyLinkId = expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? crypto.randomUUID() : undefined;
|
|
96
105
|
}
|
|
97
106
|
componentDidMount() {
|
|
98
107
|
this.resetMessage();
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jsxRuntime classic
|
|
3
|
+
* @jsx jsx
|
|
4
|
+
* @jsxFrag
|
|
5
|
+
*/
|
|
1
6
|
import React from 'react';
|
|
2
|
-
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
3
7
|
import { abortAll } from '@atlaskit/react-ufo/interaction-metrics';
|
|
4
8
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
9
|
+
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
10
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
11
|
import AnalyticsContext from '../../analytics/analyticsContext';
|
|
6
12
|
import { copyTextToClipboard } from '../utils/clipboard';
|
|
7
13
|
import HeadingAnchor from './heading-anchor';
|
|
14
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
15
|
+
import { css, jsx } from '@emotion/react';
|
|
16
|
+
const RENDERER_HEADING_WRAPPER = 'renderer-heading-wrapper';
|
|
8
17
|
const getCurrentUrlWithHash = (hash = '') => {
|
|
9
18
|
const url = new URL(window.location.href);
|
|
10
19
|
url.search = ''; // clear any query params so that the page will correctly scroll to the anchor
|
|
@@ -17,15 +26,22 @@ function hasRightAlignmentMark(marks) {
|
|
|
17
26
|
}
|
|
18
27
|
return marks.some(mark => mark.type.name === 'alignment' && mark.attrs.align === 'end');
|
|
19
28
|
}
|
|
29
|
+
const wrapperStyles = css({
|
|
30
|
+
// Important: do NOT use flex here.
|
|
31
|
+
// With flex + baseline alignment, the anchor aligns to the *first line* of a multi-line heading,
|
|
32
|
+
// which visually places it at the top-right. We want the anchor to sit immediately after the
|
|
33
|
+
// last character of the heading (i.e. after the final wrapped line), so we use normal inline flow.
|
|
34
|
+
display: 'block'
|
|
35
|
+
});
|
|
20
36
|
function WrappedHeadingAnchor({
|
|
21
37
|
enableNestedHeaderLinks,
|
|
22
38
|
level,
|
|
23
39
|
headingId,
|
|
24
40
|
hideFromScreenReader
|
|
25
41
|
}) {
|
|
26
|
-
return
|
|
42
|
+
return jsx(AnalyticsContext.Consumer, null, ({
|
|
27
43
|
fireAnalyticsEvent
|
|
28
|
-
}) =>
|
|
44
|
+
}) => jsx(HeadingAnchor, {
|
|
29
45
|
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
30
46
|
level: level,
|
|
31
47
|
onCopyText: () => {
|
|
@@ -41,7 +57,14 @@ function WrappedHeadingAnchor({
|
|
|
41
57
|
headingId: headingId
|
|
42
58
|
}));
|
|
43
59
|
}
|
|
44
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Old heading structure (before a11y fix):
|
|
62
|
+
* - headning anchor is rendered INSIDE the heading element
|
|
63
|
+
* - A duplicate anchor is rendered in VisuallyHidden for screen readers
|
|
64
|
+
* - The visible button has hideFromScreenReader={true}
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
function HeadingWithDuplicateAnchor(props) {
|
|
45
68
|
const {
|
|
46
69
|
headingId,
|
|
47
70
|
dataAttributes,
|
|
@@ -66,28 +89,134 @@ function Heading(props) {
|
|
|
66
89
|
mouseEntered.current = true;
|
|
67
90
|
}
|
|
68
91
|
};
|
|
69
|
-
return
|
|
92
|
+
return jsx(React.Fragment, null, jsx(HX, {
|
|
70
93
|
id: headingIdToUse,
|
|
71
94
|
"data-local-id": localId,
|
|
72
95
|
"data-renderer-start-pos": dataAttributes['data-renderer-start-pos'],
|
|
73
96
|
"data-as-inline": asInline,
|
|
74
97
|
onMouseEnter: mouseEnterHandler
|
|
75
|
-
},
|
|
98
|
+
}, jsx(React.Fragment, null, showAnchorLink && headingId && isRightAligned && jsx(WrappedHeadingAnchor, {
|
|
76
99
|
level: props.level,
|
|
77
100
|
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
78
101
|
headingId: headingId,
|
|
79
102
|
hideFromScreenReader: true
|
|
80
|
-
}), props.children, showAnchorLink && headingId && !isRightAligned &&
|
|
103
|
+
}), props.children, showAnchorLink && headingId && !isRightAligned && jsx(WrappedHeadingAnchor, {
|
|
81
104
|
level: props.level,
|
|
82
105
|
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
83
106
|
headingId: headingId,
|
|
84
107
|
hideFromScreenReader: true
|
|
85
|
-
}))),
|
|
108
|
+
}))), jsx(VisuallyHidden, {
|
|
86
109
|
testId: "visually-hidden-heading-anchor"
|
|
87
|
-
}, showAnchorLink && headingId &&
|
|
110
|
+
}, showAnchorLink && headingId && jsx(WrappedHeadingAnchor, {
|
|
88
111
|
level: props.level,
|
|
89
112
|
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
90
113
|
headingId: headingId
|
|
91
114
|
})));
|
|
92
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* New heading structure (a11y fix):
|
|
119
|
+
* - Heading anchor is rendered OUTSIDE the heading element in a .renderer-heading-wrapper div
|
|
120
|
+
* - Uses data-level attribute for CSS styling
|
|
121
|
+
* - Better accessibility: heading contains only text, button is a sibling
|
|
122
|
+
*/
|
|
123
|
+
function HeadingWithWrapper(props) {
|
|
124
|
+
const {
|
|
125
|
+
headingId,
|
|
126
|
+
dataAttributes,
|
|
127
|
+
allowHeadingAnchorLinks,
|
|
128
|
+
marks,
|
|
129
|
+
invisible,
|
|
130
|
+
localId,
|
|
131
|
+
asInline
|
|
132
|
+
} = props;
|
|
133
|
+
const HX = `h${props.level}`;
|
|
134
|
+
const mouseEntered = React.useRef(false);
|
|
135
|
+
const showAnchorLink = !!props.showAnchorLink;
|
|
136
|
+
const isRightAligned = hasRightAlignmentMark(marks);
|
|
137
|
+
const enableNestedHeaderLinks = allowHeadingAnchorLinks && allowHeadingAnchorLinks.allowNestedHeaderLinks;
|
|
138
|
+
const headingIdToUse = invisible ? undefined : headingId;
|
|
139
|
+
const mouseEnterHandler = () => {
|
|
140
|
+
if (showAnchorLink && !mouseEntered.current) {
|
|
141
|
+
// Abort TTVC calculation when the mouse hovers over heading. Hovering over
|
|
142
|
+
// heading render heading anchor and inline comment buttons. These user-induced
|
|
143
|
+
// DOM changes are valid reasons to abort the TTVC calculation.
|
|
144
|
+
abortAll('new_interaction');
|
|
145
|
+
mouseEntered.current = true;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
return jsx("div", {
|
|
149
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
150
|
+
className: RENDERER_HEADING_WRAPPER,
|
|
151
|
+
"data-testid": RENDERER_HEADING_WRAPPER,
|
|
152
|
+
"data-level": props.level,
|
|
153
|
+
css: wrapperStyles
|
|
154
|
+
}, showAnchorLink && headingId && isRightAligned && jsx(WrappedHeadingAnchor, {
|
|
155
|
+
level: props.level,
|
|
156
|
+
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
157
|
+
headingId: headingId,
|
|
158
|
+
hideFromScreenReader: false
|
|
159
|
+
}), jsx(HX, {
|
|
160
|
+
id: headingIdToUse,
|
|
161
|
+
"data-local-id": localId,
|
|
162
|
+
"data-renderer-start-pos": dataAttributes['data-renderer-start-pos'],
|
|
163
|
+
"data-as-inline": asInline,
|
|
164
|
+
onMouseEnter: mouseEnterHandler
|
|
165
|
+
}, props.children), showAnchorLink && headingId && !isRightAligned && jsx(WrappedHeadingAnchor, {
|
|
166
|
+
level: props.level,
|
|
167
|
+
enableNestedHeaderLinks: enableNestedHeaderLinks,
|
|
168
|
+
headingId: headingId,
|
|
169
|
+
hideFromScreenReader: false
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Gated Heading component:
|
|
175
|
+
* - When platform_editor_copy_link_a11y_inconsistency_fix experiment is enabled,
|
|
176
|
+
* returns HeadingWithWrapper (new a11y-improved structure)
|
|
177
|
+
* - Otherwise returns HeadingWithDuplicateAnchor (old structure)
|
|
178
|
+
*/
|
|
179
|
+
function Heading({
|
|
180
|
+
allowHeadingAnchorLinks,
|
|
181
|
+
children,
|
|
182
|
+
dataAttributes,
|
|
183
|
+
headingId,
|
|
184
|
+
invisible,
|
|
185
|
+
level,
|
|
186
|
+
localId,
|
|
187
|
+
marks,
|
|
188
|
+
nodeType,
|
|
189
|
+
showAnchorLink,
|
|
190
|
+
serializer,
|
|
191
|
+
asInline
|
|
192
|
+
}) {
|
|
193
|
+
if (expValEquals('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true)) {
|
|
194
|
+
return jsx(HeadingWithWrapper, {
|
|
195
|
+
allowHeadingAnchorLinks: allowHeadingAnchorLinks,
|
|
196
|
+
dataAttributes: dataAttributes,
|
|
197
|
+
headingId: headingId,
|
|
198
|
+
invisible: invisible,
|
|
199
|
+
level: level,
|
|
200
|
+
localId: localId,
|
|
201
|
+
marks: marks,
|
|
202
|
+
nodeType: nodeType,
|
|
203
|
+
serializer: serializer,
|
|
204
|
+
showAnchorLink: showAnchorLink,
|
|
205
|
+
asInline: asInline
|
|
206
|
+
}, children);
|
|
207
|
+
}
|
|
208
|
+
return jsx(HeadingWithDuplicateAnchor, {
|
|
209
|
+
allowHeadingAnchorLinks: allowHeadingAnchorLinks,
|
|
210
|
+
dataAttributes: dataAttributes,
|
|
211
|
+
headingId: headingId,
|
|
212
|
+
invisible: invisible,
|
|
213
|
+
level: level,
|
|
214
|
+
localId: localId,
|
|
215
|
+
marks: marks,
|
|
216
|
+
nodeType: nodeType,
|
|
217
|
+
serializer: serializer,
|
|
218
|
+
showAnchorLink: showAnchorLink,
|
|
219
|
+
asInline: asInline
|
|
220
|
+
}, children);
|
|
221
|
+
}
|
|
93
222
|
export default Heading;
|