Skip to content

Commit

Permalink
feat: add XPath support to getAnchor() (#813)
Browse files Browse the repository at this point in the history
Co-authored-by: Aaron <[email protected]>
  • Loading branch information
sir-kokabi and aklinker1 authored Jul 9, 2024
1 parent 6c4da72 commit 48967c7
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 3 deletions.
18 changes: 18 additions & 0 deletions packages/wxt/src/client/content-scripts/ui/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,24 @@ describe('Content Script UIs', () => {
document.querySelector('#parent > div[data-wxt-integrated]'),
).not.toBeNull();
});

it('should append the element using an XPath string', () => {
vi.stubGlobal('XPathResult', { FIRST_ORDERED_NODE_TYPE: 9 });
document.evaluate = vi.fn().mockReturnValue({
singleNodeValue: document.querySelector('#three'),
});

const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: appendTestApp,
anchor: '//p[@id="three"]',
});
ui.mount();

expect(
document.querySelector('#three > div[data-wxt-integrated]'),
).not.toBeNull();
});
});

describe('Element', () => {
Expand Down
21 changes: 19 additions & 2 deletions packages/wxt/src/client/content-scripts/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,25 @@ function getAnchor(options: ContentScriptAnchoredOptions): Element | undefined {

let resolved =
typeof options.anchor === 'function' ? options.anchor() : options.anchor;
if (typeof resolved === 'string')
return document.querySelector<Element>(resolved) ?? undefined;

if (typeof resolved === 'string') {
// If the string is an XPath expression (starts with '//' or '/')
if (resolved.startsWith('/')) {
// Evaluate the XPath and return the first ordered node
const result = document.evaluate(
resolved,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null,
);
return (result.singleNodeValue as Element) ?? undefined;
} else {
// If the string is a CSS selector, query the document and return the element
return document.querySelector<Element>(resolved) ?? undefined;
}
}

return resolved ?? undefined;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/wxt/src/client/content-scripts/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export type ContentScriptPositioningOptions =

export interface ContentScriptAnchoredOptions {
/**
* A CSS selector, element, or function that returns one of the two. Along with `append`, the
* A CSS selector, XPath expression, element, or function that returns one of the three. Along with `append`, the
* `anchor` dictates where in the page the UI will be added.
*/
anchor?:
Expand Down

0 comments on commit 48967c7

Please sign in to comment.