From 1bc2a6ef7655d9f1139dcb2d96af9552563676d3 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Thu, 16 Feb 2023 12:15:59 -0500 Subject: [PATCH] expose rel; fixes #1200 --- docs/resources/changelog.md | 1 + src/components/button/button.test.ts | 24 ++++++++++++++++++++++++ src/components/button/button.ts | 10 +++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md index 61a5f48a..4ca38065 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -12,6 +12,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti - Added the `focus()` and `blur()` methods to `` - Added the `sl-invalid` event to all form controls to enable custom validation logic [#1167](https://github.com/shoelace-style/shoelace/pull/1167) - Added `validity` and `validationMessage` properties to all form controls [#1167](https://github.com/shoelace-style/shoelace/pull/1167) +- Added the `rel` attribute to `` to allow users to create button links that point to specific targets [#1200](https://github.com/shoelace-style/shoelace/issues/1200) - Fixed a bug in `` where the play and pause buttons were transposed [#1147](https://github.com/shoelace-style/shoelace/issues/1147) - Fixed a bug that prevented `web-types.json` from being generated [#1154](https://github.com/shoelace-style/shoelace/discussions/1154) - Fixed a bug in `` that prevented `sl-change` and `sl-input` from emitting when using the eye dropper [#1157](https://github.com/shoelace-style/shoelace/issues/1157) diff --git a/src/components/button/button.test.ts b/src/components/button/button.test.ts index cc881fa2..16f155b5 100644 --- a/src/components/button/button.test.ts +++ b/src/components/button/button.test.ts @@ -117,6 +117,30 @@ describe('', () => { expect(el.shadowRoot!.querySelector('a')).to.exist; expect(el.shadowRoot!.querySelector('button')).not.to.exist; }); + + it('should render a link with rel="noreferrer noopener" when target is set and rel is not', async () => { + const el = await fixture( + html` Link ` + ); + const link = el.shadowRoot!.querySelector('a')!; + expect(link?.getAttribute('rel')).to.equal('noreferrer noopener'); + }); + + it('should render a link with rel="" when a target is provided and rel is empty', async () => { + const el = await fixture( + html` Link ` + ); + const link = el.shadowRoot!.querySelector('a')!; + expect(link?.getAttribute('rel')).to.equal(''); + }); + + it(`should render a link with a custom rel when a custom rel is provided`, async () => { + const el = await fixture( + html` Link ` + ); + const link = el.shadowRoot!.querySelector('a')!; + expect(link?.getAttribute('rel')).to.equal('1'); + }); }); describe('when submitting a form', () => { diff --git a/src/components/button/button.ts b/src/components/button/button.ts index 1744ea57..bbf5f4de 100644 --- a/src/components/button/button.ts +++ b/src/components/button/button.ts @@ -116,6 +116,14 @@ export default class SlButton extends ShoelaceElement implements ShoelaceFormCon /** Tells the browser where to open the link. Only used when `href` is present. */ @property() target: '_blank' | '_parent' | '_self' | '_top'; + /** + * When using `href`, this attribute will map to the underlying link's `rel` attribute. Unlike regular links, the + * default is `noreferrer noopener` to prevent security exploits. However, if you're using `target` to point to a + * specific tab/window, this will prevent that from working correctly. You can remove or change the default value by + * setting the attribute to an empty string or a value of your choice, respectively. + */ + @property() rel = 'noreferrer noopener'; + /** Tells the browser to download the linked file as this filename. Only used when `href` is present. */ @property() download?: string; @@ -308,7 +316,7 @@ export default class SlButton extends ShoelaceElement implements ShoelaceFormCon href=${ifDefined(isLink ? this.href : undefined)} target=${ifDefined(isLink ? this.target : undefined)} download=${ifDefined(isLink ? this.download : undefined)} - rel=${ifDefined(isLink && this.target ? 'noreferrer noopener' : undefined)} + rel=${ifDefined(isLink ? this.rel : undefined)} role=${ifDefined(isLink ? undefined : 'button')} aria-disabled=${this.disabled ? 'true' : 'false'} tabindex=${this.disabled ? '-1' : '0'}