Skip to content

Commit

Permalink
fix anchor meta and ctrl key navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdholt committed Jun 17, 2024
1 parent 7df0be1 commit 45609f3
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 11 deletions.
59 changes: 59 additions & 0 deletions packages/web-components/src/anchor-button/anchor-button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,63 @@ test.describe('Anchor Button', () => {
await expect(proxy).toHaveAttribute(`${attribute}`, `${value}`);
});
}

test('should navigate to the provided url when clicked', async ({ page }) => {
const element = page.locator('fluent-anchor-button');
const expectedUrl = '#foo';

await page.setContent(/* html */ `
<fluent-anchor-button href="${expectedUrl}"></fluent-anchor-button>
`);

await element.click();

expect(page.url()).toContain(expectedUrl);
});

test('should navigate to the provided url when clicked while pressing the `metaKey`', async ({ page, context }) => {
const element = page.locator('fluent-anchor-button');
const expectedUrl = 'https://www.microsoft.com';

await page.setContent(/* html */ `
<fluent-anchor-button href="${expectedUrl}"></fluent-anchor-button>
`);

const [newPage] = await Promise.all([context.waitForEvent('page'), element.click({ modifiers: ['Meta'] })]);

expect(newPage.url()).toContain(expectedUrl);
});

test('should navigate to the provided url when `Enter` is pressed via keyboard', async ({ page }) => {
const element = page.locator('fluent-anchor-button');
const expectedUrl = '#foo';

await page.setContent(/* html */ `
<fluent-anchor-button href="${expectedUrl}"></fluent-anchor-button>
`);

await element.focus();

await element.press('Enter');

expect(page.url()).toContain(expectedUrl);
});

test('should navigate to the provided url when `ctrl` and `Enter` are pressed via keyboard', async ({
page,
context,
}) => {
const element = page.locator('fluent-anchor-button');
const expectedUrl = 'https://www.microsoft.com';

await page.setContent(/* html */ `
<fluent-anchor-button href="${expectedUrl}"></fluent-anchor-button>
`);

await element.focus();

const [newPage] = await Promise.all([context.waitForEvent('page'), element.press('Control+Enter')]);

expect(newPage.url()).toContain(expectedUrl);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export function anchorTemplate<T extends AnchorButton>(options: AnchorOptions =
return html<T>`
<template
tabindex="0"
@click="${x => x.clickHandler()}"
@keypress="${(x, c) => x.keypressHandler(c.event as KeyboardEvent)}"
@click="${(x, c) => x.clickHandler(c.event as PointerEvent)}"
@keydown="${(x, c) => x.keydownHandler(c.event as KeyboardEvent)}"
>
${startSlotTemplate(options)}
<span class="content" part="content">
Expand Down
35 changes: 28 additions & 7 deletions packages/web-components/src/anchor-button/anchor-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export type AnchorOptions = StartEndOptions<AnchorButton>;
* @public
*/
export class BaseAnchor extends FASTElement {
/**
* Holds a reference to the platform to manage ctrl+click on Windows and cmd+click on Mac
* @internal
*/
private readonly isMac = navigator.userAgent.includes('Mac');

/**
* The internal {@link https://developer.mozilla.org/docs/Web/API/ElementInternals | `ElementInternals`} instance for the component.
*
Expand Down Expand Up @@ -177,28 +183,43 @@ export class BaseAnchor extends FASTElement {
* @param e - The event object
* @internal
*/
public clickHandler(): boolean {
this.internalProxyAnchor.click();
public clickHandler(e: PointerEvent): boolean {
if (this.href) {
this.handleNavigation(e);
}

return true;
}

/**
* Handles keypress events for the anchor.
* Handles keydown events for the anchor.
*
* @param e - the keyboard event
* @returns - the return value of the click handler
* @public
*/
public keypressHandler(e: KeyboardEvent): boolean | void {
if (e.key === keyEnter) {
this.internalProxyAnchor.click();
return;
public keydownHandler(e: KeyboardEvent): boolean | void {
if (this.href) {
if (e.key === keyEnter) {
this.handleNavigation(e);
return;
}
}

return true;
}

/**
* Handles navigation based on input
* If the metaKey is pressed, opens the href in a new window, if false, uses the click on the proxy
* @internal
*/
private handleNavigation(e: KeyboardEvent | PointerEvent): void {
const newTab = !this.isMac ? e.ctrlKey : e.metaKey || e.ctrlKey;

newTab ? window.open(this.href, '_blank') : this.internalProxyAnchor.click();
}

/**
* A method for updating proxy attributes when attributes have changed
* @internal
Expand Down
4 changes: 2 additions & 2 deletions packages/web-components/src/link/link.template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export function anchorTemplate<T extends Link>(): ViewTemplate<T> {
return html<T>`
<template
tabindex="0"
@click="${x => x.clickHandler()}"
@keypress="${(x, c) => x.keypressHandler(c.event as KeyboardEvent)}"
@click="${(x, c) => x.clickHandler(c.event as PointerEvent)}"
@keydown="${(x, c) => x.keydownHandler(c.event as KeyboardEvent)}"
>
<slot></slot>
</template>
Expand Down

0 comments on commit 45609f3

Please sign in to comment.