+
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/interactive/index.hbs b/packages/components/addon/components/hds/interactive/index.hbs
new file mode 100644
index 00000000000..69943383e6a
--- /dev/null
+++ b/packages/components/addon/components/hds/interactive/index.hbs
@@ -0,0 +1,33 @@
+{{! IMPORTANT: we removed the newlines before/after the yield to reduce the issues with unexpected whitespaces (see https://github.com/hashicorp/design-system/pull/231#issuecomment-1123502499) }}
+{{! NOTICE: we can't support the direct use of the "href" HTML attribute via ...attributes in the elements, because we need to rely on the "@href" Ember argument to differentiate between different types of generated output }}
+{{#if @route}}
+ {{#if this.isRouteExternal}}
+ {{yield}}
+ {{else}}
+ {{yield}}
+ {{/if}}
+{{else if @href}}
+ {{#if this.isHrefExternal}}
+ {{yield}}
+ {{else}}
+ {{yield}}
+ {{/if}}
+{{else}}
+
+{{/if}}
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/interactive/index.js b/packages/components/addon/components/hds/interactive/index.js
new file mode 100644
index 00000000000..3b0ae362fbb
--- /dev/null
+++ b/packages/components/addon/components/hds/interactive/index.js
@@ -0,0 +1,33 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+
+export default class HdsInteractiveComponent extends Component {
+ /**
+ * Determines if a @href value is "external" (it adds target="_blank" rel="noopener noreferrer")
+ *
+ * @param isHrefExternal
+ * @type boolean
+ * @default true
+ */
+ get isHrefExternal() {
+ return this.args.isHrefExternal ?? true;
+ }
+
+ /**
+ * Determines if a @route value is "external" (uses the LinkToExternal component instead of LinkTo)
+ *
+ * @param isRouteExternal
+ * @type boolean
+ * @default false
+ */
+ get isRouteExternal() {
+ return this.args.isRouteExternal ?? false;
+ }
+
+ @action
+ onKeyUp(event) {
+ if (event.key === ' ' || event.code === 'Space') {
+ event.target.click();
+ }
+ }
+}
diff --git a/packages/components/addon/components/hds/link-to/cta.hbs b/packages/components/addon/components/hds/link-to/cta.hbs
deleted file mode 100644
index a47743d34d5..00000000000
--- a/packages/components/addon/components/hds/link-to/cta.hbs
+++ /dev/null
@@ -1,51 +0,0 @@
-{{!
-// ββ ββ βββββ ββββββ βββ ββ ββ βββ ββ ββββββ
-// ββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββ ββ
-// ββ β ββ βββββββ ββββββ ββ ββ ββ ββ ββ ββ ββ ββ βββ
-// ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ
-// βββ βββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββββββ
-//
-// Notice: in this component we're using directly the styles from the `Hds::Button` component
-// using the `hds-button` class names (and adding a specialized class for the "cta", see below)
-// If you need to change the styling of the `Button` component, remember that this will impact also
-// this component too.
-// If instead you need to change only the styling of the `CTA` component, you can do it
-// in the CSS file using the specialized class declared there.
-// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
-// the least worst option we could find to solve the problem of sharing the exact same style of the
-// `Button (primary)` with other components.
-}}
-
-
- {{#if this.icon}}
- {{#if (eq this.iconPosition "leading")}}
-
-
-
-
- {{this.text}}
-
- {{else}}
-
- {{this.text}}
-
-
-
-
- {{/if}}
- {{else}}
-
- {{this.text}}
-
- {{/if}}
-
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/link-to/cta.js b/packages/components/addon/components/hds/link-to/cta.js
deleted file mode 100644
index a9ed2ad5948..00000000000
--- a/packages/components/addon/components/hds/link-to/cta.js
+++ /dev/null
@@ -1,165 +0,0 @@
-// ββ ββ βββββ ββββββ βββ ββ ββ βββ ββ ββββββ
-// ββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββ ββ
-// ββ β ββ βββββββ ββββββ ββ ββ ββ ββ ββ ββ ββ ββ βββ
-// ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ
-// βββ βββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββββββ
-//
-// Notice: in this component we're using directly the styles from the `Hds::Button` component
-// using the `hds-button` class names (and adding a specialized class for the "cta", see below)
-// If you need to change the styling of the `Button` component, remember that this will impact also
-// this component too.
-// If instead you need to change only the styling of the `CTA` component, you can do it
-// in the CSS file using the specialized class declared there.
-// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
-// the least worst option we could find to solve the problem of sharing the exact same style of the
-// `Button (primary)` with other components.
-
-import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
-import { action } from '@ember/object';
-
-export const DEFAULT_SIZE = 'medium';
-export const DEFAULT_ICONPOSITION = 'leading';
-export const SIZES = ['small', 'medium', 'large'];
-export const ICONPOSITIONS = ['leading', 'trailing'];
-
-export default class HdsLinkToCtaComponent extends Component {
- /**
- * @param text
- * @type {string}
- * @description The text of the component. If no text value is defined an error will be thrown.
- */
- get text() {
- let { text } = this.args;
-
- assert(
- '@text for "Hds::LinkTo::Cta" must have a valid value',
- text !== undefined
- );
-
- return text;
- }
-
- /**
- * @param size
- * @type {string}
- * @default medium
- * @description The size of the component; acceptable values are `small`, `medium`, and `large`
- */
- get size() {
- let { size = DEFAULT_SIZE } = this.args;
-
- assert(
- `@size for "Hds::LinkTo::Cta" must be one of the following: ${SIZES.join(
- ', '
- )}; received: ${size}`,
- SIZES.includes(size)
- );
-
- return size;
- }
-
- /**
- * @param icon
- * @type {string}
- * @default null
- * @description The name of the icon to be used.
- */
- get icon() {
- return this.args.icon ?? null;
- }
-
- /**
- * @param iconPosition
- * @type {string}
- * @default leading
- * @description Positions the icon before or after the text; allowed values are `leading` or `trailing`
- */
- get iconPosition() {
- let { iconPosition = DEFAULT_ICONPOSITION } = this.args;
-
- assert(
- `@iconPosition for "Hds::LinkTo::Cta" must be one of the following: ${ICONPOSITIONS.join(
- ', '
- )}; received: ${iconPosition}`,
- ICONPOSITIONS.includes(iconPosition)
- );
-
- return iconPosition;
- }
-
- /**
- * @param iconSize
- * @type {string}
- * @default 16
- * @description ensures that the correct icon size is used. Automatically calculated.
- */
- get iconSize() {
- if (this.args.size === 'large') {
- return '24';
- } else {
- return '16';
- }
- }
-
- /**
- * @param isFullWidth
- * @type {boolean}
- * @default false
- * @description Indicates that the component should take up the full width of the parent container. The default is false.
- */
- get isFullWidth() {
- return this.args.isFullWidth ?? false;
- }
-
- /**
- * @param route
- * @type {string|null}
- * @description Checks to make sure route is defined.
- */
- get route() {
- let { route } = this.args;
- assert(
- '@route must be defined for "Hds::LinkTo::Cta"',
- route !== undefined
- );
-
- return route;
- }
-
- /**
- * Get the class names to apply to the component.
- * @method classNames
- * @return {string} The "class" attribute to apply to the component.
- */
- get classNames() {
- let classes = [
- 'hds-button',
- 'hds-button--color-primary',
- 'hds-link-cta--inherit-button-styles',
- ];
-
- // add a class based on the @size argument
- classes.push(`hds-button--size-${this.size}`);
-
- // add a class based on the @isFullWidth argument
- if (this.isFullWidth) {
- classes.push('hds-button--width-full');
- }
-
- return classes.join(' ');
- }
-
- @action
- didInsert(el) {
- // we need to register the element to compare it with the one that triggered the "key/space" event
- this.el = el;
- }
-
- @action
- onKeySpace(event) {
- if (event.target === this.el) {
- event.target.click();
- }
- }
-}
diff --git a/packages/components/addon/components/hds/link-to/standalone.hbs b/packages/components/addon/components/hds/link-to/standalone.hbs
deleted file mode 100644
index 862b19fcbd9..00000000000
--- a/packages/components/addon/components/hds/link-to/standalone.hbs
+++ /dev/null
@@ -1,25 +0,0 @@
-
- {{#if (eq this.iconPosition "leading")}}
-
-
-
-
- {{this.text}}
-
- {{else}}
-
- {{this.text}}
-
-
-
-
- {{/if}}
-
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/link-to/standalone.js b/packages/components/addon/components/hds/link-to/standalone.js
deleted file mode 100644
index d7238b9e647..00000000000
--- a/packages/components/addon/components/hds/link-to/standalone.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
-
-export const DEFAULT_ICONPOSITION = 'leading';
-export const DEFAULT_COLOR = 'primary';
-export const DEFAULT_SIZE = 'medium';
-export const ICONPOSITIONS = ['leading', 'trailing'];
-export const COLORS = ['primary', 'secondary'];
-export const SIZES = ['small', 'medium', 'large'];
-
-export default class HdsLinkToStandaloneComponent extends Component {
- /**
- * @param text
- * @type {string}
- * @description The text of the link. If no text value is defined an error will be thrown.
- */
- get text() {
- let { text } = this.args;
-
- assert(
- '@text for "Hds::LinkTo::Standalone" must have a valid value',
- text !== undefined
- );
-
- return text;
- }
-
- /**
- * @param route
- * @type {string|null}
- * @description Checks to make sure route is defined.
- */
- get route() {
- let { route } = this.args;
- assert(
- '@route must be defined for "Hds::LinkTo::Standalone"',
- route !== undefined
- );
-
- return route;
- }
-
- /**
- * @param color
- * @type {string}
- * @default primary
- * @description Determines the color of link to be used; acceptable values are `primary` and `secondary`
- */
- get color() {
- let { color = DEFAULT_COLOR } = this.args;
-
- assert(
- `@color for "Hds::LinkTo::Standalone" must be one of the following: ${COLORS.join(
- ', '
- )}; received: ${color}`,
- COLORS.includes(color)
- );
-
- return color;
- }
-
- /**
- * @param icon
- * @type {string|null}
- * @default null
- * @description The name of the icon to be used. An icon name must be defined.
- */
- get icon() {
- let { icon } = this.args;
-
- assert(
- '@icon for "Hds::LinkTo::Standalone" must have a valid value',
- icon !== undefined
- );
-
- return icon;
- }
-
- /**
- * @param iconPosition
- * @type {string}
- * @default leading
- * @description Positions the icon before or after the text; allowed values are `leading` or `trailing`
- */
- get iconPosition() {
- let { iconPosition = DEFAULT_ICONPOSITION } = this.args;
-
- assert(
- `@iconPosition for "Hds::LinkTo::Standalone" must be one of the following: ${ICONPOSITIONS.join(
- ', '
- )}; received: ${iconPosition}`,
- ICONPOSITIONS.includes(iconPosition)
- );
-
- return iconPosition;
- }
-
- /**
- * @param size
- * @type {string}
- * @default medium
- * @description The size of the standalone link; acceptable values are `small`, `medium`, and `large`
- */
- get size() {
- let { size = DEFAULT_SIZE } = this.args;
-
- assert(
- `@size for "Hds::LinkTo::Standalone" must be one of the following: ${SIZES.join(
- ', '
- )}; received: ${size}`,
- SIZES.includes(size)
- );
-
- return size;
- }
-
- /**
- * @param iconSize
- * @type {string}
- * @default 16
- * @description ensures that the correct icon size is used. Automatically calculated.
- */
- get iconSize() {
- if (this.args.size === 'large') {
- return '24';
- } else {
- return '16';
- }
- }
-
- /**
- * Get the class names to apply to the component.
- * @method LinkToStandalone#classNames
- * @return {string} The "class" attribute to apply to the component.
- */
- get classNames() {
- // Notice: we've left this class name the same as the hds::link::standalone (we didn't add the "-to") so we didn't have to replicate the CSS
- let classes = ['hds-link-standalone'];
-
- // add a class based on the @size argument
- classes.push(`hds-link-standalone--size-${this.size}`);
-
- // add a class based on the @color argument
- classes.push(`hds-link-standalone--color-${this.color}`);
-
- // add a class based on the @iconPosition argument
- classes.push(`hds-link-standalone--icon-position-${this.iconPosition}`);
-
- return classes.join(' ');
- }
-}
diff --git a/packages/components/addon/components/hds/link/cta.hbs b/packages/components/addon/components/hds/link/cta.hbs
deleted file mode 100644
index 5959fe9ea4f..00000000000
--- a/packages/components/addon/components/hds/link/cta.hbs
+++ /dev/null
@@ -1,50 +0,0 @@
-{{!
-// ββ ββ βββββ ββββββ βββ ββ ββ βββ ββ ββββββ
-// ββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββ ββ
-// ββ β ββ βββββββ ββββββ ββ ββ ββ ββ ββ ββ ββ ββ βββ
-// ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ
-// βββ βββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββββββ
-//
-// Notice: in this component we're using directly the styles from the `Hds::Button` component
-// using the `hds-button` class names (and adding a specialized class for the "cta", see below)
-// If you need to change the styling of the `Button` component, remember that this will impact also
-// this component too.
-// If instead you need to change only the styling of the `CTA` component, you can do it
-// in the CSS file using the specialized class declared there.
-// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
-// the least worst option we could find to solve the problem of sharing the exact same style of the
-// `Button (primary)` with other components.
-}}
-
-{{! template-lint-disable link-href-attributes }}
-{{! we can disable this linting rule because the developer will add the html attribute themselves }}
-
- {{#if this.icon}}
- {{#if (eq this.iconPosition "leading")}}
-
- {{/if}}
-
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/link/cta.js b/packages/components/addon/components/hds/link/cta.js
deleted file mode 100644
index a1370e2c37b..00000000000
--- a/packages/components/addon/components/hds/link/cta.js
+++ /dev/null
@@ -1,150 +0,0 @@
-// ββ ββ βββββ ββββββ βββ ββ ββ βββ ββ ββββββ
-// ββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββ ββ
-// ββ β ββ βββββββ ββββββ ββ ββ ββ ββ ββ ββ ββ ββ βββ
-// ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ ββ
-// βββ βββ ββ ββ ββ ββ ββ ββββ ββ ββ ββββ ββββββ
-//
-// Notice: in this component we're using directly the styles from the `Hds::Button` component
-// using the `hds-button` class names (and adding a specialized class for the "cta", see below)
-// If you need to change the styling of the `Button` component, remember that this will impact also
-// this component too.
-// If instead you need to change only the styling of the `CTA` component, you can do it
-// in the CSS file using the specialized class declared there.
-// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
-// the least worst option we could find to solve the problem of sharing the exact same style of the
-// `Button (primary)` with other components.
-
-import Component from '@glimmer/component';
-import { assert } from '@ember/debug';
-import { action } from '@ember/object';
-
-export const DEFAULT_SIZE = 'medium';
-export const DEFAULT_ICONPOSITION = 'leading';
-export const SIZES = ['small', 'medium', 'large'];
-export const ICONPOSITIONS = ['leading', 'trailing'];
-
-export default class HdsLinkCtaComponent extends Component {
- /**
- * @param text
- * @type {string}
- * @description The text of the component. If no text value is defined an error will be thrown.
- */
- get text() {
- let { text } = this.args;
-
- assert(
- '@text for "Hds::Link::Cta" must have a valid value',
- text !== undefined
- );
-
- return text;
- }
-
- /**
- * @param size
- * @type {string}
- * @default medium
- * @description The size of the component; acceptable values are `small`, `medium`, and `large`
- */
- get size() {
- let { size = DEFAULT_SIZE } = this.args;
-
- assert(
- `@size for "Hds::Link::Cta" must be one of the following: ${SIZES.join(
- ', '
- )}; received: ${size}`,
- SIZES.includes(size)
- );
-
- return size;
- }
-
- /**
- * @param icon
- * @type {string}
- * @default null
- * @description The name of the icon to be used.
- */
- get icon() {
- return this.args.icon ?? null;
- }
-
- /**
- * @param iconPosition
- * @type {string}
- * @default leading
- * @description Positions the icon before or after the text; allowed values are `leading` or `trailing`
- */
- get iconPosition() {
- let { iconPosition = DEFAULT_ICONPOSITION } = this.args;
-
- assert(
- `@iconPosition for "Hds::Link::Cta" must be one of the following: ${ICONPOSITIONS.join(
- ', '
- )}; received: ${iconPosition}`,
- ICONPOSITIONS.includes(iconPosition)
- );
-
- return iconPosition;
- }
-
- /**
- * @param iconSize
- * @type {string}
- * @default 16
- * @description ensures that the correct icon size is used. Automatically calculated.
- */
- get iconSize() {
- if (this.args.size === 'large') {
- return '24';
- } else {
- return '16';
- }
- }
-
- /**
- * @param isFullWidth
- * @type {boolean}
- * @default false
- * @description Indicates that the component should take up the full width of the parent container. The default is false.
- */
- get isFullWidth() {
- return this.args.isFullWidth ?? false;
- }
-
- /**
- * Get the class names to apply to the component.
- * @method classNames
- * @return {string} The "class" attribute to apply to the component.
- */
- get classNames() {
- let classes = [
- 'hds-button',
- 'hds-button--color-primary',
- 'hds-link-cta--inherit-button-styles',
- ];
-
- // add a class based on the @size argument
- classes.push(`hds-button--size-${this.size}`);
-
- // add a class based on the @isFullWidth argument
- if (this.isFullWidth) {
- classes.push('hds-button--width-full');
- }
-
- return classes.join(' ');
- }
-
- @action
- didInsert(el) {
- // we need to register the element to compare it with the one that triggered the "key/space" event
- this.el = el;
- }
-
- @action
- onKeySpace(event) {
- if (event.target === this.el) {
- event.target.click();
- }
- }
-}
diff --git a/packages/components/addon/components/hds/link/inline.hbs b/packages/components/addon/components/hds/link/inline.hbs
new file mode 100644
index 00000000000..e641bbd62f0
--- /dev/null
+++ b/packages/components/addon/components/hds/link/inline.hbs
@@ -0,0 +1,23 @@
+{{! IMPORTANT: we removed the newlines before/after the yield to reduce the issues with unexpected whitespaces (see https://github.com/hashicorp/design-system/pull/231#issuecomment-1123502499) }}
+{{! 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 }}
+{{#if (and @icon (eq this.iconPosition "leading"))~}}
+
+
+
+ {{~/if~}}{{yield}}{{~#if (and @icon (eq this.iconPosition "trailing"))~}}
+
+
+
+ {{~/if}}
\ No newline at end of file
diff --git a/packages/components/addon/components/hds/link/inline.js b/packages/components/addon/components/hds/link/inline.js
new file mode 100644
index 00000000000..31af27dcbb3
--- /dev/null
+++ b/packages/components/addon/components/hds/link/inline.js
@@ -0,0 +1,71 @@
+import Component from '@glimmer/component';
+import { assert } from '@ember/debug';
+
+export const DEFAULT_ICONPOSITION = 'trailing';
+export const DEFAULT_COLOR = 'primary';
+export const ICONPOSITIONS = ['leading', 'trailing'];
+export const COLORS = ['primary', 'secondary'];
+
+export default class HdsLinkInlineComponent extends Component {
+ constructor() {
+ super(...arguments);
+ if (!(this.args.href || this.args.route)) {
+ assert('@href or @route must be defined for ');
+ }
+ }
+
+ /**
+ * @param color
+ * @type {string}
+ * @default primary
+ * @description Determines the color of link to be used; acceptable values are `primary` and `secondary`
+ */
+ get color() {
+ let { color = DEFAULT_COLOR } = this.args;
+
+ assert(
+ `@color for "Hds::Link::Inline" must be one of the following: ${COLORS.join(
+ ', '
+ )}; received: ${color}`,
+ COLORS.includes(color)
+ );
+
+ return color;
+ }
+
+ /**
+ * @param iconPosition
+ * @type {string}
+ * @default leading
+ * @description Positions the icon before or after the text; allowed values are `leading` or `trailing`
+ */
+ get iconPosition() {
+ let { iconPosition = DEFAULT_ICONPOSITION } = this.args;
+
+ assert(
+ `@iconPosition for "Hds::Link::Inline" must be one of the following: ${ICONPOSITIONS.join(
+ ', '
+ )}; received: ${iconPosition}`,
+ ICONPOSITIONS.includes(iconPosition)
+ );
+
+ return iconPosition;
+ }
+
+ /**
+ * Get the class names to apply to the component.
+ * @method LinkInline#classNames
+ * @return {string} The "class" attribute to apply to the component.
+ */
+ get classNames() {
+ let classes = ['hds-link-inline'];
+
+ // add a class based on the @color argument
+ classes.push(`hds-link-inline--color-${this.color}`);
+
+ // add a class based on the @iconPosition argument
+ classes.push(`hds-link-inline--icon-${this.iconPosition}`);
+
+ return classes.join(' ');
+ }
+}
diff --git a/packages/components/addon/components/hds/link/standalone.hbs b/packages/components/addon/components/hds/link/standalone.hbs
index be624cc8353..d051d617af0 100644
--- a/packages/components/addon/components/hds/link/standalone.hbs
+++ b/packages/components/addon/components/hds/link/standalone.hbs
@@ -1,6 +1,17 @@
-{{! template-lint-disable link-href-attributes }}
-{{! we can disable this linting rule because the developer will add the html attribute themselves }}
-
+
{{#if (eq this.iconPosition "leading")}}
This component has been designed and implemented with accessibility in mind. When used as
+ recommended, there should not be any WCAG conformance issues with this component.
+
+
+ Applicable WCAG Success Criteria (Reference)
+
+
+ This section is for reference only. This component intends to conform to the following WCAG success criteria:
+
The LinkTo::CTA component handles the generation of an
- Ember LinkTo
- component with the same exact visual appearence as the "primary"
- Button
- component, that points to an internal application link or resource.
-
-
- What do we mean by the name
- Link "CTA"? It's a link that stands out from the rest of the page, and tells the user βthis is the
- most important action on this pageβ. As such, it visually presents like a button.
-
- There are three sizes available for the component:
- small,
- medium
- and
- large. The default is
- medium. To use a different size, declare a value for
- @size:
-
- This components uses the
- Ember LinkTo
- component so you need to pass all the necessary routing arguments to it (@route,
- @models,
- @query, etc).
-
The LinkTo (Standalone) component handles generation of an
- Ember LinkTo, that points to an internal application link or resource.
-
-
- What do we mean by the name
- LinkTo (Standalone)? It's a LinkTo that's used in isolation, meaning it's not inline with adjacent
- text.
- Note: Solutions for LinkTo (Inline) are coming soon.
-
-
-
Example of LinkTo (Standalone):
-
-
Here's a completely separate paragraph of text. Lorem ipsum 123.
-
-
-
-
-
Example of LinkTo (Inline):
-
-
-
Here's a
- LinkTo (Inline), coming soon to HDS.
- And another LinkTo (Inline)
- There are two available colors for a LinkTo (Standalone):
- primary
- and
- secondary. The default is
- primary. To use a different color, declare another value for
- @color:
-
- This components uses the
- Ember LinkTo
- component so you need to pass all the necessary routing arguments to it (@route,
- @models,
- @query, etc).
-
The Link::CTA component handles the generation of an HTML
-
- <a>
- anchor element with the same exact visual appearence as the "primary"
- Button
- component, that points to an external URL.
-
-
- What do we mean by the name
- Link "CTA"? It's a link that stands out from the rest of the page, and tells the user βthis is the
- most important action on this pageβ. As such, it visually presents like a button.
-
- There are three sizes available for the component:
- small,
- medium
- and
- large. The default is
- medium. To use a different size, declare a value for
- @size:
-
- For the component to work as expected, a
- href
- value needs to be passed as attribute. The component automatically adds
- target="_blank" rel="noopener noreferrer"
- to the
- <a>
- element (but their values can be overwritten passing those attributes to the component).
-
- {{#each @model.SIZES as |size|}}
-
- {{/each}}
-
-
-
-
-
-
States (in each size)
-
- {{#each @model.SIZES as |size|}}
- {{#each @model.STATES as |state|}}
-
- {{capitalize size}}/{{state}}:
-
-
-
- {{/each}}
- {{/each}}
- {{#each @model.STATES as |state|}}
-
- Full width/{{state}}:
-
-
-
- {{/each}}
-
-
\ No newline at end of file
diff --git a/packages/components/tests/dummy/app/templates/components/link/inline.hbs b/packages/components/tests/dummy/app/templates/components/link/inline.hbs
new file mode 100644
index 00000000000..00f1bcbcba2
--- /dev/null
+++ b/packages/components/tests/dummy/app/templates/components/link/inline.hbs
@@ -0,0 +1,468 @@
+{{page-title "Link::Inline component"}}
+
+
+ Link::Inline
+
+
+{{! }}
+
+
+
+
π Notice π
+
In all the examples below we apply
+ .hds-typography
+ styles to the
+ Link::Inline
+ text for presentation purpose.
+ In reality the only CSS styling applied to the component is to handle the two available colors (see below). All
+ the other properties, especially the font styling, are inherited from the parent container, mimicking the default
+ behavior of the
+ <a>
+ HTML element
Elements passed as children are yielded to the content of the
+ <a>
+ HTML element.
+
+
icon string
+
+
Use this parameter to show an icon. Acceptable value: any Flight icon name.
+
+
iconPosition enum
+
+
Positions the icon before or after the text. Acceptable values:
+
+
leading
+
trailing
+
+
+
href
+
+
This is the URL parameter that is passed down to the
+ <a>
+ element.
+
+
isHrefExternal boolean
+
+
Default: true
+
This controls if the
+ <a>
+ link is external and so for security reasons we need to add the
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ attributes to it.
+
+
route/models/model/query/current-when/replace
+
+
These are the parameters that are passed down as arguments to the
+ <LinkTo/LinkToExternal>
+ component. For more details about these parameters see the
+ Ember documentation
+ or the
+ LinkTo component API specs.
+
+
isRouteExternal boolean
+
+
Default: false
+
This controls if the "LinkTo" is external to the Ember engine (more details here) in which case it will use a
+ <LinkToExternal>
+ instead of a simple
+ <LinkTo>
+ for the @route.
+
+
...attributes
+
+
...attributes spreading is supported on this component.
The most basic invocation requires some content to be passed as children and one of the two
+ @href
+ or
+ @route
+ arguments (for details on how URLs and routing are handled see below).
+ {{! prettier-ignore-start }}
+ Watch tutorial video
+ '
+ />
+ {{! prettier-ignore-end }}
+
Renders to:
+
+ Watch tutorial video
+
+
+
Add an icon
+
To add an icon to your inline link, give the
+ @icon
+ a
+ Flight icon
+ name:
+
Notice: since the
+ Hds::Link::Inline
+ doesn't have an intrinsic size, the size of the icon is calculated proportionally (via
+ em) in relation to the font-size of the text
+ .
+
+
Icon position
+
By default, if you define an icon, it is placed after the text. If you would like to
+ position the icon before the text, define
+ @iconPosition:
+
+ There are two available colors for a Link (Inline):
+ primary
+ and
+ secondary. The default is
+ primary. To use a different color, declare another value for
+ @color:
+
You can generate an inline link passing a
+ @href
+ or a
+ @route
+ to the component. If none of the two is provided, the component will throw an error.
+
Notice: the
+ Link::Inline
+ component internally uses the generic
+ Hds::Interactive
+ component. For more details about how this low-level component works please refer to
+ its documentation page.
+
With @href
+
If you pass a
+ @href
+ argument a
+ <a>
+ link will be generated:
β οΈ
+ Important: when using the
+ @href
+ argument the component adds by default the attributes
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ to the
+ <a>
+ element (because this is the most common use case: internal links are generally handled using a
+ @route
+ argument). If the
+ href
+ points to an internal link, or uses a different protocol (eg. "mailto" of "ftp") you can pass
+ @isHrefExternal={{true}}
+ to the component and it will not add the
+ target
+ and
+ rel
+ attributes (but you can pass yours if needed, using the
+ ...attributes
+ spreading. For more details see the
+ Hds::Interactive component.
+
+
With @route
+
If you pass a
+ @route
+ argument a
+ <a>
+ link will be generated using a
+ <LinkTo>
+ Ember component:
+ {{! prettier-ignore-start }}
+ Go to the index page
+ '
+ />
+ {{! prettier-ignore-end }}
+
Renders to:
+
+ Go to the index page
+
+
β οΈ
+ Important: if the route is external to your current engine you have to pass also
+ @isRouteExternal={{true}}
+ to the component so that it will use
+ <LinkToExternal>
+ instead of a simple
+ <LinkTo>
+ for the
+ @route. For more details see the
+ Hds::Interactive component
+
Notice: all the standard arguments for the
+ <LinkTo/LinkToExternal>
+ components are supported (eg.
+ models/model/query/current-when/replace).
The Link::Standalone component handles the generation of an HTML anchor element
-
- <a>
- that points to an external URL.
-
+
The
+ Link::Standalone
+ component handles the generation of:
+
+
an HTML anchor element
+ <a>
+ that points to an external URL (when using a
+ @href
+ argument)
+
an
+ Ember component
+ <LinkTo>
+ that points to an internal application link or resource (when using a
+ @route
+ argument)
+
+
+
Why it's called "standalone"
- What do we mean by the name
- Link "Standalone"? It's a link that's used in isolation, meaning it's not inline with adjacent
- text.
- Note: Solutions for Link (Inline) are coming soon.
+ A
+ "Link Standalone"
+ is a link that's used in isolation, meaning it's not inline with adjacent text.
+
@@ -47,12 +62,7 @@
Here is the API for the component:
-
- size
-
- enum
-
-
+
size enum
Acceptable values:
@@ -84,21 +94,10 @@
-
- The text of the link (standalone).
-
-
-
- If no text value is defined an error will be thrown.
-
-
+
The text of the link.
+
If no text value is defined an error will be thrown.
-
- icon
-
- string
-
-
+
icon string
Use this parameter to show an icon. Acceptable value: any Flight icon name.
@@ -108,30 +107,49 @@
icon
is required to make the component accessible.
-
- iconPosition
-
- enum
-
-
+
iconPosition enum
-
- Positions the icon before or after the text. Acceptable values:
-
+
Positions the icon before or after the text. Acceptable values:
-
- leading
-
-
- trailing
-
+
leading
+
trailing
href
-
This is the URL that is passed down ("splatted") as arguments to the
+
This is the URL parameter that is passed down to the
<a>
- element (see URL handling for details).
+ element.
+
+
isHrefExternal boolean
+
+
Default: true
+
This controls if the
+ <a>
+ link is external and so for security reasons we need to add the
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ attributes to it.
+
+
route/models/model/query/current-when/replace
+
+
These are the parameters that are passed down as arguments to the
+ <LinkTo/LinkToExternal>
+ component.
+
+
isRouteExternal boolean
+
+
Default: false
+
This controls if the "LinkTo" is external to the Ember engine (more details here) in which case it will use a
+ <LinkToExternal>
+ instead of a simple
+ <LinkTo>
+ for the @route.
...attributes
@@ -148,19 +166,21 @@
icon
and
text
- to be passed and an
- href
- value (for details on how URLs are handled see below).
+ to be passed and one of the two
+ @href
+ or
+ @route
+ arguments (for details on how URLs and routing are handled see below).
{{! prettier-ignore-start }}
+
'
/>
{{! prettier-ignore-end }}
Renders to:
-
+
Icon position
By default the icon is placed before the text. If you would like to position it after the
@@ -171,12 +191,12 @@
+
'
/>
{{! prettier-ignore-end }}
- For the component to work as expected, a
- href
- value needs to be passed as attribute. The component automatically adds
- target="_blank" rel="noopener noreferrer"
- to the
+
URLs and routes handling
+
You can generate a standalone link passing a
+ @href
+ or a
+ @route
+ to the component. If none of the two is provided, the component will throw an error.
+
Notice: the
+ Link::Standalone
+ component internally uses the generic
+ Hds::Interactive
+ component. For more details about how this low-level component works please refer to
+ its documentation page.
+
With @href
+
If you pass a
+ @href
+ argument a
<a>
- element (but their values can be overwritten passing those attributes to the component).
-
+ link will be generated:
{{! prettier-ignore-start }}
+
'
/>
{{! prettier-ignore-end }}
@@ -247,8 +275,64 @@
+
β οΈ
+ Important: when using the
+ @href
+ argument the component adds by default the attributes
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ to the
+ <a>
+ element (because this is the most common use case: internal links are generally handled using a
+ @route
+ argument). If the
+ href
+ points to an internal link, or uses a different protocol (eg. "mailto" of "ftp") you can pass
+ @isHrefExternal={{true}}
+ to the component and it will not add the
+ target
+ and
+ rel
+ attributes (but you can pass yours if needed, using the
+ ...attributes
+ spreading. For more details see the
+ Hds::Interactive component.
+
+
With @route
+
If you pass a
+ @route
+ argument a
+ <a>
+ link will be generated using a
+ <LinkTo>
+ Ember component:
β οΈ
+ Important: if the route is external to your current engine you have to pass also
+ @isRouteExternal={{true}}
+ to the component so that it will use
+ <LinkToExternal>
+ instead of a simple
+ <LinkTo>
+ for the
+ @route. For more details see the
+ Hds::Interactive component
+
Notice: all the standard arguments for the
+ <LinkTo/LinkToExternal>
+ components are supported (eg.
+ models/model/query/current-when/replace).
Actions can optionally be passed into the component using one of the suggested
- Button,
- Link::Standalone
+ Button
or
- LinkTo::Standalone
+ Link::Standalone
yielded components.
{{! prettier-ignore-start }}
{{! template-lint-disable no-unbalanced-curlies }}
@@ -225,7 +224,7 @@
Title hereDescription here
-
+
'
/>
@@ -236,7 +235,7 @@
Title hereDescription here
-
+
Structured content
@@ -566,7 +565,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
+
\ No newline at end of file
diff --git a/packages/components/tests/dummy/app/templates/index.hbs b/packages/components/tests/dummy/app/templates/index.hbs
index e83b6d3dd63..b750850f40a 100644
--- a/packages/components/tests/dummy/app/templates/index.hbs
+++ b/packages/components/tests/dummy/app/templates/index.hbs
@@ -69,13 +69,8 @@
π§
- Notice: this component is intended for internal use for now. It may be that in the future it will exposed as
- official component of the design system but not for now. Please if you need to use it speak with the HDS team.
+
+
+
+
β οΈ
+ Notice: this component is intended
+ only
+ for internal use (for now). If you need to use it please speak with the HDS team.
This is the URL parameter that is passed down to the
+ <a>
+ element.
+
+
isHrefExternal boolean
+
+
Default: true
+
This controls if the
+ <a>
+ link is external and so for security reasons we need to add the
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ attributes to it.
+
+
route/models/model/query/current-when/replace
+
+
These are the parameters that are passed down as arguments to the
+ <LinkTo>
+ /
+ <LinkToExternal>
+ components.
+
+
isRouteExternal boolean
+
+
Default: false
+
This controls if the "LinkTo" is external to the Ember engine (more details here) in which case it will use a
+ <LinkToExternal>
+ instead of a simple
+ <LinkTo>
+ for the @route.
+
+
...attributes
+
+
...attributes spreading is supported on this component.
Invocation of the component would look something like this:
+ {{! prettier-ignore-start }}
+
+ your content here (will be yielded)
+
+ "
+ />
+ {{! prettier-ignore-end }}
+
In this case, since no
+ @href
+ or
+ @route
+ argument is provided it will generate in output an HTML
+ <button>
+ element.
+
+
Notice: a
+ type="button"
+ HTML attribute is applied by default to the element, but this can be overwritten using the "splattributes".
+
+
With "@href" parameter (<a>)
+
π¨ ATTENTION: we can't support the direct use of the
+ href
+ HTML attribute because we need to rely on the
+ @href
+ Ember argument to differentiate between different types of generated output.
+
If an @href argument is provided:
+ {{! prettier-ignore-start }}
+
+ your content here
+
+ '
+ />
+ {{! prettier-ignore-end }}
+
it will generate in output an HTML
+ <a>
+ link element with
+ target="_blank"
+ and
+ rel="noopener noreferrer"
+ attributes.
+
+
We add these attributes by default because this is the most common case (internal links
+ are generally handled using a
+ @route
+ argument). This behavior can be overridden (see below).
+
+
If an
+ @isHrefExternal
+ argument is provided with
+ false
+ value:
and it will generate in output a
+ <LinkToExternal>
+ component.
+
+
+
Notice: all the standard arguments for the
+ <LinkTo/LinkToExternal>
+ components are supported (eg.
+ models,
+ model,
+ query,
+ current-when,
+ replace). For more details about these parameters see the
+ Ember documentation
+ or the
+ LinkTo component API specs.
+ <button>
+ (with no @route or @href provided / default)
+
+ This is a button (default)
+
+
+
+
+ <a>
+ (with @href argument)
+
+ This is an external
+ <a>
+ link (default)
+
+ This is an internal
+ <a>
+ link
+
+
+
+
+ <LinkTo>
+ (with @route argument but no @isRouteExternal)
+
+ This is a <LinkTo> link
+
+
+
+
+ <LinkToExternal>
+ (with @route argument and @isRouteExternal set to true)
+
+ {{!-- This is a <LinkToExternal> link --}}
+
β οΈ We can't render this component in this application (it will work only on Ember engines).
+
+
+
\ No newline at end of file
diff --git a/packages/components/tests/dummy/public/assets/images/link-cta-design-usage.png b/packages/components/tests/dummy/public/assets/images/link-cta-design-usage.png
deleted file mode 100644
index 423258f8760..00000000000
Binary files a/packages/components/tests/dummy/public/assets/images/link-cta-design-usage.png and /dev/null differ
diff --git a/packages/components/tests/integration/components/hds/alert/index-test.js b/packages/components/tests/integration/components/hds/alert/index-test.js
index fd99f87af32..7b6d0bd3f34 100644
--- a/packages/components/tests/integration/components/hds/alert/index-test.js
+++ b/packages/components/tests/integration/components/hds/alert/index-test.js
@@ -101,7 +101,7 @@ module('Integration | Component | hds/alert/index', function (hooks) {
test('it should render an Hds::Link::Standalone component yielded to the "actions" container', async function (assert) {
assert.expect(5);
await render(
- hbs``
+ hbs``
);
assert
.dom('#test-alert .hds-alert__actions a')
diff --git a/packages/components/tests/integration/components/hds/button/index-test.js b/packages/components/tests/integration/components/hds/button/index-test.js
index 0bbaffa2f7c..61b8fb0cc59 100644
--- a/packages/components/tests/integration/components/hds/button/index-test.js
+++ b/packages/components/tests/integration/components/hds/button/index-test.js
@@ -10,9 +10,9 @@ module('Integration | Component | hds/button/index', function (hooks) {
resetOnerror();
});
- test('it renders a button with the defined text', async function (assert) {
+ test('it renders the button', async function (assert) {
await render(hbs``);
- assert.dom(this.element).hasText('Copy to clipboard');
+ assert.dom(this.element).exists();
});
test('it should render with a CSS class that matches the component name', async function (assert) {
await render(
@@ -91,6 +91,15 @@ module('Integration | Component | hds/button/index', function (hooks) {
.doesNotHaveAria('label', 'copy to clipboard');
});
+ // TEXT
+
+ test('it renders a button with the defined text', async function (assert) {
+ await render(
+ hbs``
+ );
+ assert.dom('#test-toggle-button').hasText('Copy to clipboard');
+ });
+
// A11Y
test('it should have aria-label on the button element if isIconOnly is set to true', async function (assert) {
@@ -99,6 +108,12 @@ module('Integration | Component | hds/button/index', function (hooks) {
);
assert.dom('#test-button').hasAria('label', 'copy to clipboard');
});
+ test('it should have "button" type by default', async function (assert) {
+ await render(
+ hbs``
+ );
+ assert.dom('#test-button').hasAttribute('type', 'button');
+ });
// OTHER
@@ -134,14 +149,14 @@ module('Integration | Component | hds/button/index', function (hooks) {
throw new Error(errorMessage);
});
});
- test('it should throw an assertion if an incorrect value for @type is provided', async function (assert) {
+ test('it should throw an assertion if an incorrect value for @color is provided', async function (assert) {
const errorMessage =
- '@type for "Hds::Button" must be one of the following: button, submit, reset; received: foo';
+ '@color for "Hds::Button" must be one of the following: primary, secondary, tertiary, critical; received: foo';
assert.expect(2);
setupOnerror(function (error) {
assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`);
});
- await render(hbs``);
+ await render(hbs``);
assert.throws(function () {
throw new Error(errorMessage);
});
diff --git a/packages/components/tests/integration/components/hds/interactive/index-test.js b/packages/components/tests/integration/components/hds/interactive/index-test.js
new file mode 100644
index 00000000000..ebb502d322b
--- /dev/null
+++ b/packages/components/tests/integration/components/hds/interactive/index-test.js
@@ -0,0 +1,140 @@
+import { module, test, skip } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, triggerKeyEvent } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import sinon from 'sinon';
+
+module('Integration | Component | hds/interactive/index', function (hooks) {
+ setupRenderingTest(hooks);
+
+ // notice: since this element can generate different HTML element, to make the tests even more solid, in the DOM selectors we prefix the #ID of the element with the tag name
+
+ test('it renders the interactive container', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.element).exists();
+ });
+
+ // GENERATED ELEMENTS
+
+ test('it should render a