Skip to content

Commit

Permalink
Merge pull request #2750 from microsoft/u/juliaroldi/patch-9.8.1
Browse files Browse the repository at this point in the history
Rooster Patch 9.8.1
  • Loading branch information
juliaroldi authored Jul 18, 2024
2 parents db0ac97 + 454e0d2 commit 28f140b
Show file tree
Hide file tree
Showing 18 changed files with 468 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const DOT_STRING = '.';
* @param announceData Data to announce
*/
export const announce: Announce = (core, announceData) => {
const { text, defaultStrings, formatStrings = [] } = announceData;
const { text, defaultStrings, formatStrings = [], ariaLiveMode = 'assertive' } = announceData;
const { announcerStringGetter } = core.lifecycle;
const template = defaultStrings && announcerStringGetter?.(defaultStrings);
let textToAnnounce = formatString(template || text, formatStrings);
Expand All @@ -21,6 +21,10 @@ export const announce: Announce = (core, announceData) => {

if (textToAnnounce && core.lifecycle.announceContainer) {
const { announceContainer } = core.lifecycle;
if (announceContainer.ariaLive != ariaLiveMode) {
announceContainer.ariaLive = ariaLiveMode;
}

if (textToAnnounce == announceContainer.textContent) {
textToAnnounce += DOT_STRING;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { DOMSelection, EditorCore, Snapshot } from 'roosterjs-content-model-types';
import { getPositionFromPath } from './getPositionFromPath';
import { getSafeIdSelector } from 'roosterjs-content-model-dom';
import type { DOMSelection, EditorCore, Snapshot } from 'roosterjs-content-model-types';

/**
* @internal
Expand Down Expand Up @@ -29,7 +30,7 @@ export function restoreSnapshotSelection(core: EditorCore, snapshot: Snapshot) {
break;
case 'table':
const table = physicalRoot.querySelector(
'#' + snapshotSelection.tableId
getSafeIdSelector(snapshotSelection.tableId)
) as HTMLTableElement;

if (table) {
Expand All @@ -45,7 +46,7 @@ export function restoreSnapshotSelection(core: EditorCore, snapshot: Snapshot) {
break;
case 'image':
const image = physicalRoot.querySelector(
'#' + snapshotSelection.imageId
getSafeIdSelector(snapshotSelection.imageId)
) as HTMLImageElement;

if (image) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { areSameSelections } from '../../corePlugin/cache/areSameSelections';
import { ensureUniqueId } from '../setEditorStyle/ensureUniqueId';
import { findLastedCoInMergedCell } from './findLastedCoInMergedCell';
import { findTableCellElement } from './findTableCellElement';
import { isNodeOfType, parseTableCells, toArray } from 'roosterjs-content-model-dom';
import {
getSafeIdSelector,
isNodeOfType,
parseTableCells,
toArray,
} from 'roosterjs-content-model-dom';
import type {
ParsedTable,
SelectionChangedEvent,
Expand Down Expand Up @@ -59,7 +64,7 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
`outline-style:solid!important; outline-color:${
imageSelectionColor || DEFAULT_SELECTION_BORDER_COLOR
}!important;`,
[`#${ensureUniqueId(image, IMAGE_ID)}`]
[getSafeIdSelector(ensureUniqueId(image, IMAGE_ID))]
);
core.api.setEditorStyle(
core,
Expand Down Expand Up @@ -105,13 +110,21 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
};

const tableId = ensureUniqueId(table, TABLE_ID);
const tableSelector = getSafeIdSelector(tableId);

const tableSelectors =
firstCell.row == 0 &&
firstCell.col == 0 &&
lastCell.row == parsedTable.length - 1 &&
lastCell.col == (parsedTable[lastCell.row]?.length ?? 0) - 1
? [`#${tableId}`, `#${tableId} *`]
: handleTableSelected(parsedTable, tableId, table, firstCell, lastCell);
? [tableSelector, `${tableSelector} *`]
: handleTableSelected(
parsedTable,
tableSelector,
table,
firstCell,
lastCell
);

core.selection.selection = selection;

Expand Down Expand Up @@ -163,7 +176,7 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC

function handleTableSelected(
parsedTable: ParsedTable,
tableId: string,
tableSelector: string,
table: HTMLTableElement,
firstCell: TableCellCoordinate,
lastCell: TableCellCoordinate
Expand Down Expand Up @@ -214,7 +227,7 @@ function handleTableSelected(
cellIndex >= firstCell.col &&
cellIndex <= lastCell.col
) {
const selector = `#${tableId}${middleElSelector} tr:nth-child(${currentRow})>${cell.tagName}:nth-child(${tdCount})`;
const selector = `${tableSelector}${middleElSelector} tr:nth-child(${currentRow})>${cell.tagName}:nth-child(${tdCount})`;

selectors.push(selector, selector + ' *');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getSafeIdSelector } from 'roosterjs-content-model-dom';

/**
* @internal
*/
Expand All @@ -7,7 +9,7 @@ export function ensureUniqueId(element: HTMLElement, idPrefix: string): string {
const doc = element.ownerDocument;
let i = 0;

while (!element.id || doc.querySelectorAll(`[id="${element.id}"]`).length > 1) {
while (!element.id || doc.querySelectorAll(getSafeIdSelector(element.id)).length > 1) {
element.id = idPrefix + '_' + i++;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ensureUniqueId } from './ensureUniqueId';
import { getSafeIdSelector } from 'roosterjs-content-model-dom';
import type { SetEditorStyle } from 'roosterjs-content-model-types';

const MAX_RULE_SELECTOR_LENGTH = 9000;
Expand Down Expand Up @@ -34,7 +35,9 @@ export const setEditorStyle: SetEditorStyle = (
}

if (cssRule) {
const rootSelector = '#' + ensureUniqueId(core.physicalRoot, CONTENT_DIV_ID);
const rootSelector = getSafeIdSelector(
ensureUniqueId(core.physicalRoot, CONTENT_DIV_ID)
);
const selectors = !subSelectors
? [rootSelector]
: typeof subSelectors === 'string'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import type {
TableSelectionInfo,
TableCellCoordinate,
RangeSelection,
MouseUpEvent,
} from 'roosterjs-content-model-types';

const MouseLeftButton = 0;
const MouseMiddleButton = 1;
const MouseRightButton = 2;
const Up = 'ArrowUp';
const Down = 'ArrowDown';
Expand Down Expand Up @@ -140,7 +142,7 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
break;

case 'mouseUp':
this.onMouseUp();
this.onMouseUp(this.editor, event);
break;

case 'keyDown':
Expand All @@ -164,30 +166,46 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
let image: HTMLImageElement | null;

// Image selection
if (
selection?.type == 'image' &&
(rawEvent.button == MouseLeftButton ||
(rawEvent.button == MouseRightButton &&
!this.getClickingImage(rawEvent) &&
!this.getContainedTargetImage(rawEvent, selection)))
) {
this.setDOMSelection(null /*domSelection*/, null /*tableSelection*/);
}
if (editor.isExperimentalFeatureEnabled('LegacyImageSelection')) {
if (
rawEvent.button === MouseRightButton &&
(image =
this.getClickingImage(rawEvent) ??
this.getContainedTargetImage(rawEvent, selection)) &&
image.isContentEditable
) {
this.selectImageWithRange(image, rawEvent);
return;
} else if (selection?.type == 'image' && selection.image !== rawEvent.target) {
this.selectBeforeOrAfterElement(editor, selection.image);
return;
}
} else {
if (
selection?.type == 'image' &&
(rawEvent.button == MouseLeftButton ||
(rawEvent.button == MouseRightButton &&
!this.getClickingImage(rawEvent) &&
!this.getContainedTargetImage(rawEvent, selection)))
) {
this.setDOMSelection(null /*domSelection*/, null /*tableSelection*/);
}

if (
(image =
this.getClickingImage(rawEvent) ??
this.getContainedTargetImage(rawEvent, selection)) &&
image.isContentEditable
) {
this.setDOMSelection(
{
type: 'image',
image: image,
},
null
);
return;
if (
(image =
this.getClickingImage(rawEvent) ??
this.getContainedTargetImage(rawEvent, selection)) &&
image.isContentEditable
) {
this.setDOMSelection(
{
type: 'image',
image: image,
},
null
);
return;
}
}

// Table selection
Expand Down Expand Up @@ -228,6 +246,25 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
}
}

private selectImageWithRange(image: HTMLImageElement, event: Event) {
const range = image.ownerDocument.createRange();
range.selectNode(image);

const domSelection = this.editor?.getDOMSelection();
if (domSelection?.type == 'image' && image == domSelection.image) {
event.preventDefault();
} else {
this.setDOMSelection(
{
type: 'range',
isReverted: false,
range,
},
null
);
}
}

private onMouseMove = (event: Event) => {
if (this.editor && this.state.tableSelection) {
const hasTableSelection = !!this.state.tableSelection.lastCo;
Expand Down Expand Up @@ -288,7 +325,21 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
}
};

private onMouseUp() {
private onMouseUp(editor: IEditor, event: MouseUpEvent) {
let image: HTMLImageElement | null;

if (
editor.isExperimentalFeatureEnabled('LegacyImageSelection') &&
(image = this.getClickingImage(event.rawEvent)) &&
image.isContentEditable &&
event.rawEvent.button != MouseMiddleButton &&
(event.rawEvent.button ==
MouseRightButton /* it's not possible to drag using right click */ ||
event.isClicking)
) {
this.selectImageWithRange(image, event.rawEvent);
}

this.detachMouseEvent();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,64 @@ describe('announce', () => {
parentElement: {
removeChild: removeChildSpy,
},
ariaLive: 'assertive',
});
});

it('already has div with same text 2', () => {
const mockedDiv = {
textContent: 'test',
} as any;

core.lifecycle.announceContainer = mockedDiv;

announce(core, {
text: 'test',
});

expect(mockedDiv).toEqual({
textContent: 'test.',
ariaLive: 'assertive',
});
});

it('Set AriaLive polite', () => {
const mockedDiv = {
textContent: 'test',
} as any;

core.lifecycle.announceContainer = mockedDiv;

announce(core, {
text: 'test',
ariaLiveMode: 'polite',
});

expect(mockedDiv).toEqual({
textContent: 'test.',
ariaLive: 'polite',
});
});

it('Set AriaLive off', () => {
const mockedDiv = {
textContent: 'test',
} as any;

core.lifecycle.announceContainer = mockedDiv;

announce(core, {
text: 'test',
ariaLiveMode: 'off',
});

expect(mockedDiv).toEqual({
textContent: 'test.',
ariaLive: 'off',
});
});

it('already has div with same text', () => {
it('Set AriaLive off', () => {
const mockedDiv = {
textContent: 'test',
} as any;
Expand All @@ -195,10 +249,12 @@ describe('announce', () => {

announce(core, {
text: 'test',
ariaLiveMode: 'assertive',
});

expect(mockedDiv).toEqual({
textContent: 'test.',
ariaLive: 'assertive',
});
});
});
Loading

0 comments on commit 28f140b

Please sign in to comment.