mirror of
https://github.com/Automattic/harper.git
synced 2025-12-23 08:48:15 +00:00
fix(chrome-ext): return focus to source element when SuggestionBox is closed (#2282)
* fix(chrome-ext): return focus to source element when `SuggestionBox` is closed * test(chrome-ext): make sure focus ends up in the right places * fix(chrome-ext): ignoring a suggestion should result in the right focus as well
This commit is contained in:
parent
611475176d
commit
edff925df0
3 changed files with 39 additions and 4 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { test } from './fixtures';
|
||||
import {
|
||||
assertHarperHighlightBoxes,
|
||||
assertLocatorIsFocused,
|
||||
clickHarperHighlight,
|
||||
getTextarea,
|
||||
replaceEditorContent,
|
||||
|
|
@ -73,4 +74,6 @@ test('Can dismiss with escape key', async ({ page }) => {
|
|||
await page.keyboard.press('Escape');
|
||||
|
||||
await page.locator('.harper-container').waitFor({ state: 'hidden' });
|
||||
|
||||
await assertLocatorIsFocused(page, editor);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,6 +38,20 @@ export function getHarperHighlights(page: Page): Locator {
|
|||
return page.locator('#harper-highlight');
|
||||
}
|
||||
|
||||
export async function assertLocatorIsFocused(page: Page, loc: Locator) {
|
||||
await assertLocatorsResolveEqually(page, loc, page.locator(':focus'));
|
||||
}
|
||||
|
||||
/** Checks that the two provided locators resolve to the same element. */
|
||||
export async function assertLocatorsResolveEqually(page: Page, a: Locator, b: Locator) {
|
||||
const areSame = await page.evaluate(
|
||||
([a, b]) => a === b,
|
||||
[await a.elementHandle(), await b.elementHandle()],
|
||||
);
|
||||
|
||||
expect(areSame).toBe(true);
|
||||
}
|
||||
|
||||
/** Locates the first Harper highlight on the page and clicks it.
|
||||
* It should result in the popup opening.
|
||||
* Returns whether the highlight was found. */
|
||||
|
|
@ -97,6 +111,7 @@ export async function testBasicSuggestionTextarea(testPageUrl: TestPageUrlProvid
|
|||
await page.waitForTimeout(3000);
|
||||
|
||||
expect(editor).toHaveValue('This is a test');
|
||||
await assertLocatorIsFocused(page, editor);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +141,7 @@ export async function testCanIgnoreTextareaSuggestion(testPageUrl: TestPageUrlPr
|
|||
// Nothing should change.
|
||||
expect(editor).toHaveValue(cacheSalt);
|
||||
expect(await clickHarperHighlight(page)).toBe(false);
|
||||
await assertLocatorIsFocused(page, editor);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +162,7 @@ export async function testCanBlockRuleTextareaSuggestion(testPageUrl: TestPageUr
|
|||
await page.waitForTimeout(1000);
|
||||
|
||||
await assertHarperHighlightBoxes(page, []);
|
||||
await assertLocatorIsFocused(page, editor);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ function iconSvg(definition: IconDefinition): string {
|
|||
const settingsIconSvg = iconSvg(faGear);
|
||||
const disableIconSvg = iconSvg(faBan);
|
||||
|
||||
let previouslyActiveElement: null | HTMLElement = null;
|
||||
|
||||
var FocusHook: any = function () {};
|
||||
FocusHook.prototype.hook = function (node: any, _propertyName: any, _previousValue: any) {
|
||||
if ((node as any).__harperAutofocused) {
|
||||
|
|
@ -23,6 +25,10 @@ FocusHook.prototype.hook = function (node: any, _propertyName: any, _previousVal
|
|||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (document.activeElement?.tagName.toLowerCase() != 'harper-render-box') {
|
||||
previouslyActiveElement = document.activeElement as HTMLElement;
|
||||
}
|
||||
|
||||
node.focus();
|
||||
Object.defineProperty(node, '__harperAutofocused', {
|
||||
value: true,
|
||||
|
|
@ -453,19 +459,26 @@ export default function SuggestionBox(
|
|||
transformOrigin: `${bottom ? 'bottom' : 'top'} left`,
|
||||
};
|
||||
|
||||
const ignoreLintCallback = box.ignoreLint;
|
||||
|
||||
const refocusClose = () => {
|
||||
previouslyActiveElement?.focus();
|
||||
close();
|
||||
};
|
||||
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
className: 'harper-container fade-in',
|
||||
style: positionStyle,
|
||||
'harper-close-on-escape': new CloseOnEscapeHook(close),
|
||||
'harper-close-on-escape': new CloseOnEscapeHook(refocusClose),
|
||||
},
|
||||
[
|
||||
styleTag(box.lint.lint_kind),
|
||||
header(
|
||||
box.lint.lint_kind_pretty,
|
||||
lintKindColor(box.lint.lint_kind),
|
||||
close,
|
||||
refocusClose,
|
||||
actions.openOptions,
|
||||
box.rule,
|
||||
actions.setRuleEnabled,
|
||||
|
|
@ -474,13 +487,15 @@ export default function SuggestionBox(
|
|||
footer(
|
||||
suggestions(box.lint.lint_kind, box.lint.suggestions, (v) => {
|
||||
box.applySuggestion(v);
|
||||
close();
|
||||
refocusClose();
|
||||
}),
|
||||
[
|
||||
box.lint.lint_kind === 'Spelling' && actions.addToUserDictionary
|
||||
? addToDictionary(box, actions.addToUserDictionary)
|
||||
: undefined,
|
||||
box.ignoreLint ? ignoreLint(box.ignoreLint) : undefined,
|
||||
ignoreLintCallback
|
||||
? ignoreLint(() => ignoreLintCallback().then(refocusClose))
|
||||
: undefined,
|
||||
],
|
||||
),
|
||||
hintDrawer(hint),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue