@atlaskit/renderer 126.3.0 → 126.5.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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # @atlaskit/renderer
2
2
 
3
+ ## 126.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`d88e2cfa7371b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d88e2cfa7371b) -
8
+ [ux] fix copy heading link button a11y behaviours by only having one button, that is outside the
9
+ heading element
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 126.4.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [`26a5ec6dab84e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/26a5ec6dab84e) -
20
+ Adjust vertical spacing for inline extensions in the renderer
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies
25
+
3
26
  ## 126.3.0
4
27
 
5
28
  ### Minor Changes
@@ -14,6 +14,16 @@ var headingAnchorLinkMessages = exports.headingAnchorLinkMessages = (0, _reactIn
14
14
  defaultMessage: 'Copy link to heading',
15
15
  description: 'Copy heading link to clipboard'
16
16
  },
17
+ copyLinkToClipboard: {
18
+ id: 'fabric.editor.headingLink.copyAnchorLinkTo',
19
+ defaultMessage: 'Copy link to',
20
+ description: 'Copy heading link to clipboard. Will be used as part of a 2-part a11y label ("Copy link to", "{heading text}")'
21
+ },
22
+ copyHeadingLinkLabelledBy: {
23
+ id: 'fabric.editor.headingLink.copyAnchorLinkLabelledBy',
24
+ defaultMessage: '{copyLink} {heading}',
25
+ 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'
26
+ },
17
27
  copiedHeadingLinkToClipboard: {
18
28
  id: 'fabric.editor.headingLink.copied',
19
29
  defaultMessage: 'Copied!',
@@ -19,6 +19,7 @@ var _react = _interopRequireDefault(require("react"));
19
19
  var _react2 = require("@emotion/react");
20
20
  var _reactIntlNext = require("react-intl-next");
21
21
  var _link = _interopRequireDefault(require("@atlaskit/icon/core/link"));
22
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
22
23
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
23
24
  var _messages = require("../../messages");
24
25
  var _excluded = ["children"];
@@ -51,13 +52,10 @@ var copyAnchorButtonStyles = (0, _react2.css)({
51
52
  // Ignored via go/ees005
52
53
  // eslint-disable-next-line @repo/internal/react/no-class-components
53
54
  var HeadingAnchor = /*#__PURE__*/function (_React$PureComponent) {
54
- function HeadingAnchor() {
55
+ function HeadingAnchor(props) {
55
56
  var _this;
56
57
  (0, _classCallCheck2.default)(this, HeadingAnchor);
57
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
58
- args[_key] = arguments[_key];
59
- }
60
- _this = _callSuper(this, HeadingAnchor, [].concat(args));
58
+ _this = _callSuper(this, HeadingAnchor, [props]);
61
59
  (0, _defineProperty2.default)(_this, "state", {
62
60
  tooltipMessage: '',
63
61
  isClicked: false
@@ -104,30 +102,38 @@ var HeadingAnchor = /*#__PURE__*/function (_React$PureComponent) {
104
102
  };
105
103
  }());
106
104
  (0, _defineProperty2.default)(_this, "resetMessage", function () {
107
- _this.setTooltipState(_messages.headingAnchorLinkMessages.copyHeadingLinkToClipboard);
105
+ var tooltip = (0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? _messages.headingAnchorLinkMessages.copyLinkToClipboard : _messages.headingAnchorLinkMessages.copyHeadingLinkToClipboard;
106
+ _this.setTooltipState(tooltip);
108
107
  });
109
108
  (0, _defineProperty2.default)(_this, "renderAnchorButton", function () {
110
109
  var _this$props = _this.props,
111
110
  _this$props$hideFromS = _this$props.hideFromScreenReader,
112
111
  hideFromScreenReader = _this$props$hideFromS === void 0 ? false : _this$props$hideFromS,
113
112
  headingId = _this$props.headingId;
113
+ var labelledBy = (0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? _this.props.intl.formatMessage(_messages.headingAnchorLinkMessages.copyHeadingLinkLabelledBy, {
114
+ copyLink: _this.copyLinkId,
115
+ heading: headingId
116
+ }) : headingId;
117
+ var tabIndex = (0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? 0 : hideFromScreenReader ? undefined : -1;
114
118
  return (0, _react2.jsx)("button", {
115
119
  "data-testid": "anchor-button",
120
+ id: _this.copyLinkId,
116
121
  css: copyAnchorButtonStyles
117
122
  // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
118
123
  ,
119
124
  onMouseLeave: _this.resetMessage,
120
125
  onClick: _this.copyToClipboard,
121
126
  "aria-hidden": hideFromScreenReader,
122
- tabIndex: hideFromScreenReader ? undefined : -1,
127
+ tabIndex: tabIndex,
123
128
  "aria-label": hideFromScreenReader ? undefined : _this.state.tooltipMessage,
124
- "aria-labelledby": hideFromScreenReader ? undefined : headingId,
129
+ "aria-labelledby": hideFromScreenReader ? undefined : labelledBy,
125
130
  type: "button"
126
131
  }, (0, _react2.jsx)(_link.default, {
127
132
  label: _this.getCopyAriaLabel(),
128
133
  color: _this.state.isClicked ? "var(--ds-icon-selected, #1868DB)" : "var(--ds-icon-subtle, #505258)"
129
134
  }));
130
135
  });
136
+ _this.copyLinkId = (0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true) ? crypto.randomUUID() : undefined;
131
137
  return _this;
132
138
  }
133
139
  (0, _inherits2.default)(HeadingAnchor, _React$PureComponent);
@@ -6,12 +6,22 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _react = _interopRequireDefault(require("react"));
9
- var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden"));
10
9
  var _interactionMetrics = require("@atlaskit/react-ufo/interaction-metrics");
11
10
  var _analytics = require("@atlaskit/editor-common/analytics");
11
+ var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden"));
12
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
12
13
  var _analyticsContext = _interopRequireDefault(require("../../analytics/analyticsContext"));
13
14
  var _clipboard = require("../utils/clipboard");
14
15
  var _headingAnchor = _interopRequireDefault(require("./heading-anchor"));
16
+ var _react2 = require("@emotion/react");
17
+ /**
18
+ * @jsxRuntime classic
19
+ * @jsx jsx
20
+ * @jsxFrag
21
+ */
22
+
23
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
24
+
15
25
  var getCurrentUrlWithHash = function getCurrentUrlWithHash() {
16
26
  var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
17
27
  var url = new URL(window.location.href);
@@ -27,14 +37,18 @@ function hasRightAlignmentMark(marks) {
27
37
  return mark.type.name === 'alignment' && mark.attrs.align === 'end';
28
38
  });
29
39
  }
40
+ var wrapperStyles = (0, _react2.css)({
41
+ display: 'flex',
42
+ alignItems: 'baseline'
43
+ });
30
44
  function WrappedHeadingAnchor(_ref) {
31
45
  var enableNestedHeaderLinks = _ref.enableNestedHeaderLinks,
32
46
  level = _ref.level,
33
47
  headingId = _ref.headingId,
34
48
  hideFromScreenReader = _ref.hideFromScreenReader;
35
- return /*#__PURE__*/_react.default.createElement(_analyticsContext.default.Consumer, null, function (_ref2) {
49
+ return (0, _react2.jsx)(_analyticsContext.default.Consumer, null, function (_ref2) {
36
50
  var fireAnalyticsEvent = _ref2.fireAnalyticsEvent;
37
- return /*#__PURE__*/_react.default.createElement(_headingAnchor.default, {
51
+ return (0, _react2.jsx)(_headingAnchor.default, {
38
52
  enableNestedHeaderLinks: enableNestedHeaderLinks,
39
53
  level: level,
40
54
  onCopyText: function onCopyText() {
@@ -51,7 +65,14 @@ function WrappedHeadingAnchor(_ref) {
51
65
  });
52
66
  });
53
67
  }
54
- function Heading(props) {
68
+ /**
69
+ * Old heading structure (before a11y fix):
70
+ * - headning anchor is rendered INSIDE the heading element
71
+ * - A duplicate anchor is rendered in VisuallyHidden for screen readers
72
+ * - The visible button has hideFromScreenReader={true}
73
+ *
74
+ */
75
+ function HeadingWithDuplicateAnchor(props) {
55
76
  var headingId = props.headingId,
56
77
  dataAttributes = props.dataAttributes,
57
78
  allowHeadingAnchorLinks = props.allowHeadingAnchorLinks,
@@ -74,28 +95,132 @@ function Heading(props) {
74
95
  mouseEntered.current = true;
75
96
  }
76
97
  };
77
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(HX, {
98
+ return (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)(HX, {
78
99
  id: headingIdToUse,
79
100
  "data-local-id": localId,
80
101
  "data-renderer-start-pos": dataAttributes['data-renderer-start-pos'],
81
102
  "data-as-inline": asInline,
82
103
  onMouseEnter: mouseEnterHandler
83
- }, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, showAnchorLink && headingId && isRightAligned && /*#__PURE__*/_react.default.createElement(WrappedHeadingAnchor, {
104
+ }, (0, _react2.jsx)(_react.default.Fragment, null, showAnchorLink && headingId && isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, {
84
105
  level: props.level,
85
106
  enableNestedHeaderLinks: enableNestedHeaderLinks,
86
107
  headingId: headingId,
87
108
  hideFromScreenReader: true
88
- }), props.children, showAnchorLink && headingId && !isRightAligned && /*#__PURE__*/_react.default.createElement(WrappedHeadingAnchor, {
109
+ }), props.children, showAnchorLink && headingId && !isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, {
89
110
  level: props.level,
90
111
  enableNestedHeaderLinks: enableNestedHeaderLinks,
91
112
  headingId: headingId,
92
113
  hideFromScreenReader: true
93
- }))), /*#__PURE__*/_react.default.createElement(_visuallyHidden.default, {
114
+ }))), (0, _react2.jsx)(_visuallyHidden.default, {
94
115
  testId: "visually-hidden-heading-anchor"
95
- }, showAnchorLink && headingId && /*#__PURE__*/_react.default.createElement(WrappedHeadingAnchor, {
116
+ }, showAnchorLink && headingId && (0, _react2.jsx)(WrappedHeadingAnchor, {
96
117
  level: props.level,
97
118
  enableNestedHeaderLinks: enableNestedHeaderLinks,
98
119
  headingId: headingId
99
120
  })));
100
121
  }
122
+
123
+ /**
124
+ * New heading structure (a11y fix):
125
+ * - Heading anchor is rendered OUTSIDE the heading element in a .renderer-heading-wrapper div
126
+ * - Uses data-level attribute for CSS styling
127
+ * - Better accessibility: heading contains only text, button is a sibling
128
+ */
129
+ function HeadingWithWrapper(props) {
130
+ var headingId = props.headingId,
131
+ dataAttributes = props.dataAttributes,
132
+ allowHeadingAnchorLinks = props.allowHeadingAnchorLinks,
133
+ marks = props.marks,
134
+ invisible = props.invisible,
135
+ localId = props.localId,
136
+ asInline = props.asInline;
137
+ var HX = "h".concat(props.level);
138
+ var mouseEntered = _react.default.useRef(false);
139
+ var showAnchorLink = !!props.showAnchorLink;
140
+ var isRightAligned = hasRightAlignmentMark(marks);
141
+ var enableNestedHeaderLinks = allowHeadingAnchorLinks && allowHeadingAnchorLinks.allowNestedHeaderLinks;
142
+ var headingIdToUse = invisible ? undefined : headingId;
143
+ var mouseEnterHandler = function mouseEnterHandler() {
144
+ if (showAnchorLink && !mouseEntered.current) {
145
+ // Abort TTVC calculation when the mouse hovers over heading. Hovering over
146
+ // heading render heading anchor and inline comment buttons. These user-induced
147
+ // DOM changes are valid reasons to abort the TTVC calculation.
148
+ (0, _interactionMetrics.abortAll)('new_interaction');
149
+ mouseEntered.current = true;
150
+ }
151
+ };
152
+ return (
153
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
154
+ (0, _react2.jsx)("div", {
155
+ className: "renderer-heading-wrapper",
156
+ "data-level": props.level,
157
+ css: wrapperStyles
158
+ }, showAnchorLink && headingId && isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, {
159
+ level: props.level,
160
+ enableNestedHeaderLinks: enableNestedHeaderLinks,
161
+ headingId: headingId,
162
+ hideFromScreenReader: false
163
+ }), (0, _react2.jsx)(HX, {
164
+ id: headingIdToUse,
165
+ "data-local-id": localId,
166
+ "data-renderer-start-pos": dataAttributes['data-renderer-start-pos'],
167
+ "data-as-inline": asInline,
168
+ onMouseEnter: mouseEnterHandler
169
+ }, props.children), showAnchorLink && headingId && !isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, {
170
+ level: props.level,
171
+ enableNestedHeaderLinks: enableNestedHeaderLinks,
172
+ headingId: headingId,
173
+ hideFromScreenReader: false
174
+ }))
175
+ );
176
+ }
177
+
178
+ /**
179
+ * Gated Heading component:
180
+ * - When platform_editor_copy_link_a11y_inconsistency_fix experiment is enabled,
181
+ * returns HeadingWithWrapper (new a11y-improved structure)
182
+ * - Otherwise returns HeadingWithDuplicateAnchor (old structure)
183
+ */
184
+ function Heading(_ref3) {
185
+ var allowHeadingAnchorLinks = _ref3.allowHeadingAnchorLinks,
186
+ children = _ref3.children,
187
+ dataAttributes = _ref3.dataAttributes,
188
+ headingId = _ref3.headingId,
189
+ invisible = _ref3.invisible,
190
+ level = _ref3.level,
191
+ localId = _ref3.localId,
192
+ marks = _ref3.marks,
193
+ nodeType = _ref3.nodeType,
194
+ showAnchorLink = _ref3.showAnchorLink,
195
+ serializer = _ref3.serializer,
196
+ asInline = _ref3.asInline;
197
+ if ((0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true)) {
198
+ return (0, _react2.jsx)(HeadingWithWrapper, {
199
+ allowHeadingAnchorLinks: allowHeadingAnchorLinks,
200
+ dataAttributes: dataAttributes,
201
+ headingId: headingId,
202
+ invisible: invisible,
203
+ level: level,
204
+ localId: localId,
205
+ marks: marks,
206
+ nodeType: nodeType,
207
+ serializer: serializer,
208
+ showAnchorLink: showAnchorLink,
209
+ asInline: asInline
210
+ }, children);
211
+ }
212
+ return (0, _react2.jsx)(HeadingWithDuplicateAnchor, {
213
+ allowHeadingAnchorLinks: allowHeadingAnchorLinks,
214
+ dataAttributes: dataAttributes,
215
+ headingId: headingId,
216
+ invisible: invisible,
217
+ level: level,
218
+ localId: localId,
219
+ marks: marks,
220
+ nodeType: nodeType,
221
+ serializer: serializer,
222
+ showAnchorLink: showAnchorLink,
223
+ asInline: asInline
224
+ }, children);
225
+ }
101
226
  var _default = exports.default = Heading;