@hashicorp/design-system-components 0.9.2 → 0.10.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 (31) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/addon/components/hds/disclosure/index.js +4 -0
  3. package/addon/components/hds/dropdown/index.hbs +10 -13
  4. package/addon/components/hds/dropdown/index.js +45 -0
  5. package/addon/components/hds/dropdown/list-item/copy-item.hbs +20 -0
  6. package/addon/components/hds/dropdown/list-item/copy-item.js +48 -0
  7. package/addon/components/hds/dropdown/list-item/description.hbs +3 -0
  8. package/addon/components/hds/dropdown/list-item/description.js +38 -0
  9. package/addon/components/hds/dropdown/list-item/generic.hbs +3 -0
  10. package/addon/components/hds/dropdown/list-item/interactive.hbs +44 -0
  11. package/addon/components/hds/dropdown/list-item/interactive.js +59 -0
  12. package/addon/components/hds/dropdown/list-item/separator.hbs +1 -0
  13. package/addon/components/hds/dropdown/list-item/title.hbs +3 -0
  14. package/addon/components/hds/dropdown/list-item/title.js +35 -0
  15. package/addon/components/hds/dropdown/{toggle-button.hbs → toggle/button.hbs} +3 -3
  16. package/addon/components/hds/dropdown/{toggle-button.js → toggle/button.js} +21 -3
  17. package/addon/components/hds/dropdown/toggle/icon.hbs +12 -0
  18. package/addon/components/hds/dropdown/{toggle-icon.js → toggle/icon.js} +28 -13
  19. package/app/components/hds/dropdown/list-item/copy-item.js +1 -0
  20. package/app/components/hds/dropdown/list-item/description.js +1 -0
  21. package/app/components/hds/dropdown/list-item/generic.js +1 -0
  22. package/app/components/hds/dropdown/list-item/interactive.js +1 -0
  23. package/app/components/hds/dropdown/list-item/separator.js +1 -0
  24. package/app/components/hds/dropdown/{toggle-icon.js → list-item/title.js} +1 -1
  25. package/app/components/hds/dropdown/{toggle-button.js → toggle/button.js} +1 -1
  26. package/app/components/hds/dropdown/{list-item.js → toggle/icon.js} +1 -1
  27. package/app/styles/components/dropdown.scss +121 -113
  28. package/package.json +2 -1
  29. package/addon/components/hds/dropdown/list-item.hbs +0 -84
  30. package/addon/components/hds/dropdown/list-item.js +0 -120
  31. package/addon/components/hds/dropdown/toggle-icon.hbs +0 -22
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#200](https://github.com/hashicorp/design-system/pull/200) [`a8072537`](https://github.com/hashicorp/design-system/commit/a8072537542791398d375cde4a7a85c2955c66da) Thanks [@didoo](https://github.com/didoo)! - Updated Dropdown component:
8
+
9
+ - added chevron animation for `toggle` elements
10
+ - fixed issue with `list-item/interactive` height
11
+ - added handling of dynamic `width` for the list
12
+ - exposed an `onClose` event
13
+ - removed the default icon for `toggle/icon`
14
+ - removed icon requirement from the `critical` list item
15
+ - updated the documentation and integration tests
16
+ - some code refactorings, reorganizations and cleanups
17
+
3
18
  ## 0.9.2
4
19
 
5
20
  ### Patch Changes
@@ -38,6 +38,10 @@ export default class HdsDisclosureComponent extends Component {
38
38
  this.isActive = false;
39
39
  // we need to reset this check
40
40
  this.isToggleClicked = false;
41
+ // we call the "onClose" callback if it exists (and is a function)
42
+ if (this.args.onClose && typeof this.args.onClose === 'function') {
43
+ this.args.onClose();
44
+ }
41
45
  }
42
46
  }
43
47
  }
@@ -1,25 +1,22 @@
1
- <Hds::Disclosure class="hds-dropdown">
1
+ <Hds::Disclosure class="hds-dropdown" @onClose={{@onClose}} ...attributes>
2
2
  <:toggle as |t|>
3
3
  {{yield
4
4
  (hash
5
- ToggleButton=(component "hds/dropdown/toggle-button" isOpen=t.isActive onClick=t.onClickToggle)
6
- ToggleIcon=(component "hds/dropdown/toggle-icon" isOpen=t.isActive onClick=t.onClickToggle)
5
+ ToggleButton=(component "hds/dropdown/toggle/button" isOpen=t.isActive onClick=t.onClickToggle)
6
+ ToggleIcon=(component "hds/dropdown/toggle/icon" isOpen=t.isActive onClick=t.onClickToggle)
7
7
  )
8
8
  }}
9
9
  </:toggle>
10
10
  <:content>
11
- <ul
12
- class="hds-dropdown-list
13
- {{if (eq @listPosition 'left') 'hds-dropdown-list--position-left' 'hds-dropdown-list--position-right'}}"
14
- >
11
+ <ul class={{this.listClassNames}} {{style width=@width}}>
15
12
  {{yield
16
13
  (hash
17
- CopyItem=(component "hds/dropdown/list-item" item="copy-item")
18
- Description=(component "hds/dropdown/list-item" item="description")
19
- Generic=(component "hds/dropdown/list-item" item="generic")
20
- Interactive=(component "hds/dropdown/list-item")
21
- Separator=(component "hds/dropdown/list-item" item="separator")
22
- Title=(component "hds/dropdown/list-item" item="title")
14
+ CopyItem=(component "hds/dropdown/list-item/copy-item")
15
+ Description=(component "hds/dropdown/list-item/description")
16
+ Generic=(component "hds/dropdown/list-item/generic")
17
+ Interactive=(component "hds/dropdown/list-item/interactive")
18
+ Separator=(component "hds/dropdown/list-item/separator")
19
+ Title=(component "hds/dropdown/list-item/title")
23
20
  )
24
21
  }}
25
22
  </ul>
@@ -0,0 +1,45 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export const DEFAULT_POSITION = 'right';
5
+ export const POSITIONS = ['right', 'left'];
6
+
7
+ export default class HdsDropdownIndexComponent extends Component {
8
+ /**
9
+ * @param listPosition
10
+ * @type {string}
11
+ * @default primary
12
+ * @description Determines the position of the "list"
13
+ */
14
+ get listPosition() {
15
+ let { listPosition = DEFAULT_POSITION } = this.args;
16
+
17
+ assert(
18
+ `@listPosition for "Hds::Dropdown::Index" must be one of the following: ${POSITIONS.join(
19
+ ', '
20
+ )}; received: ${listPosition}`,
21
+ POSITIONS.includes(listPosition)
22
+ );
23
+
24
+ return listPosition;
25
+ }
26
+
27
+ /**
28
+ * Get the class names to apply to the "list"
29
+ * @method DropdownIndex#listClassNames
30
+ * @return {string} The "class" attribute to apply to the "list" element
31
+ */
32
+ get listClassNames() {
33
+ let classes = ['hds-dropdown-list'];
34
+
35
+ // add a class based on the @listPosition argument
36
+ classes.push(`hds-dropdown-list--position-${this.listPosition}`);
37
+
38
+ // add a class based on the @width argument
39
+ if (this.args.width) {
40
+ classes.push('hds-dropdown-list--fixed-width');
41
+ }
42
+
43
+ return classes.join(' ');
44
+ }
45
+ }
@@ -0,0 +1,20 @@
1
+ <li class={{this.classNames}} ...attributes>
2
+ {{#if @copyItemTitle}}
3
+ <div
4
+ class="hds-dropdown-list-item__copy-item-title hds-typography-body-100 hds-font-weight-semibold"
5
+ >{{@copyItemTitle}}</div>
6
+ {{/if}}
7
+ <button
8
+ type="button"
9
+ class="{{if @state (concat 'is-' @state)}} {{if this.isSuccess 'is-success'}}"
10
+ {{on "click" this.copyCode}}
11
+ >
12
+ <div class="hds-dropdown-list-item__copy-item-text hds-typography-code-100">
13
+ {{this.text}}
14
+ </div>
15
+ <FlightIcon
16
+ @name="{{if this.isSuccess 'clipboard-checked' 'clipboard-copy'}}"
17
+ class="hds-dropdown-list-item__copy-item-icon"
18
+ />
19
+ </button>
20
+ </li>
@@ -0,0 +1,48 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { action } from '@ember/object';
5
+
6
+ export default class HdsDropdownListItemCopyItemComponent extends Component {
7
+ @tracked isSuccess = this.args.isSuccess ?? false;
8
+
9
+ /**
10
+ * @param text
11
+ * @type {string}
12
+ * @description The text of the item. If no text value is defined an error will be thrown
13
+ */
14
+ get text() {
15
+ let { text } = this.args;
16
+
17
+ assert(
18
+ '@text for "Hds::Dropdown::ListItem::CopyItem" must have a valid value',
19
+ text !== undefined
20
+ );
21
+
22
+ return text;
23
+ }
24
+
25
+ /**
26
+ * Get the class names to apply to the component.
27
+ * @method classNames
28
+ * @return {string} The "class" attribute to apply to the component.
29
+ */
30
+ get classNames() {
31
+ let classes = [
32
+ 'hds-dropdown-list-item',
33
+ 'hds-dropdown-list-item--copy-item',
34
+ ];
35
+
36
+ return classes.join(' ');
37
+ }
38
+
39
+ @action
40
+ copyCode() {
41
+ navigator.clipboard.writeText(this.args.text);
42
+ // this if statement resolves to [object Promise] so maybe some improvements
43
+ // could be made here
44
+ if (navigator.clipboard.readText()) {
45
+ this.isSuccess = true;
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,3 @@
1
+ <li class={{this.classNames}} ...attributes>
2
+ {{this.text}}
3
+ </li>
@@ -0,0 +1,38 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export default class HdsDropdownListItemDescriptionComponent extends Component {
5
+ /**
6
+ * @param text
7
+ * @type {string}
8
+ * @description The text of the item. If no text value is defined an error will be thrown
9
+ */
10
+ get text() {
11
+ let { text } = this.args;
12
+
13
+ assert(
14
+ '@text for "Hds::Dropdown::ListItem::Description" must have a valid value',
15
+ text !== undefined
16
+ );
17
+
18
+ return text;
19
+ }
20
+
21
+ /**
22
+ * Get the class names to apply to the component.
23
+ * @method classNames
24
+ * @return {string} The "class" attribute to apply to the component.
25
+ */
26
+ get classNames() {
27
+ let classes = [
28
+ 'hds-dropdown-list-item',
29
+ 'hds-dropdown-list-item--description',
30
+ ];
31
+
32
+ // add classes for the typographic style
33
+ classes.push('hds-typography-body-100');
34
+ classes.push('hds-font-weight-regular');
35
+
36
+ return classes.join(' ');
37
+ }
38
+ }
@@ -0,0 +1,3 @@
1
+ <li class="hds-dropdown-list-item hds-dropdown-list-item--generic" ...attributes>
2
+ {{yield}}
3
+ </li>
@@ -0,0 +1,44 @@
1
+ <li class={{this.classNames}}>
2
+ {{#if @route}}
3
+ <LinkTo
4
+ class="{{if @state (concat 'is-' @state)}}"
5
+ @current-when={{@current-when}}
6
+ @models={{hds-link-to-models @model @models}}
7
+ @query={{hds-link-to-query @query}}
8
+ @replace={{@replace}}
9
+ @route={{@route}}
10
+ ...attributes
11
+ >
12
+ {{#if @icon}}
13
+ <div class="hds-dropdown-list-item__interactive-icon">
14
+ <FlightIcon @name={{@icon}} @isInlineBlock={{false}} />
15
+ </div>
16
+ {{/if}}
17
+ <div class="hds-dropdown-list-item__interactive-text hds-typography-body-200 hds-font-weight-medium">
18
+ {{this.text}}
19
+ </div>
20
+ </LinkTo>
21
+ {{else if @href}}
22
+ <a target="_blank" rel="noopener noreferrer" href={{@href}} class="{{if @state (concat 'is-' @state)}}">
23
+ {{#if @icon}}
24
+ <div class="hds-dropdown-list-item__interactive-icon">
25
+ <FlightIcon @name={{@icon}} @isInlineBlock={{false}} />
26
+ </div>
27
+ {{/if}}
28
+ <div class="hds-dropdown-list-item__interactive-text hds-typography-body-200 hds-font-weight-medium">
29
+ {{this.text}}
30
+ </div>
31
+ </a>
32
+ {{else}}
33
+ <button class="{{if @state (concat 'is-' @state)}}" type="button" ...attributes>
34
+ {{#if @icon}}
35
+ <div class="hds-dropdown-list-item__interactive-icon">
36
+ <FlightIcon @name={{@icon}} @isInlineBlock={{false}} />
37
+ </div>
38
+ {{/if}}
39
+ <div class="hds-dropdown-list-item__interactive-text hds-typography-body-200 hds-font-weight-medium">
40
+ {{this.text}}
41
+ </div>
42
+ </button>
43
+ {{/if}}
44
+ </li>
@@ -0,0 +1,59 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export const DEFAULT_COLOR = 'action';
5
+ export const COLORS = ['action', 'critical'];
6
+
7
+ export default class HdsDropdownListItemInteractiveComponent extends Component {
8
+ /**
9
+ * @param text
10
+ * @type {string}
11
+ * @description The text of the item. If no text value is defined an error will be thrown
12
+ */
13
+ get text() {
14
+ let { text } = this.args;
15
+
16
+ assert(
17
+ '@text for "Hds::Dropdown::ListItem::Interactive" must have a valid value',
18
+ text !== undefined
19
+ );
20
+
21
+ return text;
22
+ }
23
+
24
+ /**
25
+ * @param color
26
+ * @type {string}
27
+ * @default primary
28
+ * @description Determines the color of the item (when item is set to interactive)
29
+ */
30
+ get color() {
31
+ let { color = DEFAULT_COLOR } = this.args;
32
+
33
+ assert(
34
+ `@color for "Hds::Dropdown::ListItem::Interactive" must be one of the following: ${COLORS.join(
35
+ ', '
36
+ )}; received: ${color}`,
37
+ COLORS.includes(color)
38
+ );
39
+
40
+ return color;
41
+ }
42
+
43
+ /**
44
+ * Get the class names to apply to the component.
45
+ * @method classNames
46
+ * @return {string} The "class" attribute to apply to the component.
47
+ */
48
+ get classNames() {
49
+ let classes = [
50
+ 'hds-dropdown-list-item',
51
+ 'hds-dropdown-list-item--interactive',
52
+ ];
53
+
54
+ // add a class based on the @color argument
55
+ classes.push(`hds-dropdown-list-item--color-${this.color}`);
56
+
57
+ return classes.join(' ');
58
+ }
59
+ }
@@ -0,0 +1 @@
1
+ <li class="hds-dropdown-list-item hds-dropdown-list-item--separator" role="separator" ...attributes></li>
@@ -0,0 +1,3 @@
1
+ <li class={{this.classNames}} ...attributes>
2
+ {{this.text}}
3
+ </li>
@@ -0,0 +1,35 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export default class HdsDropdownListItemTitleComponent extends Component {
5
+ /**
6
+ * @param text
7
+ * @type {string}
8
+ * @description The text of the item. If no text value is defined an error will be thrown
9
+ */
10
+ get text() {
11
+ let { text } = this.args;
12
+
13
+ assert(
14
+ '@text for "Hds::Dropdown::ListItem::Title" must have a valid value',
15
+ text !== undefined
16
+ );
17
+
18
+ return text;
19
+ }
20
+
21
+ /**
22
+ * Get the class names to apply to the component.
23
+ * @method classNames
24
+ * @return {string} The "class" attribute to apply to the component.
25
+ */
26
+ get classNames() {
27
+ let classes = ['hds-dropdown-list-item', 'hds-dropdown-list-item--title'];
28
+
29
+ // add classes for the typographic style
30
+ classes.push('hds-typography-body-100');
31
+ classes.push('hds-font-weight-semibold');
32
+
33
+ return classes.join(' ');
34
+ }
35
+ }
@@ -13,11 +13,11 @@
13
13
  }}
14
14
 
15
15
  <Hds::Button
16
+ class={{this.classNames}}
16
17
  @text={{this.text}}
17
- @icon="chevron-{{if @isOpen 'up' 'down'}}"
18
+ @icon="chevron-down"
18
19
  @iconPosition="trailing"
19
20
  @color={{this.color}}
20
- class="hds-dropdown-toggle--with-button-component"
21
- {{on "click" this.onClick}}
22
21
  ...attributes
22
+ {{on "click" this.onClick}}
23
23
  />
@@ -4,6 +4,8 @@ import { assert } from '@ember/debug';
4
4
  export const DEFAULT_COLOR = 'primary';
5
5
  export const COLORS = ['primary', 'secondary'];
6
6
 
7
+ const NOOP = () => {};
8
+
7
9
  export default class HdsDropdownToggleButtonComponent extends Component {
8
10
  /**
9
11
  * @param text
@@ -14,7 +16,7 @@ export default class HdsDropdownToggleButtonComponent extends Component {
14
16
  let { text } = this.args;
15
17
 
16
18
  assert(
17
- '@text for "Hds::Dropdown::ToggleButton" must have a valid value',
19
+ '@text for "Hds::Dropdown::Toggle::Button" must have a valid value',
18
20
  text !== undefined
19
21
  );
20
22
 
@@ -31,7 +33,7 @@ export default class HdsDropdownToggleButtonComponent extends Component {
31
33
  let { color = DEFAULT_COLOR } = this.args;
32
34
 
33
35
  assert(
34
- `@color for "Hds::Dropdown::ToggleButton" must be one of the following: ${COLORS.join(
36
+ `@color for "Hds::Dropdown::Toggle::Button" must be one of the following: ${COLORS.join(
35
37
  ', '
36
38
  )}; received: ${color}`,
37
39
  COLORS.includes(color)
@@ -53,7 +55,23 @@ export default class HdsDropdownToggleButtonComponent extends Component {
53
55
  if (typeof onClick === 'function') {
54
56
  return onClick;
55
57
  } else {
56
- return () => {};
58
+ return NOOP;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get the class names to apply to the component.
64
+ * @method ToggleButton#classNames
65
+ * @return {string} The "class" attribute to apply to the component.
66
+ */
67
+ get classNames() {
68
+ let classes = ['hds-dropdown-toggle-button'];
69
+
70
+ // add a class based on the @isOpen argument
71
+ if (this.args.isOpen) {
72
+ classes.push('hds-dropdown-toggle-button--is-open');
57
73
  }
74
+
75
+ return classes.join(' ');
58
76
  }
59
77
  }
@@ -0,0 +1,12 @@
1
+ <button class={{this.classNames}} aria-label={{this.text}} ...attributes {{on "click" this.onClick}} type="button">
2
+ <div class="hds-dropdown-toggle-icon__wrapper">
3
+ {{#if @imageSrc}}
4
+ <img src={{@imageSrc}} alt="" role="presentation" height="32" width="32" />
5
+ {{else if @icon}}
6
+ <FlightIcon @name={{@icon}} @size="24" />
7
+ {{/if}}
8
+ </div>
9
+ {{#if this.hasChevron}}
10
+ <FlightIcon @name="chevron-down" class="hds-dropdown-toggle-icon__chevron" @isInlineBlock={{false}} />
11
+ {{/if}}
12
+ </button>
@@ -1,7 +1,17 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { assert } from '@ember/debug';
3
3
 
4
+ const NOOP = () => {};
4
5
  export default class HdsDropdownToggleIconComponent extends Component {
6
+ constructor() {
7
+ super(...arguments);
8
+ if (!(this.args.icon || this.args.imageSrc)) {
9
+ assert(
10
+ '@icon or @imageSrc must be defined for "Hds::Dropdown::Toggle::Icon"'
11
+ );
12
+ }
13
+ }
14
+
5
15
  /**
6
16
  * @param text
7
17
  * @type {string}
@@ -11,7 +21,7 @@ export default class HdsDropdownToggleIconComponent extends Component {
11
21
  let { text } = this.args;
12
22
 
13
23
  assert(
14
- '@text for "Hds::Dropdown::ToggleIcon" must have a valid value',
24
+ '@text for "Hds::Dropdown::Toggle::Icon" must have a valid value',
15
25
  text !== undefined
16
26
  );
17
27
 
@@ -29,17 +39,6 @@ export default class HdsDropdownToggleIconComponent extends Component {
29
39
  return this.args.hasChevron ?? true;
30
40
  }
31
41
 
32
- /**
33
- * Sets the icon name
34
- *
35
- * @param icon
36
- * @type {string}
37
- * @default user
38
- */
39
- get icon() {
40
- return this.args.icon ?? 'user';
41
- }
42
-
43
42
  /**
44
43
  * @param onClick
45
44
  * @type {function}
@@ -53,7 +52,23 @@ export default class HdsDropdownToggleIconComponent extends Component {
53
52
  if (typeof onClick === 'function') {
54
53
  return onClick;
55
54
  } else {
56
- return () => {};
55
+ return NOOP;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Get the class names to apply to the component.
61
+ * @method ToggleIcon#classNames
62
+ * @return {string} The "class" attribute to apply to the component.
63
+ */
64
+ get classNames() {
65
+ let classes = ['hds-dropdown-toggle-icon'];
66
+
67
+ // add a class based on the @isOpen argument
68
+ if (this.args.isOpen) {
69
+ classes.push('hds-dropdown-toggle-icon--is-open');
57
70
  }
71
+
72
+ return classes.join(' ');
58
73
  }
59
74
  }
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/copy-item';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/description';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/generic';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/interactive';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/separator';
@@ -1 +1 @@
1
- export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-icon';
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item/title';
@@ -1 +1 @@
1
- export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-button';
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle/button';
@@ -1 +1 @@
1
- export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item';
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle/icon';
@@ -20,29 +20,33 @@
20
20
 
21
21
  @use '../mixins/focus-ring' as *;
22
22
 
23
+ $hds-dropdown-toggle-base-height: 36px;
24
+ $hds-dropdown-toggle-border-radius: 5px;
25
+
26
+
23
27
  // TOGGLE/ICON
28
+
24
29
  .hds-dropdown-toggle-icon {
25
30
  align-items: center;
26
31
  background-color: transparent;
27
32
  border: 1px solid transparent; // We need this to be transparent for a11y
28
- border-radius: 5px;
33
+ border-radius: $hds-dropdown-toggle-border-radius;
29
34
  display: flex;
30
- height: 36px;
35
+ height: $hds-dropdown-toggle-base-height;
31
36
  justify-content: center;
37
+ min-width: $hds-dropdown-toggle-base-height;
32
38
  outline-style: solid; // used to avoid double outline+focus-ring in Safari (see https://github.com/hashicorp/design-system-components/issues/161#issuecomment-1031548656)
33
39
  outline-color: transparent; // We need this to be transparent for a11y
34
40
  padding: 1px;
35
- min-width: 36px;
36
41
 
37
- &:hover,
42
+ &:hover,
38
43
  &.is-hover {
39
44
  background-color: var(--token-color-surface-interactive);
40
45
  border-color: var(--token-color-border-strong);
41
46
  cursor: pointer;
42
47
  }
43
48
 
44
- // this is the :focus
45
- @include hds-focus-ring-with-pseudo-element($top: -1px, $right: -1px, $bottom: -1px, $left: -1px, $radius: 5px);
49
+ @include hds-focus-ring-with-pseudo-element($top: -1px, $right: -1px, $bottom: -1px, $left: -1px, $radius: $hds-dropdown-toggle-border-radius);
46
50
 
47
51
  &:active,
48
52
  &.is-active {
@@ -53,7 +57,7 @@
53
57
 
54
58
  .hds-dropdown-toggle-icon__wrapper {
55
59
  align-items: center;
56
- border-radius: 3px; // 5px- 1px padding - 1px border
60
+ border-radius: 3px; // $hds-dropdown-toggle-border-radius - 1px padding - 1px border
57
61
  display: flex;
58
62
  justify-content: center;
59
63
  height: 32px;
@@ -62,21 +66,43 @@
62
66
 
63
67
  img {
64
68
  border-radius: inherit;
69
+ height: 100%;
65
70
  object-fit: cover; // this will make sure it's correct even if the item isn't square
71
+ width: 100%;
66
72
  }
67
73
  }
68
74
 
69
75
  .hds-dropdown-toggle-icon__chevron {
70
- margin-left: 0.25rem;
76
+ margin-left: 4px;
77
+
78
+ @media (prefers-reduced-motion: no-preference) {
79
+ transition: transform .3s;
80
+ }
81
+
82
+ .hds-dropdown-toggle-icon--is-open & {
83
+ transform: rotate(-180deg);
84
+ }
71
85
  }
72
86
 
73
87
  // TOGGLE/BUTTON
74
- .hds-dropdown-toggle--with-button-component {
88
+
89
+ .hds-dropdown-toggle-button {
75
90
  box-shadow: none; // we override this to remove the elevation style
76
91
 
77
92
  .hds-button__icon {
93
+ margin-left: 8px; // this overrides the rule `.hds-button__text + .hds-button__icon`
78
94
  margin-right: -6px; // we apply a negative margin to counter the padding-right of the button and reduce the visual space between the icon and the right border
79
- margin-left: 0.5rem; // this overrides the rule `.hds-button__text + .hds-button__icon`
95
+
96
+ @media (prefers-reduced-motion: no-preference) {
97
+ transition: transform .3s;
98
+ }
99
+ }
100
+ }
101
+
102
+
103
+ .hds-dropdown-toggle-button--is-open {
104
+ .hds-button__icon {
105
+ transform: rotate(-180deg);
80
106
  }
81
107
  }
82
108
 
@@ -91,71 +117,37 @@
91
117
  box-shadow: var(--token-surface-high-box-shadow);
92
118
  list-style: none;
93
119
  margin: 0;
94
- max-width: 25rem;
95
- min-width: 12.5rem;
120
+ max-width: 400px;
121
+ min-width: 200px;
96
122
  padding: 4px 0;
97
- &.hds-dropdown-list--position-right {
98
- position: absolute;
99
- right: 0;
100
- top: calc(100% + 0.25rem);
101
- z-index: 2; // https://github.com/hashicorp/design-system/issues/114
102
- }
103
-
104
- &.hds-dropdown-list--position-left {
105
- position: absolute;
106
- left: 0;
107
- top: calc(100% + 0.25rem);
108
- z-index: 2; // https://github.com/hashicorp/design-system/issues/114
109
- }
123
+ width: max-content; // notice: this is important because being in a position absolute means the layout algorithm assigns a width of 0 and this impacts on the flex algorithm of the children (in some cases they don't use the full width)
110
124
  }
111
125
 
112
- // LIST > LIST-ITEM
113
- // HDS::DROPDOWN::LIST-ITEM
114
-
115
- .hds-dropdown-list-item--title {
116
- color: var(--token-color-foreground-strong);
117
- font-family: var(--token-typography-body-100-font-family);
118
- font-size: var(--token-typography-body-100-font-size);
119
- font-weight: var(--token-typography-font-weight-semibold);
120
- line-height: var(--token-typography-body-100-line-height);
121
- padding: 10px 16px 4px;
126
+ .hds-dropdown-list--fixed-width {
127
+ max-width: initial;
128
+ min-width: initial;
122
129
  }
123
130
 
124
- .hds-dropdown-list-item--generic {
125
- padding-left: 16px;
126
- padding-right: 16px;
131
+ .hds-dropdown-list--position-right {
132
+ position: absolute;
133
+ right: 0;
134
+ top: calc(100% + 4px);
135
+ z-index: 2; // https://github.com/hashicorp/design-system/issues/114
127
136
  }
128
137
 
129
- .hds-dropdown-list-item--description {
130
- color: var(--token-color-foreground-faint);
131
- font-family: var(--token-typography-body-100-font-family);
132
- font-size: var(--token-typography-body-100-font-size);
133
- font-weight: var(--token-typography-font-weight-regular);
134
- line-height: var(--token-typography-body-100-line-height);
135
- padding: 2px 16px 4px;
138
+ .hds-dropdown-list--position-left {
139
+ left: 0;
140
+ position: absolute;
141
+ top: calc(100% + 4px);
142
+ z-index: 2; // https://github.com/hashicorp/design-system/issues/114
136
143
  }
137
144
 
138
- .hds-dropdown-list-item--separator {
139
- position: relative;
140
- height: 4px;
141
- width: 100%;
142
145
 
143
- &::before {
144
- position: absolute;
145
- right: 6px;
146
- left: 6px;
147
- bottom: 0;
148
- border-bottom: 1px solid var(--token-color-border-primary);
149
- content: '';
150
- }
151
- }
146
+ // LIST > LIST-ITEM
147
+ // HDS::DROPDOWN::LIST-ITEM
152
148
 
153
149
  .hds-dropdown-list-item__copy-item-title {
154
150
  color: var(--token-color-foreground-faint);
155
- font-family: var(--token-typography-body-100-font-family);
156
- font-size: var(--token-typography-body-100-font-size); // 13
157
- font-weight: var(--token-typography-font-weight-semibold);
158
- line-height: var(--token-typography-body-100-line-height); // 18
159
151
  padding: 2px 0 4px;
160
152
  }
161
153
 
@@ -163,17 +155,16 @@
163
155
  padding: 10px 16px 12px;
164
156
  width: 100%;
165
157
 
166
- button {
158
+ button {
167
159
  background-color: transparent;
168
160
  border-radius: 5px;
169
161
  border: 1px solid var(--token-color-border-primary);
170
- color: var(--token-color-foreground-primary);
162
+ color: var(--token-color-foreground-primary);
171
163
  display: flex;
172
- font-family: var(--token-typography-font-stack-code);
173
164
  justify-content: space-between;
174
165
  padding: 12px 8px;
175
166
  width: 100%;
176
-
167
+
177
168
  &:hover,
178
169
  &.is-hover {
179
170
  background-color: var(--token-color-surface-interactive-hover);
@@ -181,55 +172,60 @@
181
172
  }
182
173
 
183
174
  @include hds-focus-ring-basic();
184
-
175
+
185
176
  &:focus,
186
177
  &.is-focus {
187
- //TODO this focus is just way too complex
178
+ //TODO this focus is just way too complex
188
179
  background-color: var(--token-color-surface-action);
189
180
  border-color: var(--token-color-focus-action-internal);
190
181
  }
191
-
192
- &:active,
182
+
183
+ &:active,
193
184
  &.is-active {
194
185
  background-color: var(--token-color-surface-interactive-active);
195
186
  }
196
187
 
197
188
  &.is-success {
198
- border-color: var(--token-color-border-success);
199
189
  background-color: var(--token-color-surface-success);
200
-
190
+ border-color: var(--token-color-border-success);
191
+
201
192
  .hds-dropdown-list-item__copy-item-icon {
202
193
  color: var(--token-color-foreground-success);
203
194
  }
204
- }
205
-
206
- .hds-dropdown-list-item__copy-item-text {
207
- font-size: var(--token-typography-code-100-font-size);
208
- font-weight: var(--token-typography-font-weight-regular);
209
- line-height: var(--token-typography-code-100-line-height);
210
- // max-width: 250px; // TODO we should be able to figure out the proportions here
211
- overflow: hidden;
212
- text-align: left;
213
- text-overflow: ellipsis;
214
- white-space: nowrap;
215
- }
216
-
217
- .hds-dropdown-list-item__copy-item-icon {
218
- color: var(--token-color-foreground-action);
219
- margin-left: 0.5rem;
220
195
  }
221
196
  }
222
197
  }
223
198
 
199
+ .hds-dropdown-list-item__copy-item-text {
200
+ overflow: hidden;
201
+ text-align: left;
202
+ text-overflow: ellipsis;
203
+ white-space: nowrap;
204
+ }
205
+
206
+ .hds-dropdown-list-item__copy-item-icon {
207
+ color: var(--token-color-foreground-action);
208
+ margin-left: 8px;
209
+ }
210
+
211
+ .hds-dropdown-list-item--description {
212
+ color: var(--token-color-foreground-faint);
213
+ padding: 2px 16px 4px;
214
+ }
215
+
216
+ .hds-dropdown-list-item--generic {
217
+ padding-left: 16px;
218
+ padding-right: 16px;
219
+ }
220
+
221
+
224
222
  .hds-dropdown-list-item--interactive {
225
- position: relative;
226
223
  isolation: isolate; // used to create a new stacking context (needed to have the pseudo element below text/icon but not the parent container)
227
224
  min-height: 36px;
225
+ position: relative;
228
226
 
229
- // need to reset a few extra things to make the button visually appear the same as the links
230
- // TODO this is 0.125rem taller than the link...
227
+ // need to reset a few extra things to make the button visually appear the same as the link
231
228
  button {
232
- border: 1px inset transparent; // cause of the extra height
233
229
  background-color: transparent;
234
230
  width: 100%;
235
231
 
@@ -242,10 +238,11 @@
242
238
  // shared styles for links and buttons
243
239
  a, button {
244
240
  align-items: center;
241
+ border: 1px solid transparent; // because a border for the button is needed for a11y, we apply it to both the button and the link so they have the same height
245
242
  display: flex;
246
243
  outline-style: solid; // used to avoid double outline+focus-ring in Safari (see https://github.com/hashicorp/design-system-components/issues/161#issuecomment-1031548656)
247
244
  outline-color: transparent;
248
- padding: 8px 10px 8px 16px;
245
+ padding: 7px 9px 7px 15px; // notice: we're subtracting 1px because of the transparent border
249
246
  text-decoration: none;
250
247
 
251
248
  // this is used for the left "hover" indicator
@@ -253,7 +250,7 @@
253
250
  border-radius: 1px;
254
251
  bottom: 6px;
255
252
  content: '';
256
- left: 0.25rem;
253
+ left: 4px;
257
254
  position: absolute;
258
255
  top: 6px;
259
256
  width: 2px;
@@ -266,11 +263,11 @@
266
263
  border-radius: 5px;
267
264
  bottom: 0px;
268
265
  content: '';
269
- left: 0.625rem;
266
+ left: 10px;
270
267
  position: absolute;
271
- right: 0.25rem;
272
- z-index: -1;
268
+ right: 4px;
273
269
  top: 0;
270
+ z-index: -1;
274
271
  }
275
272
 
276
273
  // Notice: to avoid too much duplication we define two local CSS variables
@@ -282,7 +279,7 @@
282
279
  &::after {
283
280
  background-color: var(--current-background-color);
284
281
  box-shadow: var(--current-focus-ring-box-shadow);
285
- left: 0.25rem;
282
+ left: 4px;
286
283
  }
287
284
  }
288
285
  // undo the previous declaration for browsers that support ":focus-visible" but wouldn't normally show default focus styles
@@ -296,7 +293,7 @@
296
293
  &::after {
297
294
  background-color: var(--current-background-color);
298
295
  box-shadow: var(--current-focus-ring-box-shadow);
299
- left: 0.25rem;
296
+ left: 4px;
300
297
  }
301
298
  }
302
299
  // remove the focus ring on "active + focused" state (by design)
@@ -310,15 +307,11 @@
310
307
  }
311
308
 
312
309
  .hds-dropdown-list-item__interactive-text {
313
- font-family: var(--token-typography-body-200-font-family);
314
- font-size: var(--token-typography-body-200-font-size);
315
- font-weight: var(--token-typography-font-weight-medium);
316
- line-height: var(--token-typography-body-200-line-height);
317
310
  text-align: left; // the button element was centering text
318
311
  }
319
312
 
320
313
  .hds-dropdown-list-item__interactive-icon {
321
- margin-right: 0.5rem;
314
+ margin-right: 8px;
322
315
  }
323
316
 
324
317
  .hds-dropdown-list-item--color-action {
@@ -361,25 +354,19 @@
361
354
  // assign the values to the local CSS variables used above
362
355
  &::after {
363
356
  --current-background-color: var(--token-color-surface-critical);
364
- --current-focus-ring-box-shadow: var(
365
- --token-focus-ring-critical-box-shadow
366
- );
357
+ --current-focus-ring-box-shadow: var(--token-focus-ring-critical-box-shadow);
367
358
  }
368
359
 
369
360
  &:hover,
370
361
  &.is-hover {
371
- color: var(
372
- --token-color-palette-red-300
373
- ); // TODO we need to add this token to the design system
362
+ color: var(--token-color-palette-red-300);
374
363
  &::before {
375
364
  background-color: currentColor;
376
365
  }
377
366
  }
378
367
  &:active,
379
368
  &.is-active {
380
- color: var(
381
- --token-color-palette-red-400
382
- ); // TODO we need to add this token to the design system
369
+ color: var(--token-color-palette-red-400);
383
370
  &::before {
384
371
  background-color: currentColor;
385
372
  }
@@ -389,3 +376,24 @@
389
376
  }
390
377
  }
391
378
  }
379
+
380
+ .hds-dropdown-list-item--separator {
381
+ height: 4px;
382
+ position: relative;
383
+ width: 100%;
384
+
385
+ &::before {
386
+ border-bottom: 1px solid var(--token-color-border-primary);
387
+ bottom: 0;
388
+ content: '';
389
+ left: 6px;
390
+ position: absolute;
391
+ right: 6px;
392
+ }
393
+ }
394
+
395
+ .hds-dropdown-list-item--title {
396
+ color: var(--token-color-foreground-strong);
397
+ padding: 10px 16px 4px;
398
+ }
399
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashicorp/design-system-components",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
4
  "description": "HashiCorp Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",
@@ -75,6 +75,7 @@
75
75
  "ember-resolver": "^8.0.2",
76
76
  "ember-source": "~3.28.0",
77
77
  "ember-source-channel-url": "^3.0.0",
78
+ "ember-style-modifier": "^0.8.0",
78
79
  "ember-template-lint": "^3.6.0",
79
80
  "ember-template-lint-plugin-prettier": "^3.0.1",
80
81
  "ember-truth-helpers": "^3.0.0",
@@ -1,84 +0,0 @@
1
- {{#if (eq this.item "title")}}
2
- <li class={{this.classNames}}>
3
- {{this.text}}
4
- </li>
5
-
6
- {{else if (eq this.item "generic")}}
7
- <li class={{this.classNames}}>
8
- {{yield}}
9
- </li>
10
-
11
- {{else if (eq this.item "copy-item")}}
12
- <li class={{this.classNames}}>
13
- {{#if @copyItemTitle}}
14
- <div class="hds-dropdown-list-item__copy-item-title">{{@copyItemTitle}}</div>
15
- {{/if}}
16
- <button
17
- type="button"
18
- class="{{if @state (concat 'is-' @state)}} {{if this.isSuccess 'is-success'}}"
19
- {{on "click" this.copyCode}}
20
- >
21
- <div class="hds-dropdown-list-item__copy-item-text">
22
- {{this.text}}
23
- </div>
24
- <FlightIcon
25
- @name="{{if this.isSuccess 'clipboard-checked' 'clipboard-copy'}}"
26
- class="hds-dropdown-list-item__copy-item-icon"
27
- />
28
- </button>
29
- </li>
30
-
31
- {{else if (eq this.item "description")}}
32
- <li class={{this.classNames}}>
33
- {{this.text}}
34
- </li>
35
-
36
- {{else if (eq this.item "separator")}}
37
- <li class={{this.classNames}} role="separator"></li>
38
-
39
- {{else if (eq this.item "interactive")}}
40
- <li class={{this.classNames}}>
41
- {{#if @route}}
42
- <LinkTo
43
- class="{{if @state (concat 'is-' @state)}}"
44
- @current-when={{@current-when}}
45
- @models={{hds-link-to-models @model @models}}
46
- @query={{hds-link-to-query @query}}
47
- @replace={{@replace}}
48
- @route={{@route}}
49
- ...attributes
50
- >
51
- {{#if @icon}}
52
- <div class="hds-dropdown-list-item__interactive-icon">
53
- <FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
54
- </div>
55
- {{/if}}
56
- <div class="hds-dropdown-list-item__interactive-text">
57
- {{this.text}}
58
- </div>
59
- </LinkTo>
60
- {{else if @href}}
61
- <a target="_blank" rel="noopener noreferrer" href={{@href}} class="{{if @state (concat 'is-' @state)}}">
62
- {{#if this.icon}}
63
- <div class="hds-dropdown-list-item__interactive-icon">
64
- <FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
65
- </div>
66
- {{/if}}
67
- <div class="hds-dropdown-list-item__interactive-text">
68
- {{this.text}}
69
- </div>
70
- </a>
71
- {{else}}
72
- <button class="{{if @state (concat 'is-' @state)}}" type="button" ...attributes>
73
- {{#if this.icon}}
74
- <div class="hds-dropdown-list-item__interactive-icon">
75
- <FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
76
- </div>
77
- {{/if}}
78
- <div class="hds-dropdown-list-item__interactive-text">
79
- {{this.text}}
80
- </div>
81
- </button>
82
- {{/if}}
83
- </li>
84
- {{/if}}
@@ -1,120 +0,0 @@
1
- import Component from '@glimmer/component';
2
- import { assert } from '@ember/debug';
3
- import { tracked } from '@glimmer/tracking';
4
- import { action } from '@ember/object';
5
-
6
- export const DEFAULT_COLOR = 'action';
7
- export const DEFAULT_ITEM = 'interactive';
8
- export const COLORS = ['action', 'critical'];
9
- export const ITEMS = [
10
- 'copy-item',
11
- 'description',
12
- 'generic',
13
- 'interactive',
14
- 'separator',
15
- 'title',
16
- ];
17
-
18
- export default class HdsDropdownListItemComponent extends Component {
19
- @tracked isSuccess = this.args.isSuccess ?? false;
20
-
21
- /**
22
- * @param item
23
- * @type {string}
24
- * @default interactive
25
- * @description Determines the type of item to show
26
- */
27
- get item() {
28
- let { item = DEFAULT_ITEM } = this.args;
29
-
30
- assert(
31
- `@item for "Hds::Dropdown::ListItem" must be one of the following: ${ITEMS.join(
32
- ', '
33
- )}; received: ${item}`,
34
- ITEMS.includes(item)
35
- );
36
-
37
- return item;
38
- }
39
-
40
- /**
41
- * @param text
42
- * @type {string}
43
- * @description The text of the item. If no text value is defined an error will be thrown unless it is the generic or separator item type.
44
- */
45
- get text() {
46
- let { text } = this.args;
47
-
48
- assert(
49
- '@text for "Hds::Dropdown::ListItem" must have a valid value',
50
- text !== undefined
51
- );
52
-
53
- return text;
54
- }
55
-
56
- /**
57
- * @param color
58
- * @type {string}
59
- * @default primary
60
- * @description Determines the color of the item (when item is set to interactive)
61
- */
62
- get color() {
63
- let { color = DEFAULT_COLOR } = this.args;
64
-
65
- assert(
66
- `@color for "Hds::Dropdown::ListItem" must be one of the following: ${COLORS.join(
67
- ', '
68
- )}; received: ${color}`,
69
- COLORS.includes(color)
70
- );
71
-
72
- return color;
73
- }
74
-
75
- /**
76
- * @param icon
77
- * @type {string}
78
- * @default null
79
- * @description The name of the icon to be used.
80
- */
81
- get icon() {
82
- assert(
83
- `when the "Hds::ListItem" @color is "critical" an @icon is required`,
84
- !(this.color === 'critical' && !this.args.icon)
85
- );
86
-
87
- return this.args.icon ?? null;
88
- }
89
-
90
- /**
91
- * Get the class names to apply to the component.
92
- * @method classNames
93
- * @return {string} The "class" attribute to apply to the component.
94
- */
95
- get classNames() {
96
- let classes = ['hds-dropdown-list-item'];
97
-
98
- // add a class based on the @item argument
99
- if (this.item) {
100
- classes.push(`hds-dropdown-list-item--${this.item}`);
101
- }
102
-
103
- // add a class based on the @color argument
104
- if (this.item === 'interactive') {
105
- classes.push(`hds-dropdown-list-item--color-${this.color}`);
106
- }
107
-
108
- return classes.join(' ');
109
- }
110
-
111
- @action
112
- copyCode() {
113
- navigator.clipboard.writeText(this.args.text);
114
- // this if statement resolves to [object Promise] so maybe some improvements
115
- // could be made here
116
- if (navigator.clipboard.readText()) {
117
- this.isSuccess = true;
118
- }
119
- }
120
- }
@@ -1,22 +0,0 @@
1
- <button
2
- class="hds-dropdown-toggle-icon"
3
- aria-label={{this.text}}
4
- ...attributes
5
- {{on "click" this.onClick}}
6
- type="button"
7
- >
8
- <div class="hds-dropdown-toggle-icon__wrapper">
9
- {{#if @imageSrc}}
10
- <img src={{@imageSrc}} alt="" role="presentation" height="32" width="32" />
11
- {{else}}
12
- <FlightIcon @name={{this.icon}} @size="24" />
13
- {{/if}}
14
- </div>
15
- {{#if this.hasChevron}}
16
- <FlightIcon
17
- @name="chevron-{{if @isOpen 'up' 'down'}}"
18
- class="hds-dropdown-toggle-icon__chevron"
19
- @isInlineBlock={{false}}
20
- />
21
- {{/if}}
22
- </button>