diff --git a/libs/core/src/components.d.ts b/libs/core/src/components.d.ts index 239a7802e..ff046805b 100644 --- a/libs/core/src/components.d.ts +++ b/libs/core/src/components.d.ts @@ -1352,6 +1352,38 @@ export namespace Components { * @example Opens in new tab */ "target"?: '_blank' | '_self' | '_parent' | '_top'; + /** + * Enables or disables Turbo Drive for this link. Maps to the `data-turbo` attribute on the inner anchor element. + */ + "turbo"?: string; + /** + * Controls the Turbo visit action type. Maps to the `data-turbo-action` attribute on the inner anchor element. + */ + "turboAction"?: 'advance' | 'replace'; + /** + * Displays a confirmation dialog before navigating. Maps to the `data-turbo-confirm` attribute on the inner anchor element. + */ + "turboConfirm"?: string; + /** + * Specifies the Turbo Frame to target for navigation. Maps to the `data-turbo-frame` attribute on the inner anchor element. + */ + "turboFrame"?: string; + /** + * Changes the HTTP method for the link request. Maps to the `data-turbo-method` attribute on the inner anchor element. + */ + "turboMethod"?: 'get' | 'post' | 'put' | 'patch' | 'delete'; + /** + * Controls link prefetching on hover. Maps to the `data-turbo-prefetch` attribute on the inner anchor element. + */ + "turboPrefetch"?: string; + /** + * Eagerly preloads the link's destination into cache. Maps to the `data-turbo-preload` attribute on the inner anchor element. + */ + "turboPreload"?: string; + /** + * Accepts Turbo Stream responses for GET requests. Maps to the `data-turbo-stream` attribute on the inner anchor element. + */ + "turboStream"?: string; /** * Sets the link variant styles. * @defaultValue inline @@ -4472,6 +4504,38 @@ declare namespace LocalJSX { * @example Opens in new tab */ "target"?: '_blank' | '_self' | '_parent' | '_top'; + /** + * Enables or disables Turbo Drive for this link. Maps to the `data-turbo` attribute on the inner anchor element. + */ + "turbo"?: string; + /** + * Controls the Turbo visit action type. Maps to the `data-turbo-action` attribute on the inner anchor element. + */ + "turboAction"?: 'advance' | 'replace'; + /** + * Displays a confirmation dialog before navigating. Maps to the `data-turbo-confirm` attribute on the inner anchor element. + */ + "turboConfirm"?: string; + /** + * Specifies the Turbo Frame to target for navigation. Maps to the `data-turbo-frame` attribute on the inner anchor element. + */ + "turboFrame"?: string; + /** + * Changes the HTTP method for the link request. Maps to the `data-turbo-method` attribute on the inner anchor element. + */ + "turboMethod"?: 'get' | 'post' | 'put' | 'patch' | 'delete'; + /** + * Controls link prefetching on hover. Maps to the `data-turbo-prefetch` attribute on the inner anchor element. + */ + "turboPrefetch"?: string; + /** + * Eagerly preloads the link's destination into cache. Maps to the `data-turbo-preload` attribute on the inner anchor element. + */ + "turboPreload"?: string; + /** + * Accepts Turbo Stream responses for GET requests. Maps to the `data-turbo-stream` attribute on the inner anchor element. + */ + "turboStream"?: string; /** * Sets the link variant styles. * @defaultValue inline diff --git a/libs/core/src/components/pds-link/pds-link.tsx b/libs/core/src/components/pds-link/pds-link.tsx index 6d71ada83..d805268d0 100644 --- a/libs/core/src/components/pds-link/pds-link.tsx +++ b/libs/core/src/components/pds-link/pds-link.tsx @@ -62,6 +62,54 @@ export class PdsLink { */ @Prop() href!: string; + /** + * Enables or disables Turbo Drive for this link. + * Maps to the `data-turbo` attribute on the inner anchor element. + */ + @Prop() turbo?: string; + + /** + * Specifies the Turbo Frame to target for navigation. + * Maps to the `data-turbo-frame` attribute on the inner anchor element. + */ + @Prop() turboFrame?: string; + + /** + * Controls the Turbo visit action type. + * Maps to the `data-turbo-action` attribute on the inner anchor element. + */ + @Prop() turboAction?: 'advance' | 'replace'; + + /** + * Changes the HTTP method for the link request. + * Maps to the `data-turbo-method` attribute on the inner anchor element. + */ + @Prop() turboMethod?: 'get' | 'post' | 'put' | 'patch' | 'delete'; + + /** + * Displays a confirmation dialog before navigating. + * Maps to the `data-turbo-confirm` attribute on the inner anchor element. + */ + @Prop() turboConfirm?: string; + + /** + * Accepts Turbo Stream responses for GET requests. + * Maps to the `data-turbo-stream` attribute on the inner anchor element. + */ + @Prop() turboStream?: string; + + /** + * Controls link prefetching on hover. + * Maps to the `data-turbo-prefetch` attribute on the inner anchor element. + */ + @Prop() turboPrefetch?: string; + + /** + * Eagerly preloads the link's destination into cache. + * Maps to the `data-turbo-preload` attribute on the inner anchor element. + */ + @Prop() turboPreload?: string; + private classNames() { const classNames = ['pds-link']; @@ -105,6 +153,14 @@ export class PdsLink { target={targetValue} rel={relValue} style={this.setLinkStyles()} + data-turbo={this.turbo || undefined} + data-turbo-frame={this.turboFrame || undefined} + data-turbo-action={this.turboAction || undefined} + data-turbo-method={this.turboMethod || undefined} + data-turbo-confirm={this.turboConfirm || undefined} + data-turbo-stream={this.turboStream || undefined} + data-turbo-prefetch={this.turboPrefetch || undefined} + data-turbo-preload={this.turboPreload || undefined} > {this.href} {showExternalIcon && diff --git a/libs/core/src/components/pds-link/readme.md b/libs/core/src/components/pds-link/readme.md index 2f6076467..cce0d8170 100644 --- a/libs/core/src/components/pds-link/readme.md +++ b/libs/core/src/components/pds-link/readme.md @@ -7,16 +7,24 @@ Link is mainly used as navigational element and usually appear within or directl ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ----------- | -| `color` | `color` | Sets the link color. | `string` | `undefined` | -| `componentId` | `component-id` | A unique identifier used for the underlying component `id` attribute. | `string` | `undefined` | -| `download` | `download` | Prompts the user to save the linked URL instead of navigating to it. It can be used without a value to download with the default filename, or with a string value to suggest a specific filename for the download. | `string` | `undefined` | -| `external` | `external` | **[DEPRECATED]** Consider using the `target` prop for more control. This prop will be maintained for backward compatibility.

Determines whether the link should open in a new tab. | `boolean` | `false` | -| `fontSize` | `font-size` | The font size of the link's text. | `"lg" \| "md" \| "sm"` | `'lg'` | -| `href` _(required)_ | `href` | The hyperlink's destination URL. If no text is provided in the custom slot, the href will be used. | `string` | `undefined` | -| `target` | `target` | Specifies where to open the linked document. | `"_blank" \| "_parent" \| "_self" \| "_top"` | `undefined` | -| `variant` | `variant` | Sets the link variant styles. | `"inline" \| "plain"` | `'inline'` | +| Property | Attribute | Description | Type | Default | +| ------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------- | +| `color` | `color` | Sets the link color. | `string` | `undefined` | +| `componentId` | `component-id` | A unique identifier used for the underlying component `id` attribute. | `string` | `undefined` | +| `download` | `download` | Prompts the user to save the linked URL instead of navigating to it. It can be used without a value to download with the default filename, or with a string value to suggest a specific filename for the download. | `string` | `undefined` | +| `external` | `external` | **[DEPRECATED]** Consider using the `target` prop for more control. This prop will be maintained for backward compatibility.

Determines whether the link should open in a new tab. | `boolean` | `false` | +| `fontSize` | `font-size` | The font size of the link's text. | `"lg" \| "md" \| "sm"` | `'lg'` | +| `href` _(required)_ | `href` | The hyperlink's destination URL. If no text is provided in the custom slot, the href will be used. | `string` | `undefined` | +| `target` | `target` | Specifies where to open the linked document. | `"_blank" \| "_parent" \| "_self" \| "_top"` | `undefined` | +| `turbo` | `turbo` | Enables or disables Turbo Drive for this link. Maps to the `data-turbo` attribute on the inner anchor element. | `string` | `undefined` | +| `turboAction` | `turbo-action` | Controls the Turbo visit action type. Maps to the `data-turbo-action` attribute on the inner anchor element. | `"advance" \| "replace"` | `undefined` | +| `turboConfirm` | `turbo-confirm` | Displays a confirmation dialog before navigating. Maps to the `data-turbo-confirm` attribute on the inner anchor element. | `string` | `undefined` | +| `turboFrame` | `turbo-frame` | Specifies the Turbo Frame to target for navigation. Maps to the `data-turbo-frame` attribute on the inner anchor element. | `string` | `undefined` | +| `turboMethod` | `turbo-method` | Changes the HTTP method for the link request. Maps to the `data-turbo-method` attribute on the inner anchor element. | `"delete" \| "get" \| "patch" \| "post" \| "put"` | `undefined` | +| `turboPrefetch` | `turbo-prefetch` | Controls link prefetching on hover. Maps to the `data-turbo-prefetch` attribute on the inner anchor element. | `string` | `undefined` | +| `turboPreload` | `turbo-preload` | Eagerly preloads the link's destination into cache. Maps to the `data-turbo-preload` attribute on the inner anchor element. | `string` | `undefined` | +| `turboStream` | `turbo-stream` | Accepts Turbo Stream responses for GET requests. Maps to the `data-turbo-stream` attribute on the inner anchor element. | `string` | `undefined` | +| `variant` | `variant` | Sets the link variant styles. | `"inline" \| "plain"` | `'inline'` | ## Slots diff --git a/libs/core/src/components/pds-link/test/pds-link.spec.tsx b/libs/core/src/components/pds-link/test/pds-link.spec.tsx index 54ab69c75..4b27ddcc3 100644 --- a/libs/core/src/components/pds-link/test/pds-link.spec.tsx +++ b/libs/core/src/components/pds-link/test/pds-link.spec.tsx @@ -118,4 +118,115 @@ describe('pds-link', () => { const anchor = root?.shadowRoot?.querySelector('a'); expect(anchor?.getAttribute('download')).toBe('my-file.pdf'); }); + + describe('turbo props', () => { + it('renders data-turbo on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo')).toBe('true'); + }); + + it('renders data-turbo-frame on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-frame')).toBe('admin-content'); + }); + + it('renders data-turbo-action on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-action')).toBe('replace'); + }); + + it('renders data-turbo-method on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Delete`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-method')).toBe('delete'); + }); + + it('renders data-turbo-confirm on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Delete`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-confirm')).toBe('Are you sure?'); + }); + + it('renders data-turbo-stream on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-stream')).toBe('true'); + }); + + it('renders data-turbo-prefetch on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-prefetch')).toBe('false'); + }); + + it('renders data-turbo-preload on the inner anchor', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo-preload')).toBe('true'); + }); + + it('omits turbo data attributes when props are not set', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.hasAttribute('data-turbo')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-frame')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-action')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-method')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-confirm')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-stream')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-prefetch')).toBe(false); + expect(anchor?.hasAttribute('data-turbo-preload')).toBe(false); + }); + + it('renders multiple turbo attributes together', async () => { + const { root } = await newSpecPage({ + components: [PdsLink], + html: `Page`, + }); + + const anchor = root?.shadowRoot?.querySelector('a'); + expect(anchor?.getAttribute('data-turbo')).toBe('true'); + expect(anchor?.getAttribute('data-turbo-frame')).toBe('content'); + expect(anchor?.getAttribute('data-turbo-action')).toBe('advance'); + }); + }); });