@atlaskit/editor-plugin-show-diff 16.0.1 → 16.0.2

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 (45) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/afm-products/tsconfig.json +3 -0
  3. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +86 -50
  4. package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +19 -8
  5. package/dist/cjs/pm-plugins/decorations/utils/getMarkChangeRanges.js +18 -4
  6. package/dist/cjs/pm-plugins/decorations/utils/mapStepRangeToFinal.js +34 -0
  7. package/dist/cjs/pm-plugins/main.js +18 -0
  8. package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +26 -4
  9. package/dist/cjs/ui/ContributorTag/contributorLabel.js +55 -0
  10. package/dist/cjs/ui/ContributorTag/contributorTagController.js +14 -39
  11. package/dist/cjs/ui/ContributorTag/messages.js +5 -0
  12. package/dist/cjs/ui/DiffNavigation/announceActiveDiff.js +54 -0
  13. package/dist/cjs/ui/DiffNavigation/messages.js +19 -0
  14. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +43 -12
  15. package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +16 -0
  16. package/dist/es2019/pm-plugins/decorations/utils/getMarkChangeRanges.js +12 -4
  17. package/dist/es2019/pm-plugins/decorations/utils/mapStepRangeToFinal.js +28 -0
  18. package/dist/es2019/pm-plugins/main.js +18 -0
  19. package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +24 -4
  20. package/dist/es2019/ui/ContributorTag/contributorLabel.js +49 -0
  21. package/dist/es2019/ui/ContributorTag/contributorTagController.js +14 -39
  22. package/dist/es2019/ui/ContributorTag/messages.js +5 -0
  23. package/dist/es2019/ui/DiffNavigation/announceActiveDiff.js +49 -0
  24. package/dist/es2019/ui/DiffNavigation/messages.js +13 -0
  25. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +86 -50
  26. package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +19 -8
  27. package/dist/esm/pm-plugins/decorations/utils/getMarkChangeRanges.js +17 -4
  28. package/dist/esm/pm-plugins/decorations/utils/mapStepRangeToFinal.js +28 -0
  29. package/dist/esm/pm-plugins/main.js +18 -0
  30. package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +26 -4
  31. package/dist/esm/ui/ContributorTag/contributorLabel.js +49 -0
  32. package/dist/esm/ui/ContributorTag/contributorTagController.js +14 -39
  33. package/dist/esm/ui/ContributorTag/messages.js +5 -0
  34. package/dist/esm/ui/DiffNavigation/announceActiveDiff.js +48 -0
  35. package/dist/esm/ui/DiffNavigation/messages.js +13 -0
  36. package/dist/types/pm-plugins/decorations/utils/getAttrChangeRanges.d.ts +3 -0
  37. package/dist/types/pm-plugins/decorations/utils/getMarkChangeRanges.d.ts +4 -1
  38. package/dist/types/pm-plugins/decorations/utils/mapStepRangeToFinal.d.ts +8 -0
  39. package/dist/types/showDiffPluginType.d.ts +7 -1
  40. package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +5 -2
  41. package/dist/types/ui/ContributorTag/contributorLabel.d.ts +13 -0
  42. package/dist/types/ui/ContributorTag/messages.d.ts +1 -0
  43. package/dist/types/ui/DiffNavigation/announceActiveDiff.d.ts +18 -0
  44. package/dist/types/ui/DiffNavigation/messages.d.ts +5 -0
  45. package/package.json +3 -2
@@ -125,6 +125,19 @@ var srLabelStyle = convertToInlineCss({
125
125
  userSelect: 'none',
126
126
  whiteSpace: 'nowrap'
127
127
  });
128
+
129
+ /**
130
+ * Ids for the hidden label that the tag's `aria-labelledby` points at. Namespaced and random because
131
+ * the tag renders inside the document and shares the host page's id space — the same reason
132
+ * `VanillaTooltip` does it this way. The counter is a fallback for non-secure contexts, where
133
+ * `crypto.randomUUID` is unavailable.
134
+ */
135
+ var generateLabelId = function () {
136
+ var count = 0;
137
+ return function () {
138
+ return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? "".concat(CONTRIBUTOR_TAG_CLASS, "-label-").concat(crypto.randomUUID()) : "".concat(CONTRIBUTOR_TAG_CLASS, "-label-").concat(count += 1);
139
+ };
140
+ }();
128
141
  var createSpan = function createSpan(doc, style) {
129
142
  var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
130
143
  var span = doc.createElement('span');
@@ -144,21 +157,30 @@ var createSpan = function createSpan(doc, style) {
144
157
  * would need a cast per reference below.
145
158
  */
146
159
  export var buildContributorTagDom = function buildContributorTagDom(doc) {
160
+ var labelId = generateLabelId();
147
161
  var root = createSpan(doc, constraintStyle);
148
162
  var tag = createSpan(doc, tagStyle, {
149
163
  class: CONTRIBUTOR_TAG_CLASS,
150
164
  'data-testid': CONTRIBUTOR_TAG_TESTID,
151
- // Deliberately no `aria-label`: it would be announced ahead of the element's contents, but
152
- // design requires the contributor *after* the change — hence the visually hidden label.
165
+ // Named by the hidden label rather than `aria-label`, which would be a second copy of the same
166
+ // sentence. `role="note"` takes its name from the author, not its contents, so without this
167
+ // the tag is a focus stop with no accessible name.
168
+ 'aria-labelledby': labelId,
153
169
  role: 'note',
154
- // Focusable so keyboard and screen-reader users can reach the tag and its tooltip.
170
+ // Focusable so keyboard and screen-reader users can reach the tag and its tooltip. Its focus
171
+ // ring is drawn inward — see `contributorTagStyles` in editor-core.
155
172
  tabindex: '0'
156
173
  });
157
174
  var avatars = createSpan(doc, avatarsStyle);
175
+ // `aria-hidden`: the hidden label restates this name in a full sentence, so leaving it in the
176
+ // tree announced the contributor twice ("Priya Changed by Priya").
158
177
  var name = createSpan(doc, nameStyle, {
178
+ 'aria-hidden': 'true',
159
179
  'data-testid': CONTRIBUTOR_TAG_NAME_TESTID
160
180
  });
161
- var srLabel = createSpan(doc, srLabelStyle);
181
+ var srLabel = createSpan(doc, srLabelStyle, {
182
+ id: labelId
183
+ });
162
184
  tag.append(avatars, name, srLabel);
163
185
  root.appendChild(tag);
164
186
  return {
@@ -0,0 +1,49 @@
1
+ import { contributorTagMessages } from './messages';
2
+ var getContributorName = function getContributorName(contributor, formatMessage) {
3
+ var _contributor$name, _contributor$agentKin;
4
+ var name = (_contributor$name = contributor.name) === null || _contributor$name === void 0 ? void 0 : _contributor$name.trim();
5
+ if (name) {
6
+ return name;
7
+ }
8
+
9
+ // `agentKind` defaults to `'external'` per the plugin type contract; an unidentified external
10
+ // agent falls back to a generic label rather than rendering a nameless tag.
11
+ if (contributor.kind === 'agent' && ((_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external') === 'external') {
12
+ return formatMessage(contributorTagMessages.externalAgentName);
13
+ }
14
+ return '';
15
+ };
16
+
17
+ /**
18
+ * Whether the label has to spell out that the contributor is an agent. A sighted user reads that off
19
+ * the `aria-hidden` avatar, so the label is the only other carrier.
20
+ *
21
+ * Excluded: a connected pair, which says it via `changedByConnected`, and an unnamed external agent,
22
+ * whose name `getContributorName` has already resolved to "External agent".
23
+ */
24
+ var isUnaccompaniedNamedAgent = function isUnaccompaniedNamedAgent(model) {
25
+ var _model$contributor$na;
26
+ return !model.connectedContributor && model.contributor.kind === 'agent' && Boolean((_model$contributor$na = model.contributor.name) === null || _model$contributor$na === void 0 ? void 0 : _model$contributor$na.trim());
27
+ };
28
+ /**
29
+ * The two strings a tag's model resolves to. Shared with `getActiveDiffAnnouncement`, so stepping
30
+ * onto a change says the same sentence as the tag pinned to it rather than a copy free to drift.
31
+ */
32
+ export var formatContributorLabel = function formatContributorLabel(model, formatMessage) {
33
+ var isPrimaryAgent = model.contributor.kind === 'agent';
34
+ var primaryName = getContributorName(model.contributor, formatMessage);
35
+ var secondaryName = model.connectedContributor ? getContributorName(model.connectedContributor, formatMessage) : '';
36
+ var agentName = isPrimaryAgent ? primaryName : secondaryName;
37
+ var userName = isPrimaryAgent ? secondaryName : primaryName;
38
+ return {
39
+ fullLabel: model.connectedContributor ? formatMessage(contributorTagMessages.changedByConnected, {
40
+ agentName: agentName,
41
+ userName: userName
42
+ }) : formatMessage(isUnaccompaniedNamedAgent(model) ? contributorTagMessages.changedByAgent : contributorTagMessages.changedBy, {
43
+ name: primaryName
44
+ }),
45
+ // A connected pair only ever shows the agent; the pair is spelled out in `fullLabel`, because
46
+ // the tag is capped at `MAX_TAG_WIDTH` and two names rarely fit inside it.
47
+ visibleName: model.connectedContributor ? agentName || primaryName : primaryName
48
+ };
49
+ };
@@ -8,22 +8,7 @@ import { getAccentTokens } from '../../pm-plugins/decorations/colorSchemes/facto
8
8
  import { colorSchemeRegistry } from '../../pm-plugins/decorations/colorSchemes/schemes';
9
9
  import { buildContributorTagDom, CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE, TAG_EXIT_FALLBACK_MS } from './buildContributorTagDom';
10
10
  import { contributorAvatarRenderer } from './contributorAvatarRenderer';
11
- import { contributorTagMessages } from './messages';
12
- var getContributorName = function getContributorName(contributor, formatMessage) {
13
- var _contributor$name, _contributor$agentKin;
14
- var name = (_contributor$name = contributor.name) === null || _contributor$name === void 0 ? void 0 : _contributor$name.trim();
15
- if (name) {
16
- return name;
17
- }
18
-
19
- // `agentKind` defaults to `'external'` per the plugin type contract; an unidentified external
20
- // agent falls back to a generic label rather than rendering a nameless tag.
21
- if (contributor.kind === 'agent' && ((_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external') === 'external') {
22
- return formatMessage(contributorTagMessages.externalAgentName);
23
- }
24
- return '';
25
- };
26
-
11
+ import { formatContributorLabel } from './contributorLabel';
27
12
  /**
28
13
  * Resolves the tag's accent from the same scheme value the highlight was drawn from, so the colour
29
14
  * the two carry can never drift — only the tone does, which is `getAccentTokens`'s to pick.
@@ -106,8 +91,8 @@ var fadeTagOut = function fadeTagOut(tag, release) {
106
91
  /** The contributors a tag draws, keyed so a republished model can be recognised as the same pair. */
107
92
  var identityOf = function identityOf(model) {
108
93
  return [model === null || model === void 0 ? void 0 : model.contributor, model === null || model === void 0 ? void 0 : model.connectedContributor].map(function (contributor) {
109
- var _contributor$agentKin2, _contributor$avatarUr;
110
- return contributor ? "".concat(contributor.kind, ":").concat((_contributor$agentKin2 = contributor.agentKind) !== null && _contributor$agentKin2 !== void 0 ? _contributor$agentKin2 : '', ":").concat(contributor.name, ":").concat((_contributor$avatarUr = contributor.avatarUrl) !== null && _contributor$avatarUr !== void 0 ? _contributor$avatarUr : '') : '';
94
+ var _contributor$agentKin, _contributor$avatarUr;
95
+ return contributor ? "".concat(contributor.kind, ":").concat((_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : '', ":").concat(contributor.name, ":").concat((_contributor$avatarUr = contributor.avatarUrl) !== null && _contributor$avatarUr !== void 0 ? _contributor$avatarUr : '') : '';
111
96
  }).join('|');
112
97
  };
113
98
 
@@ -348,24 +333,12 @@ export var ContributorTagController = /*#__PURE__*/function () {
348
333
  if (!this.dom) {
349
334
  return;
350
335
  }
351
- var _this$options$getIntl = this.options.getIntl(),
352
- formatMessage = _this$options$getIntl.formatMessage;
353
- var isPrimaryAgent = model.contributor.kind === 'agent';
354
- var primaryName = getContributorName(model.contributor, formatMessage);
355
- var secondaryName = model.connectedContributor ? getContributorName(model.connectedContributor, formatMessage) : '';
356
- var agentName = isPrimaryAgent ? primaryName : secondaryName;
357
- var userName = isPrimaryAgent ? secondaryName : primaryName;
358
-
359
- // A connected pair only ever shows the agent; the pair is spelled out in the label, because the
360
- // tag is capped at `MAX_TAG_WIDTH` and two names rarely fit inside it.
361
- this.dom.name.textContent = model.connectedContributor ? agentName || primaryName : primaryName;
362
- this.fullLabel = model.connectedContributor ? formatMessage(contributorTagMessages.changedByConnected, {
363
- agentName: agentName,
364
- userName: userName
365
- }) : formatMessage(contributorTagMessages.changedBy, {
366
- name: primaryName
367
- });
368
- this.dom.srLabel.textContent = this.fullLabel;
336
+ var _formatContributorLab = formatContributorLabel(model, this.options.getIntl().formatMessage),
337
+ fullLabel = _formatContributorLab.fullLabel,
338
+ visibleName = _formatContributorLab.visibleName;
339
+ this.dom.name.textContent = visibleName;
340
+ this.fullLabel = fullLabel;
341
+ this.dom.srLabel.textContent = fullLabel;
369
342
  }
370
343
  }, {
371
344
  key: "accentOf",
@@ -420,7 +393,7 @@ export var ContributorTagController = /*#__PURE__*/function () {
420
393
  }, {
421
394
  key: "syncTooltip",
422
395
  value: function syncTooltip() {
423
- var _this$tooltip2;
396
+ var _this$tooltip2, _this$dom$tag$querySe;
424
397
  var content = this.dom ? this.fullLabel : undefined;
425
398
  if (content === this.tooltipContent) {
426
399
  return;
@@ -437,9 +410,11 @@ export var ContributorTagController = /*#__PURE__*/function () {
437
410
  // renaming. Those styles are scoped under `.ProseMirror`, and the tag renders inside it.
438
411
  'ak-editor-vanilla-tooltip-default');
439
412
  // `VanillaTooltip` sets `aria-describedby` on its trigger, which would announce the label a
440
- // second time — the visually hidden child already announces it, after the change as design
441
- // requires.
413
+ // second time — the hidden child already announces it, and names the tag with it.
442
414
  this.dom.tag.removeAttribute('aria-describedby');
415
+ // The popover is a child of the trigger, so its text would be a third copy of the same
416
+ // sentence. It stays visible for sighted users, who need the part of the name the tag clipped.
417
+ (_this$dom$tag$querySe = this.dom.tag.querySelector(':scope > [role="tooltip"]')) === null || _this$dom$tag$querySe === void 0 || _this$dom$tag$querySe.setAttribute('aria-hidden', 'true');
443
418
  }
444
419
 
445
420
  /**
@@ -5,6 +5,11 @@ export var contributorTagMessages = defineMessages({
5
5
  defaultMessage: 'Changed by {name}',
6
6
  description: 'Screen-reader label and tooltip for the tag pinned to a highlighted change in the version-history diff, naming the single person or agent that made the change. The name placeholder is the contributor display name.'
7
7
  },
8
+ changedByAgent: {
9
+ id: 'editor-plugin-show-diff.ContributorTag.changedByAgent',
10
+ defaultMessage: 'Changed by {name}, an AI agent',
11
+ description: 'Screen-reader label and tooltip for the tag pinned to a highlighted change in the version-history diff, when a named AI agent made the change rather than a person. The name placeholder is the agent display name.'
12
+ },
8
13
  changedByConnected: {
9
14
  id: 'editor-plugin-show-diff.ContributorTag.changedByConnected',
10
15
  defaultMessage: 'Changed by {agentName} with {userName}',
@@ -0,0 +1,48 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ import { isDiffDecoration } from '../../pm-plugins/decorations/decorationKeys';
5
+ import { formatContributorLabel } from '../ContributorTag/contributorLabel';
6
+ import { diffNavigationMessages } from './messages';
7
+
8
+ /**
9
+ * The tag that captions the given decoration, if any. `linkedDiffIds` is checked as well as
10
+ * `diffId`, because a replacement's two decorations share one tag and navigation can land on either.
11
+ */
12
+ var findTagFor = function findTagFor(decoration, contributorTags) {
13
+ if (!(contributorTags !== null && contributorTags !== void 0 && contributorTags.length) || !isDiffDecoration(decoration)) {
14
+ return undefined;
15
+ }
16
+ var diffId = decoration.spec.diffId;
17
+ return contributorTags.find(function (tag) {
18
+ var _tag$linkedDiffIds;
19
+ return tag.diffId === diffId || ((_tag$linkedDiffIds = tag.linkedDiffIds) === null || _tag$linkedDiffIds === void 0 ? void 0 : _tag$linkedDiffIds.includes(diffId));
20
+ });
21
+ };
22
+
23
+ /**
24
+ * What a screen reader should say once the reader has stepped onto a change: where they are in the
25
+ * diff, and — when the plugin can credit the change — who made it. Stepping otherwise only scrolls,
26
+ * which is silent.
27
+ *
28
+ * Returns `undefined` for an index that resolves no change, so the caller says nothing at all
29
+ * rather than "Change 4 of 3".
30
+ */
31
+ export var getActiveDiffAnnouncement = function getActiveDiffAnnouncement(_ref) {
32
+ var activeIndex = _ref.activeIndex,
33
+ contributorTags = _ref.contributorTags,
34
+ decorations = _ref.decorations,
35
+ intl = _ref.intl;
36
+ var activeDecoration = decorations[activeIndex];
37
+ if (!activeDecoration) {
38
+ return undefined;
39
+ }
40
+ var position = {
41
+ index: activeIndex + 1,
42
+ total: decorations.length
43
+ };
44
+ var tag = findTagFor(activeDecoration, contributorTags);
45
+ return tag ? intl.formatMessage(diffNavigationMessages.activeChangeWithContributor, _objectSpread(_objectSpread({}, position), {}, {
46
+ contributor: formatContributorLabel(tag, intl.formatMessage).fullLabel
47
+ })) : intl.formatMessage(diffNavigationMessages.activeChange, position);
48
+ };
@@ -0,0 +1,13 @@
1
+ import { defineMessages } from 'react-intl';
2
+ export var diffNavigationMessages = defineMessages({
3
+ activeChange: {
4
+ id: 'editor-plugin-show-diff.DiffNavigation.activeChange',
5
+ defaultMessage: 'Change {index} of {total}',
6
+ description: 'Announced to screen readers when the reader steps to the next or previous change in the version-history diff. The index placeholder is the position of the change stepped to, counting from 1; total is how many changes the diff has.'
7
+ },
8
+ activeChangeWithContributor: {
9
+ id: 'editor-plugin-show-diff.DiffNavigation.activeChangeWithContributor',
10
+ defaultMessage: 'Change {index} of {total}. {contributor}',
11
+ description: 'Announced to screen readers when the reader steps to the next or previous change in the version-history diff and that change is credited to someone. The index and total placeholders are as in activeChange. The contributor placeholder is an already-translated sentence (changedBy, changedByAgent or changedByConnected), so it must be kept whole and only repositioned.'
12
+ }
13
+ });
@@ -5,6 +5,7 @@ import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
5
5
  export type InlineAttrChangeNodeName = 'date' | 'emoji' | 'mention' | 'status';
6
6
  type AttrChangeStep = AttrStep | SetAttrsStep;
7
7
  export type AttrStepContext = {
8
+ attributionKey?: string;
8
9
  afterNode: PMNode | null;
9
10
  beforeNode: PMNode | null;
10
11
  finalPos: number;
@@ -12,6 +13,8 @@ export type AttrStepContext = {
12
13
  step: AttrChangeStep;
13
14
  };
14
15
  type StepRange = {
16
+ /** Attribution key for the step that produced this range. */
17
+ attributionKey?: string;
15
18
  /**
16
19
  * Position of the original (before) node in the original doc.
17
20
  * Populated for inline attr changes (e.g. emoji, date) so the caller can
@@ -1,7 +1,10 @@
1
1
  import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform-override';
2
+ import { type StepMap } from '@atlaskit/editor-prosemirror/transform';
3
+ import type { DiffStepAttribution } from '../../../showDiffPluginType';
2
4
  type StepRange = {
5
+ attributionKey?: string;
3
6
  fromB: number;
4
7
  toB: number;
5
8
  };
6
- export declare const getMarkChangeRanges: (steps: ProseMirrorStep[]) => StepRange[];
9
+ export declare const getMarkChangeRanges: (steps: ProseMirrorStep[], stepAttributions?: Array<DiffStepAttribution | undefined>, stepMaps?: StepMap[]) => StepRange[];
7
10
  export {};
@@ -0,0 +1,8 @@
1
+ import { type StepMap } from '@atlaskit/editor-prosemirror/transform';
2
+ type StepRange = {
3
+ fromB: number;
4
+ toB: number;
5
+ };
6
+ /** Maps a step's range into final-document coordinates when the rollout path supplies step maps. */
7
+ export declare const mapStepRangeToFinal: (from: number, to: number, stepIndex: number, stepMaps?: StepMap[]) => StepRange | undefined;
8
+ export {};
@@ -1,6 +1,7 @@
1
1
  import type { StepJson } from '@atlaskit/editor-common/collab';
2
2
  import type { NextEditorPlugin, EditorCommand, OptionalPlugin } from '@atlaskit/editor-common/types';
3
3
  import type { JSONDocNode } from '@atlaskit/editor-json-transformer/types';
4
+ import type { AccessibilityUtilsPlugin } from '@atlaskit/editor-plugin-accessibility-utils';
4
5
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
5
6
  import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
6
7
  import type { Node } from '@atlaskit/editor-prosemirror/model';
@@ -226,7 +227,12 @@ export type ShowDiffPlugin = NextEditorPlugin<'showDiff', {
226
227
  scrollToPrevious: EditorCommand;
227
228
  showDiff: (config: ShowDiffParams) => EditorCommand;
228
229
  };
229
- dependencies: [OptionalPlugin<AnalyticsPlugin>, OptionalPlugin<UserIntentPlugin>];
230
+ dependencies: [
231
+ OptionalPlugin<AnalyticsPlugin>,
232
+ OptionalPlugin<UserIntentPlugin>,
233
+ /** Carries the live-region announcement made when stepping between changes. */
234
+ OptionalPlugin<AccessibilityUtilsPlugin>
235
+ ];
230
236
  pluginConfiguration: DiffParams | undefined;
231
237
  sharedState: {
232
238
  /**
@@ -39,8 +39,11 @@ export type ContributorTagDom = {
39
39
  name: HTMLSpanElement;
40
40
  root: HTMLSpanElement;
41
41
  /**
42
- * Announced after the name, which is the order design requires. `VanillaTooltip` appends its
43
- * popover to the tag as well, so this is the last *content* child rather than the last node.
42
+ * The tag's single announcement: it names the tag via `aria-labelledby` and is the only child
43
+ * left in the accessibility tree.
44
+ *
45
+ * `VanillaTooltip` appends its popover to the tag as well, so this is the last *content* child
46
+ * rather than the last node.
44
47
  */
45
48
  srLabel: HTMLSpanElement;
46
49
  tag: HTMLSpanElement;
@@ -0,0 +1,13 @@
1
+ import type { IntlShape } from 'react-intl';
2
+ import type { ContributorTagModel } from '../../showDiffPluginType';
3
+ export type ContributorLabel = {
4
+ /** One sentence crediting everyone the change is attributed to — the tag's announcement. */
5
+ fullLabel: string;
6
+ /** The name the tag paints, which `MAX_TAG_WIDTH` may ellipsise. */
7
+ visibleName: string;
8
+ };
9
+ /**
10
+ * The two strings a tag's model resolves to. Shared with `getActiveDiffAnnouncement`, so stepping
11
+ * onto a change says the same sentence as the tag pinned to it rather than a copy free to drift.
12
+ */
13
+ export declare const formatContributorLabel: (model: ContributorTagModel, formatMessage: IntlShape['formatMessage']) => ContributorLabel;
@@ -1,6 +1,7 @@
1
1
  import { type MessageDescriptor } from 'react-intl';
2
2
  export declare const contributorTagMessages: {
3
3
  changedBy: MessageDescriptor;
4
+ changedByAgent: MessageDescriptor;
4
5
  changedByConnected: MessageDescriptor;
5
6
  externalAgentName: MessageDescriptor;
6
7
  };
@@ -0,0 +1,18 @@
1
+ import type { IntlShape } from 'react-intl';
2
+ import type { Decoration } from '@atlaskit/editor-prosemirror/view';
3
+ import type { ContributorTagModel } from '../../showDiffPluginType';
4
+ /**
5
+ * What a screen reader should say once the reader has stepped onto a change: where they are in the
6
+ * diff, and — when the plugin can credit the change — who made it. Stepping otherwise only scrolls,
7
+ * which is silent.
8
+ *
9
+ * Returns `undefined` for an index that resolves no change, so the caller says nothing at all
10
+ * rather than "Change 4 of 3".
11
+ */
12
+ export declare const getActiveDiffAnnouncement: ({ activeIndex, contributorTags, decorations, intl, }: {
13
+ activeIndex: number;
14
+ contributorTags: ContributorTagModel[] | undefined;
15
+ /** The navigable changes, in document order — `getScrollableDecorations`'s result. */
16
+ decorations: Decoration[];
17
+ intl: IntlShape;
18
+ }) => string | undefined;
@@ -0,0 +1,5 @@
1
+ import { type MessageDescriptor } from 'react-intl';
2
+ export declare const diffNavigationMessages: {
3
+ activeChange: MessageDescriptor;
4
+ activeChangeWithContributor: MessageDescriptor;
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "16.0.1",
3
+ "version": "16.0.2",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -23,6 +23,7 @@
23
23
  "@atlaskit/adf-schema": "^57.4.0",
24
24
  "@atlaskit/avatar": "^28.0.0",
25
25
  "@atlaskit/custom-steps": "^1.1.0",
26
+ "@atlaskit/editor-plugin-accessibility-utils": "^18.0.0",
26
27
  "@atlaskit/editor-plugin-analytics": "^18.0.0",
27
28
  "@atlaskit/editor-plugin-user-intent": "^16.0.0",
28
29
  "@atlaskit/editor-prosemirror": "^8.0.0",
@@ -31,7 +32,7 @@
31
32
  "@atlaskit/platform-feature-experiments": "^0.3.0",
32
33
  "@atlaskit/platform-feature-flags": "^2.2.0",
33
34
  "@atlaskit/primitives": "^22.5.0",
34
- "@atlaskit/tmp-editor-statsig": "^188.0.0",
35
+ "@atlaskit/tmp-editor-statsig": "^189.0.0",
35
36
  "@atlaskit/tokens": "^16.12.0",
36
37
  "@babel/runtime": "^7.0.0",
37
38
  "@compiled/react": "^1.0.2",