UNPKG

@atlaskit/renderer

Version:
233 lines (228 loc) • 9.72 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _interactionMetrics = require("@atlaskit/react-ufo/interaction-metrics"); var _analytics = require("@atlaskit/editor-common/analytics"); var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden")); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _analyticsContext = _interopRequireDefault(require("../../analytics/analyticsContext")); var _clipboard = require("../utils/clipboard"); var _headingAnchor = _interopRequireDefault(require("./heading-anchor")); var _react2 = require("@emotion/react"); /** * @jsxRuntime classic * @jsx jsx * @jsxFrag */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 var RENDERER_HEADING_WRAPPER = 'renderer-heading-wrapper'; var getCurrentUrlWithHash = function getCurrentUrlWithHash() { var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var url = new URL(window.location.href); url.search = ''; // clear any query params so that the page will correctly scroll to the anchor url.hash = encodeURIComponent(hash); return url.href; }; function hasRightAlignmentMark(marks) { if (!marks || !marks.length) { return false; } return marks.some(function (mark) { return mark.type.name === 'alignment' && mark.attrs.align === 'end'; }); } var wrapperStyles = (0, _react2.css)({ // Important: do NOT use flex here. // With flex + baseline alignment, the anchor aligns to the *first line* of a multi-line heading, // which visually places it at the top-right. We want the anchor to sit immediately after the // last character of the heading (i.e. after the final wrapped line), so we use normal inline flow. display: 'block' }); function WrappedHeadingAnchor(_ref) { var enableNestedHeaderLinks = _ref.enableNestedHeaderLinks, level = _ref.level, headingId = _ref.headingId, hideFromScreenReader = _ref.hideFromScreenReader; return (0, _react2.jsx)(_analyticsContext.default.Consumer, null, function (_ref2) { var fireAnalyticsEvent = _ref2.fireAnalyticsEvent; return (0, _react2.jsx)(_headingAnchor.default, { enableNestedHeaderLinks: enableNestedHeaderLinks, level: level // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onCopyText: function onCopyText() { fireAnalyticsEvent({ action: _analytics.ACTION.CLICKED, actionSubject: _analytics.ACTION_SUBJECT.BUTTON, actionSubjectId: _analytics.ACTION_SUBJECT_ID.HEADING_ANCHOR_LINK, eventType: _analytics.EVENT_TYPE.UI }); return (0, _clipboard.copyTextToClipboard)(getCurrentUrlWithHash(headingId)); }, hideFromScreenReader: hideFromScreenReader, headingId: headingId }); }); } /** * Old heading structure (before a11y fix): * - headning anchor is rendered INSIDE the heading element * - A duplicate anchor is rendered in VisuallyHidden for screen readers * - The visible button has hideFromScreenReader={true} * */ function HeadingWithDuplicateAnchor(props) { var headingId = props.headingId, dataAttributes = props.dataAttributes, allowHeadingAnchorLinks = props.allowHeadingAnchorLinks, marks = props.marks, invisible = props.invisible, localId = props.localId, asInline = props.asInline; var HX = "h".concat(props.level); var mouseEntered = _react.default.useRef(false); var showAnchorLink = !!props.showAnchorLink; var isRightAligned = hasRightAlignmentMark(marks); var enableNestedHeaderLinks = allowHeadingAnchorLinks && allowHeadingAnchorLinks.allowNestedHeaderLinks; var headingIdToUse = invisible ? undefined : headingId; var mouseEnterHandler = function mouseEnterHandler() { if (showAnchorLink && !mouseEntered.current) { // Abort TTVC calculation when the mouse hovers over heading. Hovering over // heading render heading anchor and inline comment buttons. These user-induced // DOM changes are valid reasons to abort the TTVC calculation. (0, _interactionMetrics.abortAll)('new_interaction'); mouseEntered.current = true; } }; return (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)(HX, { id: headingIdToUse, "data-local-id": localId, "data-renderer-start-pos": dataAttributes['data-renderer-start-pos'], "data-as-inline": asInline, onMouseEnter: mouseEnterHandler, tabIndex: (0, _expValEquals.expValEquals)('confluence_toc_nav_a11y', 'isEnabled', true) ? -1 : undefined }, (0, _react2.jsx)(_react.default.Fragment, null, showAnchorLink && headingId && isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, { level: props.level, enableNestedHeaderLinks: enableNestedHeaderLinks, headingId: headingId, hideFromScreenReader: true }), props.children, showAnchorLink && headingId && !isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, { level: props.level, enableNestedHeaderLinks: enableNestedHeaderLinks, headingId: headingId, hideFromScreenReader: true }))), (0, _react2.jsx)(_visuallyHidden.default, { testId: "visually-hidden-heading-anchor" }, showAnchorLink && headingId && (0, _react2.jsx)(WrappedHeadingAnchor, { level: props.level, enableNestedHeaderLinks: enableNestedHeaderLinks, headingId: headingId }))); } /** * New heading structure (a11y fix): * - Heading anchor is rendered OUTSIDE the heading element in a .renderer-heading-wrapper div * - Uses data-level attribute for CSS styling * - Better accessibility: heading contains only text, button is a sibling */ function HeadingWithWrapper(props) { var headingId = props.headingId, dataAttributes = props.dataAttributes, allowHeadingAnchorLinks = props.allowHeadingAnchorLinks, marks = props.marks, invisible = props.invisible, localId = props.localId, asInline = props.asInline; var HX = "h".concat(props.level); var mouseEntered = _react.default.useRef(false); var showAnchorLink = !!props.showAnchorLink; var isRightAligned = hasRightAlignmentMark(marks); var enableNestedHeaderLinks = allowHeadingAnchorLinks && allowHeadingAnchorLinks.allowNestedHeaderLinks; var headingIdToUse = invisible ? undefined : headingId; var mouseEnterHandler = function mouseEnterHandler() { if (showAnchorLink && !mouseEntered.current) { // Abort TTVC calculation when the mouse hovers over heading. Hovering over // heading render heading anchor and inline comment buttons. These user-induced // DOM changes are valid reasons to abort the TTVC calculation. (0, _interactionMetrics.abortAll)('new_interaction'); mouseEntered.current = true; } }; return (0, _react2.jsx)("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop className: RENDERER_HEADING_WRAPPER, "data-testid": RENDERER_HEADING_WRAPPER, "data-level": props.level, css: wrapperStyles }, showAnchorLink && headingId && isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, { level: props.level, enableNestedHeaderLinks: enableNestedHeaderLinks, headingId: headingId, hideFromScreenReader: false }), (0, _react2.jsx)(HX, { id: headingIdToUse, "data-local-id": localId, "data-renderer-start-pos": dataAttributes['data-renderer-start-pos'], "data-as-inline": asInline, onMouseEnter: mouseEnterHandler, tabIndex: (0, _expValEquals.expValEquals)('confluence_toc_nav_a11y', 'isEnabled', true) ? -1 : undefined }, props.children), showAnchorLink && headingId && !isRightAligned && (0, _react2.jsx)(WrappedHeadingAnchor, { level: props.level, enableNestedHeaderLinks: enableNestedHeaderLinks, headingId: headingId, hideFromScreenReader: false })); } /** * Gated Heading component: * - When platform_editor_copy_link_a11y_inconsistency_fix experiment is enabled, * returns HeadingWithWrapper (new a11y-improved structure) * - Otherwise returns HeadingWithDuplicateAnchor (old structure) */ function Heading(_ref3) { var allowHeadingAnchorLinks = _ref3.allowHeadingAnchorLinks, children = _ref3.children, dataAttributes = _ref3.dataAttributes, headingId = _ref3.headingId, invisible = _ref3.invisible, level = _ref3.level, localId = _ref3.localId, marks = _ref3.marks, nodeType = _ref3.nodeType, showAnchorLink = _ref3.showAnchorLink, serializer = _ref3.serializer, asInline = _ref3.asInline; if ((0, _expValEquals.expValEquals)('platform_editor_copy_link_a11y_inconsistency_fix', 'isEnabled', true)) { return (0, _react2.jsx)(HeadingWithWrapper, { allowHeadingAnchorLinks: allowHeadingAnchorLinks, dataAttributes: dataAttributes, headingId: headingId, invisible: invisible, level: level, localId: localId, marks: marks, nodeType: nodeType, serializer: serializer, showAnchorLink: showAnchorLink, asInline: asInline }, children); } return (0, _react2.jsx)(HeadingWithDuplicateAnchor, { allowHeadingAnchorLinks: allowHeadingAnchorLinks, dataAttributes: dataAttributes, headingId: headingId, invisible: invisible, level: level, localId: localId, marks: marks, nodeType: nodeType, serializer: serializer, showAnchorLink: showAnchorLink, asInline: asInline }, children); } var _default = exports.default = Heading;