Skip to content

fix(components): LinkToExternal retake #2867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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/tall-rings-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hashicorp/design-system-components': patch
---

Introduce the `hds-resolve-link-to-component` utility to correctly resolve the LinkTo component when `@isRouteExternal` is set on `HdsBreadcrumbItem` or `HdsInteractive`. Consumers are now required to install `ember-engines` when `@isRouteExternal` is `true`.
8 changes: 7 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@
"webpack": "^5.97.1"
},
"peerDependencies": {
"ember-source": "^3.28.0 || ^4.0.0 || ^5.3.0"
"ember-source": "^3.28.0 || ^4.0.0 || ^5.3.0",
"ember-engines": ">= 0.11.0"
},
"peerDependenciesMeta": {
"ember-engines": {
"optional": true
}
},
"ember": {
"edition": "octane"
Expand Down
48 changes: 15 additions & 33 deletions packages/components/src/components/hds/breadcrumb/item.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,20 @@
<span class="hds-breadcrumb__text">{{@text}}</span>
</div>
{{else}}
{{#if @isRouteExternal}}
<LinkToExternal
class="hds-breadcrumb__link"
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
>
{{#if @icon}}
<div class="hds-breadcrumb__icon">
<Hds::Icon @name={{@icon}} @size="16" @stretched={{true}} />
</div>
{{/if}}
<span class="hds-breadcrumb__text">{{@text}}</span>
</LinkToExternal>
{{else}}
<LinkTo
class="hds-breadcrumb__link"
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
>
{{#if @icon}}
<div class="hds-breadcrumb__icon">
<Hds::Icon @name={{@icon}} @size="16" @stretched={{true}} />
</div>
{{/if}}
<span class="hds-breadcrumb__text">{{@text}}</span>
</LinkTo>
{{/if}}
<this.linkToComponent
class="hds-breadcrumb__link"
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
>
{{#if @icon}}
<div class="hds-breadcrumb__icon">
<Hds::Icon @name={{@icon}} @size="16" @stretched={{true}} />
</div>
{{/if}}
<span class="hds-breadcrumb__text">{{@text}}</span>
</this.linkToComponent>
{{/if}}
</li>
15 changes: 14 additions & 1 deletion packages/components/src/components/hds/breadcrumb/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
import Component from '@glimmer/component';
import { htmlSafe } from '@ember/template';
import { assert } from '@ember/debug';

import { hdsResolveLinkToComponent } from '../../../utils/hds-resolve-link-to-component.ts';

import type Owner from '@ember/owner';
import type { LinkTo } from '@ember/routing';
import type { SafeString } from '@ember/template';
import type { HdsIconSignature } from '../icon';
import type { HdsIconSignature } from '../icon/index';

export interface HdsBreadcrumbItemSignature {
Args: {
Expand All @@ -27,6 +32,14 @@ export interface HdsBreadcrumbItemSignature {
}

export default class HdsBreadcrumbItem extends Component<HdsBreadcrumbItemSignature> {
linkToComponent: LinkTo;

constructor(owner: Owner, args: HdsBreadcrumbItemSignature['Args']) {
super(owner, args);

this.linkToComponent = hdsResolveLinkToComponent(args.isRouteExternal);
}

/**
* @param maxWidth
* @type {string}
Expand Down
27 changes: 8 additions & 19 deletions packages/components/src/components/hds/interactive/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,14 @@
{{! IMPORTANT: we need to add "squishies" here (~) because otherwise the whitespace added by Ember becomes visible in the link (being an inline element) - See https://handlebarsjs.com/guide/expressions.html#whitespace-control }}
{{! NOTICE: we can't support the direct use of the "href" HTML attribute via ...attributes in the <a> elements, because we need to rely on the "@href" Ember argument to differentiate between different types of generated output }}
{{~#if @route~}}
{{~#if this.isRouteExternal~}}
<LinkToExternal
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
...attributes
>{{yield}}</LinkToExternal>
{{~else~}}
<LinkTo
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
...attributes
>{{yield}}</LinkTo>
{{~/if~}}
<this.linkToComponent
@current-when={{@current-when}}
@models={{hds-link-to-models @model @models}}
@query={{hds-link-to-query @query}}
@replace={{@replace}}
@route={{@route}}
...attributes
>{{yield}}</this.linkToComponent>
{{~else if @href~}}
{{~#if this.isHrefExternal~}}
<a target="_blank" rel="noopener noreferrer" ...attributes href={{@href}} {{on "keyup" this.onKeyUp}}>{{yield}}</a>
Expand Down
13 changes: 13 additions & 0 deletions packages/components/src/components/hds/interactive/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';

import { hdsResolveLinkToComponent } from '../../../utils/hds-resolve-link-to-component.ts';

import type Owner from '@ember/owner';
import type { LinkTo } from '@ember/routing';

export interface HdsInteractiveSignature {
Args: {
href?: string;
Expand All @@ -27,6 +32,14 @@ export interface HdsInteractiveSignature {
}

export default class HdsInteractive extends Component<HdsInteractiveSignature> {
linkToComponent: LinkTo;

constructor(owner: Owner, args: HdsInteractiveSignature['Args']) {
super(owner, args);

this.linkToComponent = hdsResolveLinkToComponent(args.isRouteExternal);
}

/**
* Determines if a @href value is "external" (it adds target="_blank" rel="noopener noreferrer")
*
Expand Down
33 changes: 33 additions & 0 deletions packages/components/src/utils/hds-resolve-link-to-component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { LinkTo } from '@ember/routing';
import { assert } from '@ember/debug';
import {
dependencySatisfies,
importSync,
macroCondition,
} from '@embroider/macros';

/**
* Resolves the correct component to use for the `LinkTo`.
*
* @param isRouteExternal - If true, will return the `LinkToExternal` component. If `ember-engines` is not installed, an assertion will be thrown.
* @returns The correct component to use for the `LinkTo`.
*/
export function hdsResolveLinkToComponent(
isRouteExternal?: boolean
): typeof LinkTo {
if (isRouteExternal) {
if (macroCondition(dependencySatisfies('ember-engines', '*'))) {
// @ts-expect-error: shape is unknown
return importSync(
'ember-engines/components/link-to-external-component.js'
).default as LinkTo;
} else {
assert(
`@isRouteExternal is only available when using the "ember-engines" addon. Please install it to use this feature.`,
false
);
}
}

return LinkTo;
}