@hashicorp/design-system-components 2.14.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG-FIGMA-COMPONENTS.md +177 -0
  2. package/CHANGELOG-FIGMA-FOUNDATIONS.md +2 -0
  3. package/CHANGELOG.md +368 -55
  4. package/README.md +1 -1
  5. package/addon/components/hds/accordion/item/index.hbs +16 -5
  6. package/addon/components/hds/application-state/body.hbs +2 -2
  7. package/addon/components/hds/application-state/header.hbs +4 -4
  8. package/addon/components/hds/button/{index.js → index.ts} +34 -4
  9. package/addon/components/hds/copy/button/index.hbs +1 -1
  10. package/addon/components/hds/copy/button/index.js +14 -2
  11. package/addon/components/hds/copy/snippet/index.hbs +3 -3
  12. package/addon/components/hds/copy/snippet/index.js +19 -2
  13. package/addon/components/hds/dropdown/index.js +0 -3
  14. package/addon/components/hds/dropdown/list-item/checkmark.hbs +9 -3
  15. package/addon/components/hds/dropdown/list-item/copy-item.hbs +8 -4
  16. package/addon/components/hds/dropdown/list-item/copy-item.js +13 -0
  17. package/addon/components/hds/dropdown/list-item/description.hbs +9 -2
  18. package/addon/components/hds/dropdown/list-item/description.js +0 -18
  19. package/addon/components/hds/dropdown/list-item/interactive.hbs +4 -4
  20. package/addon/components/hds/dropdown/list-item/radio.hbs +6 -1
  21. package/addon/components/hds/dropdown/list-item/title.hbs +9 -2
  22. package/addon/components/hds/dropdown/list-item/title.js +0 -18
  23. package/addon/components/hds/flyout/description.hbs +2 -2
  24. package/addon/components/hds/flyout/header.hbs +4 -4
  25. package/addon/components/hds/form/error/index.hbs +2 -2
  26. package/addon/components/hds/form/error/index.js +0 -3
  27. package/addon/components/hds/form/error/message.hbs +2 -2
  28. package/addon/components/hds/form/helper-text/index.hbs +10 -2
  29. package/addon/components/hds/form/helper-text/index.js +0 -3
  30. package/addon/components/hds/form/indicator/index.hbs +1 -1
  31. package/addon/components/hds/form/indicator/index.js +0 -3
  32. package/addon/components/hds/form/masked-input/base.hbs +8 -10
  33. package/addon/components/hds/form/masked-input/base.js +14 -14
  34. package/addon/components/hds/form/masked-input/field.hbs +3 -1
  35. package/addon/components/hds/form/radio-card/description.hbs +6 -1
  36. package/addon/components/hds/form/radio-card/group.hbs +1 -2
  37. package/addon/components/hds/form/radio-card/index.js +5 -33
  38. package/addon/components/hds/form/radio-card/label.hbs +7 -1
  39. package/addon/components/hds/form/text-input/base.js +5 -0
  40. package/addon/components/hds/form/text-input/field.hbs +23 -11
  41. package/addon/components/hds/form/text-input/field.js +59 -0
  42. package/addon/components/hds/form/visibility-toggle/index.hbs +8 -0
  43. package/addon/components/hds/interactive/{index.js → index.ts} +28 -3
  44. package/addon/components/hds/modal/header.hbs +4 -4
  45. package/addon/components/hds/page-header/description.hbs +7 -1
  46. package/addon/components/hds/page-header/subtitle.hbs +7 -1
  47. package/addon/components/hds/page-header/title.hbs +7 -1
  48. package/addon/components/hds/pagination/compact/index.js +0 -16
  49. package/addon/components/hds/pagination/info/index.hbs +2 -2
  50. package/addon/components/hds/pagination/nav/arrow.hbs +16 -4
  51. package/addon/components/hds/pagination/nav/arrow.js +0 -2
  52. package/addon/components/hds/pagination/nav/number.hbs +2 -1
  53. package/addon/components/hds/pagination/nav/number.js +1 -6
  54. package/addon/components/hds/side-nav/list/index.hbs +2 -2
  55. package/addon/components/hds/stepper/step/indicator.hbs +6 -1
  56. package/addon/components/hds/tag/index.hbs +2 -2
  57. package/addon/modifiers/hds-clipboard.js +163 -0
  58. package/addon/template-registry.ts +12 -0
  59. package/app/components/hds/form/visibility-toggle/index.js +6 -0
  60. package/app/modifiers/hds-clipboard.js +6 -0
  61. package/app/styles/components/button.scss +2 -0
  62. package/app/styles/components/copy/snippet.scss +22 -14
  63. package/app/styles/components/dropdown.scss +5 -8
  64. package/app/styles/components/flyout.scss +0 -2
  65. package/app/styles/components/form/group.scss +5 -0
  66. package/app/styles/components/form/index.scss +1 -0
  67. package/app/styles/components/form/masked-input.scss +0 -9
  68. package/app/styles/components/form/radio-card.scss +2 -4
  69. package/app/styles/components/form/text-input.scss +17 -0
  70. package/app/styles/components/form/visibility-toggle.scss +23 -0
  71. package/app/styles/components/stepper/step-indicator.scss +0 -3
  72. package/app/styles/components/tag.scss +1 -5
  73. package/app/styles/mixins/_button.scss +14 -4
  74. package/index.js +3 -0
  75. package/package.json +33 -4
  76. package/tsconfig.json +46 -0
  77. package/types/dummy/index.d.ts +6 -0
  78. package/types/global.d.ts +12 -0
@@ -12,10 +12,8 @@ import { setAriaDescribedBy } from '@hashicorp/design-system-components/utils/hd
12
12
 
13
13
  export const DEFAULT_CONTROL_POSITION = 'bottom';
14
14
  export const DEFAULT_ALIGNMENT = 'left';
15
- export const DEFAULT_LAYOUT = 'fluid';
16
15
  export const CONTROL_POSITIONS = ['bottom', 'left'];
17
16
  export const ALIGNMENTS = ['left', 'center'];
18
- export const LAYOUTS = ['fluid', 'fixed'];
19
17
 
20
18
  export default class HdsFormRadioCardIndexComponent extends Component {
21
19
  @tracked ariaDescribedBy = this.args.extraAriaDescribedBy;
@@ -71,34 +69,6 @@ export default class HdsFormRadioCardIndexComponent extends Component {
71
69
  return alignment;
72
70
  }
73
71
 
74
- /**
75
- * Sets the layout of the card within the group
76
- * Accepted values: fluid, fixed
77
- *
78
- * @param layout
79
- * @type {string}
80
- * @default 'fluid'
81
- */
82
- get layout() {
83
- let { layout = DEFAULT_LAYOUT } = this.args;
84
-
85
- assert(
86
- `@layout for "Hds::Form::RadioCard" must be one of the following: ${LAYOUTS.join(
87
- ', '
88
- )}; received: ${layout}`,
89
- LAYOUTS.includes(layout)
90
- );
91
-
92
- // if the `@layout` is set to 'fixed' we need a `@maxWidth` value to constrain the card to
93
- if (layout === 'fixed') {
94
- assert(
95
- `@maxWidth for "Hds::Form::RadioCard" with @layout "fixed" is required`,
96
- this.args.maxWidth
97
- );
98
- }
99
- return layout;
100
- }
101
-
102
72
  /**
103
73
  * Get the class names to apply to the component.
104
74
  * @method classNames
@@ -113,6 +83,11 @@ export default class HdsFormRadioCardIndexComponent extends Component {
113
83
  if (this.args.disabled) {
114
84
  classes.push('hds-form-radio-card--disabled');
115
85
  }
86
+ if (this.args.maxWidth) {
87
+ classes.push('hds-form-radio-card--has-fixed-width');
88
+ } else {
89
+ classes.push('hds-form-radio-card--has-fluid-width');
90
+ }
116
91
 
117
92
  // add a class based on the @controlPosition argument
118
93
  classes.push(`hds-form-radio-card--control-${this.controlPosition}`);
@@ -120,9 +95,6 @@ export default class HdsFormRadioCardIndexComponent extends Component {
120
95
  // add a class based on the @alignment argument
121
96
  classes.push(`hds-form-radio-card--align-${this.alignment}`);
122
97
 
123
- // add a class based on the @layout argument
124
- classes.push(`hds-form-radio-card--layout-${this.layout}`);
125
-
126
98
  return classes.join(' ');
127
99
  }
128
100
  }
@@ -2,4 +2,10 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <span class="hds-form-radio-card__label hds-typography-display-300 hds-font-weight-bold" ...attributes>{{yield}}</span>
5
+ <Hds::Text::Display
6
+ class="hds-form-radio-card__label"
7
+ @tag="span"
8
+ @size="300"
9
+ @weight="bold"
10
+ ...attributes
11
+ >{{yield}}</Hds::Text::Display>
@@ -57,6 +57,11 @@ export default class HdsFormTextInputBaseComponent extends Component {
57
57
  classes.push(`hds-form-text-input--is-invalid`);
58
58
  }
59
59
 
60
+ // add a class based on the @hasVisibilityToggle argument
61
+ if (this.args.hasVisibilityToggle) {
62
+ classes.push(`hds-form-text-input--has-visibility-toggle`);
63
+ }
64
+
60
65
  // add a class based on the @isLoading argument
61
66
  if (this.args.isLoading) {
62
67
  classes.push(`hds-form-text-input--is-loading`);
@@ -14,16 +14,28 @@
14
14
  {{yield (hash Label=F.Label isRequired=F.isRequired isOptional=F.isOptional)}}
15
15
  {{yield (hash HelperText=F.HelperText Error=F.Error)}}
16
16
  <F.Control>
17
- <Hds::Form::TextInput::Base
18
- @type={{@type}}
19
- @value={{@value}}
20
- @isInvalid={{@isInvalid}}
21
- @width={{@width}}
22
- @isLoading={{@isLoading}}
23
- required={{@isRequired}}
24
- ...attributes
25
- id={{F.id}}
26
- aria-describedby={{F.ariaDescribedBy}}
27
- />
17
+ <div class="hds-form-text-input__wrapper" {{style width=@width}}>
18
+ <Hds::Form::TextInput::Base
19
+ @type={{this.type}}
20
+ @value={{@value}}
21
+ @isInvalid={{@isInvalid}}
22
+ @isLoading={{@isLoading}}
23
+ @hasVisibilityToggle={{this.showVisibilityToggle}}
24
+ required={{@isRequired}}
25
+ ...attributes
26
+ id={{F.id}}
27
+ aria-describedby={{F.ariaDescribedBy}}
28
+ />
29
+ {{#if this.showVisibilityToggle}}
30
+ <Hds::Form::VisibilityToggle
31
+ @isVisible={{this.isPasswordMasked}}
32
+ @ariaLabel={{this.visibilityToggleAriaLabel}}
33
+ @ariaMessageText={{this.visibilityToggleAriaMessageText}}
34
+ aria-controls={{F.id}}
35
+ class="hds-form-text-input__visibility-toggle"
36
+ {{on "click" this.onClickTogglePasswordReadability}}
37
+ />
38
+ {{/if}}
39
+ </div>
28
40
  </F.Control>
29
41
  </Hds::Form::Field>
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) HashiCorp, Inc.
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+
6
+ import Component from '@glimmer/component';
7
+ import { tracked } from '@glimmer/tracking';
8
+ import { action } from '@ember/object';
9
+
10
+ export default class HdsFormTextInputFieldComponent extends Component {
11
+ @tracked isPasswordMasked = true;
12
+ @tracked hasVisibilityToggle = this.args.hasVisibilityToggle ?? true;
13
+ @tracked type = this.args.type ?? 'text';
14
+
15
+ /**
16
+ * @param showVisibilityToggle
17
+ * @type {boolean}
18
+ * @default false
19
+ */
20
+ get showVisibilityToggle() {
21
+ return this.args.type === 'password' && this.hasVisibilityToggle;
22
+ }
23
+
24
+ /**
25
+ * @param visibilityToggleAriaLabel
26
+ * @type {string}
27
+ * @default 'Show password'
28
+ */
29
+ get visibilityToggleAriaLabel() {
30
+ if (this.args.visibilityToggleAriaLabel) {
31
+ return this.args.visibilityToggleAriaLabel;
32
+ } else if (this.isPasswordMasked) {
33
+ return 'Show password';
34
+ } else {
35
+ return 'Hide password';
36
+ }
37
+ }
38
+
39
+ /**
40
+ * @param visibilityToggleAriaMessageText
41
+ * @type {string}
42
+ * @default 'Password is now hidden'
43
+ */
44
+ get visibilityToggleAriaMessageText() {
45
+ if (this.args.visibilityToggleAriaMessageText) {
46
+ return this.args.visibilityToggleAriaMessageText;
47
+ } else if (this.isPasswordMasked) {
48
+ return 'Password is hidden';
49
+ } else {
50
+ return 'Password is visible';
51
+ }
52
+ }
53
+
54
+ @action
55
+ onClickTogglePasswordReadability() {
56
+ this.isPasswordMasked = !this.isPasswordMasked;
57
+ this.type = this.isPasswordMasked ? 'password' : 'text';
58
+ }
59
+ }
@@ -0,0 +1,8 @@
1
+ {{!
2
+ Copyright (c) HashiCorp, Inc.
3
+ SPDX-License-Identifier: MPL-2.0
4
+ }}
5
+ <button class="hds-form-visibility-toggle" type="button" aria-label={{@ariaLabel}} ...attributes>
6
+ <FlightIcon @name={{if @isVisible "eye" "eye-off"}} @size="16" @isInlineBlock={{false}} />
7
+ <span class="sr-only" aria-live="polite">{{@ariaMessageText}}</span>
8
+ </button>
@@ -6,7 +6,25 @@
6
6
  import Component from '@glimmer/component';
7
7
  import { action } from '@ember/object';
8
8
 
9
- export default class HdsInteractiveComponent extends Component {
9
+ export interface HdsInteractiveSignature {
10
+ Args: {
11
+ href?: string;
12
+ isHrefExternal?: boolean;
13
+ isRouteExternal?: boolean;
14
+ route?: unknown;
15
+ models?: unknown;
16
+ model?: unknown;
17
+ query?: unknown;
18
+ 'current-when'?: unknown;
19
+ replace?: unknown;
20
+ };
21
+ Blocks: {
22
+ default: [];
23
+ };
24
+ Element: HTMLAnchorElement | HTMLButtonElement;
25
+ }
26
+
27
+ export default class HdsInteractiveIndexComponent extends Component<HdsInteractiveSignature> {
10
28
  /**
11
29
  * Determines if a @href value is "external" (it adds target="_blank" rel="noopener noreferrer")
12
30
  *
@@ -30,9 +48,16 @@ export default class HdsInteractiveComponent extends Component {
30
48
  }
31
49
 
32
50
  @action
33
- onKeyUp(event) {
51
+ onKeyUp(event: KeyboardEvent) {
34
52
  if (event.key === ' ' || event.code === 'Space') {
35
- event.target.click();
53
+ (event.target as HTMLElement).click();
36
54
  }
37
55
  }
38
56
  }
57
+
58
+ declare module '@glint/environment-ember-loose/registry' {
59
+ export default interface Registry {
60
+ 'Hds::Interactive': typeof HdsInteractiveIndexComponent;
61
+ 'hds/interactive': typeof HdsInteractiveIndexComponent;
62
+ }
63
+ }
@@ -6,13 +6,13 @@
6
6
  {{#if @icon}}
7
7
  <FlightIcon class="hds-modal__icon" @name={{@icon}} @size="24" @isInlineBlock={{false}} />
8
8
  {{/if}}
9
- <div class="hds-modal__title hds-typography-display-300 hds-font-weight-semibold" id={{@id}}>
9
+ <Hds::Text::Display class="hds-modal__title" @tag="div" @size="300" @weight="semibold" id={{@id}}>
10
10
  {{#if @tagline}}
11
- <div class="hds-modal__tagline hds-typography-body-100 hds-font-weight-regular">
11
+ <Hds::Text::Body class="hds-modal__tagline" @tag="div" @size="100" @weight="regular">
12
12
  {{@tagline}}
13
- </div>
13
+ </Hds::Text::Body>
14
14
  {{/if}}
15
15
  {{yield}}
16
- </div>
16
+ </Hds::Text::Display>
17
17
  <Hds::DismissButton class="hds-modal__dismiss" {{on "click" @onDismiss}} />
18
18
  </div>
@@ -2,4 +2,10 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <p class="hds-foreground-primary hds-typography-body-200 hds-page-header__description" ...attributes>{{yield}}</p>
5
+ <Hds::Text::Body
6
+ class="hds-page-header__description"
7
+ @tag="p"
8
+ @size="200"
9
+ @color="primary"
10
+ ...attributes
11
+ >{{yield}}</Hds::Text::Body>
@@ -2,4 +2,10 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <p class="hds-foreground-faint hds-typography-body-200 hds-page-header__subtitle" ...attributes>{{yield}}</p>
5
+ <Hds::Text::Body
6
+ class="hds-page-header__subtitle"
7
+ @tag="p"
8
+ @size="200"
9
+ @color="faint"
10
+ ...attributes
11
+ >{{yield}}</Hds::Text::Body>
@@ -2,4 +2,10 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <h1 class="hds-foreground-strong hds-typography-display-500 hds-page-header__title" ...attributes>{{yield}}</h1>
5
+ <Hds::Text::Display
6
+ class="hds-page-header__title"
7
+ @tag="h1"
8
+ @size="500"
9
+ @color="strong"
10
+ ...attributes
11
+ >{{yield}}</Hds::Text::Display>
@@ -7,15 +7,12 @@ import Component from '@glimmer/component';
7
7
  import { tracked } from '@glimmer/tracking';
8
8
  import { action } from '@ember/object';
9
9
  import { assert } from '@ember/debug';
10
- import { inject as service } from '@ember/service';
11
10
 
12
11
  // for context about the decision to use these values, see:
13
12
  // https://hashicorp.slack.com/archives/C03A0N1QK8S/p1673546329082759
14
13
  export const DEFAULT_PAGE_SIZES = [10, 30, 50];
15
14
 
16
15
  export default class HdsPaginationCompactIndexComponent extends Component {
17
- @service router;
18
-
19
16
  // This private variable is used to differentiate between
20
17
  // "uncontrolled" component (where the state is handled internally) and
21
18
  // "controlled" component (where the state is handled externally, by the consumer's code).
@@ -109,10 +106,6 @@ export default class HdsPaginationCompactIndexComponent extends Component {
109
106
  return pageSizes;
110
107
  }
111
108
 
112
- get routeQueryParams() {
113
- return this.router.currentRoute?.queryParams || {};
114
- }
115
-
116
109
  buildQueryParamsObject(page, pageSize) {
117
110
  if (this.isControlled) {
118
111
  return this.args.queryFunction(page, pageSize);
@@ -162,15 +155,6 @@ export default class HdsPaginationCompactIndexComponent extends Component {
162
155
  onPageSizeChange(newPageSize) {
163
156
  let { onPageSizeChange } = this.args;
164
157
 
165
- // we need to manually update the query parameters in the route (it's not a link!)
166
- if (this.isControlled) {
167
- // we pass `null` as value for the `page` argument, so consumers can handle this condition accordingly (probably will just change the side of the data/array slice)
168
- const queryParams = this.buildQueryParamsObject(null, newPageSize);
169
- this.router.transitionTo({ queryParams });
170
- } else {
171
- this.currentPageSize = newPageSize;
172
- }
173
-
174
158
  // invoke the callback function
175
159
  if (typeof onPageSizeChange === 'function') {
176
160
  onPageSizeChange(newPageSize);
@@ -2,10 +2,10 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <div class="hds-pagination-info hds-typography-body-100 hds-font-weight-medium" ...attributes>
5
+ <Hds::Text::Body class="hds-pagination-info" @tag="div" @size="100" @weight="medium" ...attributes>
6
6
  {{@itemsRangeStart}}–{{@itemsRangeEnd}}
7
7
  {{#if this.showTotalItems}}
8
8
  of
9
9
  {{@totalItems}}
10
10
  {{/if}}
11
- </div>
11
+ </Hds::Text::Body>
@@ -6,9 +6,15 @@
6
6
  <Hds::Interactive class={{this.classNames}} aria-label={{this.content.ariaLabel}} disabled={{true}} ...attributes>
7
7
  <FlightIcon @name={{this.content.icon}} />
8
8
  {{#if this.showLabel}}
9
- <span class="hds-pagination-nav__arrow-label" aria-hidden="true">
9
+ <Hds::Text::Body
10
+ class="hds-pagination-nav__arrow-label"
11
+ @tag="span"
12
+ @size="100"
13
+ @weight="medium"
14
+ aria-hidden="true"
15
+ >
10
16
  {{this.content.label}}
11
- </span>
17
+ </Hds::Text::Body>
12
18
  {{/if}}
13
19
  </Hds::Interactive>
14
20
  {{else}}
@@ -24,9 +30,15 @@
24
30
  >
25
31
  <FlightIcon @name={{this.content.icon}} />
26
32
  {{#if this.showLabel}}
27
- <span class="hds-pagination-nav__arrow-label" aria-hidden="true">
33
+ <Hds::Text::Body
34
+ class="hds-pagination-nav__arrow-label"
35
+ @tag="span"
36
+ @size="100"
37
+ @weight="medium"
38
+ aria-hidden="true"
39
+ >
28
40
  {{this.content.label}}
29
- </span>
41
+ </Hds::Text::Body>
30
42
  {{/if}}
31
43
  </Hds::Interactive>
32
44
  {{/if}}
@@ -61,8 +61,6 @@ export default class HdsPaginationControlArrowComponent extends Component {
61
61
  'hds-pagination-nav__control',
62
62
  'hds-pagination-nav__arrow',
63
63
  `hds-pagination-nav__arrow--direction-${this.args.direction}`,
64
- 'hds-typography-body-100',
65
- 'hds-font-weight-medium',
66
64
  ];
67
65
 
68
66
  return classes.join(' ');
@@ -12,5 +12,6 @@
12
12
  ...attributes
13
13
  aria-current={{if @isSelected "page" null}}
14
14
  >
15
- <span class="sr-only">page </span>{{this.page}}
15
+ <Hds::Text::Body @tag="span" @size="100" @weight="medium"><span class="sr-only">page
16
+ </span>{{this.page}}</Hds::Text::Body>
16
17
  </Hds::Interactive>
@@ -25,12 +25,7 @@ export default class HdsPaginationControlNumberComponent extends Component {
25
25
  * @return {string} The "class" attribute to apply to the component.
26
26
  */
27
27
  get classNames() {
28
- let classes = [
29
- 'hds-pagination-nav__control',
30
- 'hds-pagination-nav__number',
31
- 'hds-typography-body-100',
32
- 'hds-font-weight-medium"',
33
- ];
28
+ let classes = ['hds-pagination-nav__control', 'hds-pagination-nav__number'];
34
29
 
35
30
  if (this.args.isSelected) {
36
31
  classes.push(`hds-pagination-nav__number--is-selected`);
@@ -4,7 +4,7 @@
4
4
  }}
5
5
 
6
6
  <nav class="hds-side-nav__list-wrapper" ...attributes>
7
- {{yield (hash extraBefore=(component "hds/yield"))}}
7
+ {{yield (hash ExtraBefore=(component "hds/yield"))}}
8
8
  <ul class="hds-side-nav__list" role="list">
9
9
  {{yield
10
10
  (hash
@@ -15,5 +15,5 @@
15
15
  )
16
16
  }}
17
17
  </ul>
18
- {{yield (hash extraAfter=(component "hds/yield"))}}
18
+ {{yield (hash ExtraAfter=(component "hds/yield"))}}
19
19
  </nav>
@@ -17,7 +17,12 @@
17
17
  {{else if (eq @status "complete")}}
18
18
  <FlightIcon class="hds-stepper-indicator-step__icon" @name="check" @size="16" />
19
19
  {{else}}
20
- <span class="hds-stepper-indicator-step__text">{{@text}}</span>
20
+ <Hds::Text::Body
21
+ class="hds-stepper-indicator-step__text"
22
+ @tag="span"
23
+ @size="100"
24
+ @weight="medium"
25
+ >{{@text}}</Hds::Text::Body>
21
26
  {{/if}}
22
27
  </div>
23
28
  </div>
@@ -2,7 +2,7 @@
2
2
  Copyright (c) HashiCorp, Inc.
3
3
  SPDX-License-Identifier: MPL-2.0
4
4
  }}
5
- <span class={{this.classNames}} ...attributes>
5
+ <Hds::Text::Body class={{this.classNames}} @tag="span" @size="100" @weight="medium" @color="primary" ...attributes>
6
6
  {{#if this.onDismiss}}
7
7
  <button class="hds-tag__dismiss" type="button" aria-label={{this.ariaLabel}} {{on "click" this.onDismiss}}>
8
8
  <FlightIcon class="hds-tag__dismiss-icon" @name="x" @size="16" @isInlineBlock={{false}} />
@@ -27,4 +27,4 @@
27
27
  {{this.text}}
28
28
  </span>
29
29
  {{/if}}
30
- </span>
30
+ </Hds::Text::Body>
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Copyright (c) HashiCorp, Inc.
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+
6
+ import { modifier } from 'ember-modifier';
7
+ import { assert, warn } from '@ember/debug';
8
+
9
+ export const getTextToCopy = (text) => {
10
+ let textToCopy;
11
+
12
+ if (text) {
13
+ if (typeof text === 'string') {
14
+ textToCopy = text;
15
+ } else if (
16
+ // context: https://github.com/hashicorp/design-system/pull/1564
17
+ typeof text === 'number' ||
18
+ typeof text === 'bigint'
19
+ ) {
20
+ textToCopy = text.toString();
21
+ } else {
22
+ assert(
23
+ `\`hds-clipboard\` modifier - \`text\` argument must be a string - provided: ${typeof text}`
24
+ );
25
+ }
26
+ }
27
+ return textToCopy;
28
+ };
29
+
30
+ export const getTargetElement = (target) => {
31
+ let targetElement;
32
+ if (typeof target === 'string') {
33
+ targetElement = document.querySelector(target);
34
+
35
+ if (!targetElement) {
36
+ console.error(
37
+ '`hds-clipboard` modifier - `target` selector provided does not point to an existing DOM node, check your selector string',
38
+ targetElement
39
+ );
40
+ return;
41
+ }
42
+ } else if (target instanceof Node && target.nodeType === Node.ELEMENT_NODE) {
43
+ targetElement = target;
44
+ } else {
45
+ if (target instanceof NodeList) {
46
+ assert(
47
+ '`hds-clipboard` modifier - `target` argument must be a string or a DOM node - provided: a list of DOM nodes'
48
+ );
49
+ } else {
50
+ assert(
51
+ `\`hds-clipboard\` modifier - \`target\` argument must be a string or a DOM node - provided: ${typeof target}`
52
+ );
53
+ }
54
+ }
55
+ return targetElement;
56
+ };
57
+
58
+ export const getTextToCopyFromTargetElement = (targetElement) => {
59
+ let textToCopy;
60
+ if (
61
+ targetElement instanceof Node &&
62
+ targetElement.nodeType === Node.ELEMENT_NODE
63
+ ) {
64
+ if (
65
+ targetElement instanceof HTMLInputElement || // targetElement.nodeName === 'INPUT' ||
66
+ targetElement instanceof HTMLTextAreaElement || // targetElement.nodeName === 'TEXTAREA' ||
67
+ targetElement instanceof HTMLSelectElement // targetElement.nodeName === 'SELECT'
68
+ ) {
69
+ textToCopy = targetElement.value;
70
+ } else {
71
+ // simplest approach
72
+ textToCopy = targetElement.innerText;
73
+
74
+ // approach based on text selection (left for backup just in case)
75
+ // var selection = window.getSelection();
76
+ // var range = document.createRange();
77
+ // selection.removeAllRanges();
78
+ // range.selectNodeContents(targetElement);
79
+ // selection.addRange(range);
80
+ // textToCopy = selection.toString();
81
+ // selection.removeAllRanges();
82
+ }
83
+ }
84
+ return textToCopy;
85
+ };
86
+
87
+ export const writeTextToClipboard = async (textToCopy) => {
88
+ // finally copy the text to the clipboard using the Clipboard API
89
+ // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API
90
+ if (textToCopy) {
91
+ try {
92
+ // notice: the "clipboard-write" permission is granted automatically to pages when they are in the active tab
93
+ // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write
94
+ await navigator.clipboard.writeText(textToCopy);
95
+ // DEBUG uncomment this for easy debugging
96
+ // console.log('success', textToCopy);
97
+ return true;
98
+ } catch (error) {
99
+ // clipboard write failed
100
+ // this probably never happens (see comment above) or happens only for very old browsers that don't for which `navigator.clipboard` is undefined
101
+ warn('copy action failed, please check your browser‘s permissions', {
102
+ id: 'hds-clipboard.write-text-to-clipboard.catch-error',
103
+ });
104
+ return false;
105
+ }
106
+ } else {
107
+ return false;
108
+ }
109
+ };
110
+
111
+ export const copyToClipboard = async (text, target) => {
112
+ let textToCopy;
113
+
114
+ if (text) {
115
+ textToCopy = getTextToCopy(text);
116
+ } else if (target) {
117
+ const targetElement = getTargetElement(target);
118
+ textToCopy = getTextToCopyFromTargetElement(targetElement);
119
+ } else {
120
+ assert(
121
+ '`hds-clipboard` modifier - either a `text` or a `target` argument is required'
122
+ );
123
+ }
124
+ const success = await writeTextToClipboard(textToCopy);
125
+ return success;
126
+ };
127
+
128
+ // Notice: we use a function-based modifier here instead of a class-based one
129
+ // because it's quite simple in its logic, and doesn't require injecting services
130
+ // see: https://github.com/ember-modifier/ember-modifier#function-based-modifiers
131
+
132
+ export default modifier((element, positional, named) => {
133
+ assert(
134
+ '`hds-clipboard` modifier - the modifier must be applied to an element',
135
+ element
136
+ );
137
+
138
+ const { text, target, onSuccess, onError } = named;
139
+
140
+ const onClick = async (event) => {
141
+ const trigger = event.currentTarget;
142
+ const success = await copyToClipboard(text, target);
143
+
144
+ // fire the `onSuccess/onError` callbacks (if provided)
145
+ if (success) {
146
+ if (typeof onSuccess === 'function') {
147
+ onSuccess({ trigger, text, target });
148
+ }
149
+ } else {
150
+ if (typeof onError === 'function') {
151
+ onError({ trigger, text, target });
152
+ }
153
+ }
154
+ };
155
+
156
+ // add the "onClick" event listener to the element
157
+ element.addEventListener('click', onClick);
158
+
159
+ // this (teardown) function is run when the element is removed
160
+ return () => {
161
+ element.removeEventListener('click', onClick);
162
+ };
163
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) HashiCorp, Inc.
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+
6
+ import type HdsButtonIndexComponent from '@hashicorp/design-system-components/components/hds/button';
7
+ import type HdsInteractiveIndexComponent from '@hashicorp/design-system-components/components/hds/interactive';
8
+
9
+ export default interface HdsComponentsRegistry {
10
+ HdsButtonComponent: typeof HdsButtonIndexComponent;
11
+ HdsInteractiveComponent: typeof HdsInteractiveIndexComponent;
12
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) HashiCorp, Inc.
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+
6
+ export { default } from '@hashicorp/design-system-components/components/hds/form/visibility-toggle/index';