Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/free-candles-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@spectrum-web-components/link': patch
---

** Fixed ** : Safari keyboard navigation compatibility for sp-link component by implementing an approach with shadow DOM delegatesFocus and explicit tabindex.
20 changes: 18 additions & 2 deletions packages/link/src/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
* governing permissions and limitations under the License.
*/

import { CSSResultArray, TemplateResult } from '@spectrum-web-components/base';
import {
CSSResultArray,
SpectrumElement,
TemplateResult,
} from '@spectrum-web-components/base';
import {
property,
query,
Expand All @@ -22,6 +26,7 @@ import linkStyles from './link.css.js';

/**
* @element sp-link
*
*/
export class Link extends LikeAnchor(Focusable) {
public static override get styles(): CSSResultArray {
Expand All @@ -48,6 +53,17 @@ export class Link extends LikeAnchor(Focusable) {
}

protected override render(): TemplateResult {
return this.renderAnchor({ id: 'anchor' });
return this.renderAnchor({
id: 'anchor',
tabindex: 0,
});
}
/**
* Static property to configure shadow root options
* This enables delegatesFocus for Safari compatibility
*/
static override shadowRootOptions = {
...SpectrumElement.shadowRootOptions,
delegatesFocus: true,
};
}
31 changes: 31 additions & 0 deletions packages/link/test/link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Link } from '@spectrum-web-components/link';
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { testForLitDevWarnings } from '../../../test/testing-helpers.js';
import { spy } from 'sinon';
import { isWebKit } from '@spectrum-web-components/shared';

describe('Link', () => {
testForLitDevWarnings(
Expand Down Expand Up @@ -76,4 +77,34 @@ describe('Link', () => {
el.click();
expect(clickSpy.callCount).to.equal(0);
});

it('has proper Safari keyboard navigation support when running in WebKit', async () => {
const el = await fixture<Link>(html`
<sp-link href="test_url">WebKit Test Link</sp-link>
`);

await elementUpdated(el);

// Always verify basic configuration
expect(el.shadowRoot).to.not.be.null;
expect(el.shadowRoot?.delegatesFocus).to.be.true;

const anchor = el.shadowRoot?.querySelector(
'#anchor'
) as HTMLAnchorElement;
expect(anchor.getAttribute('tabindex')).to.eq('0');

// WebKit-specific enhanced tests
if (isWebKit()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can remove this check and run the assertions for all browsers... any reason we need to scope it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there is an upstream issue around this. It is a known limitation of iOS/Safari with custom elements and shadow DOM. See WebKit bug reports and related issues.

// Verify that the anchor element is properly focusable in Safari
expect(anchor.tabIndex).to.be.greaterThan(-1);

// Verify that the link maintains proper ARIA attributes in Safari
expect(anchor.hasAttribute('href')).to.be.true;
expect(anchor.getAttribute('role')).to.not.equal('button');
}

// Common verification for all browsers
await expect(el).to.be.accessible();
});
});
Loading