@hashicorp/design-system-components 0.8.0 → 0.9.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 0.9.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#209](https://github.com/hashicorp/design-system/pull/209) [`6021d433`](https://github.com/hashicorp/design-system/commit/6021d43352b8e38b268b06cd98ca0c62adb14999) Thanks [@didoo](https://github.com/didoo)! - Re-ordered declarations of CSS states
8
+
9
+ ## 0.9.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies []:
14
+ - @hashicorp/ember-flight-icons@2.0.5
15
+
16
+ ## 0.9.0
17
+
18
+ ### Minor Changes
19
+
20
+ - [#66](https://github.com/hashicorp/design-system/pull/66) [`29e2ce55`](https://github.com/hashicorp/design-system/commit/29e2ce55bab74cc02dd511794dfadd2b3ac40a14) Thanks [@MelSumner](https://github.com/MelSumner)! - Adds dropdown component to the design system
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies []:
25
+ - @hashicorp/ember-flight-icons@2.0.4
26
+
3
27
  ## 0.8.0
4
28
 
5
29
  ### Minor Changes
@@ -14,25 +14,27 @@ The engineering checklist has six parts: creating the feature branch, component
14
14
 
15
15
  ### Component Creation
16
16
 
17
- - [ ] create new branch from main for the component.
17
+ - [ ] create new branch from main for the component (`git checkout -b USER/COMPONENT-NAME`)
18
18
  - [ ] create new component
19
- - `ember generate component hds/COMPONENT_NAME/index --gc` (the component won't need to be invoked with index, it's just to put all the files in the correct place)
20
- - if it's a variation on a component, then `hds/COMPONENT_NAME/VARIATION` instead of `index`
19
+ - `ember generate component hds/COMPONENT-NAME/index --gc` (the component won't need to be invoked with index, it's just to put all the files in the correct place)
20
+ - tip: if you forget to generate the backing class (via `--gc`), you can add it with `ember generate component-class hds/COMPONENT-NAME/index`.
21
+ - if it's a child component, then `hds/COMPONENT_NAME/CHILD_NAME` instead of `index`
22
+ - tip: if you need to destroy a component and start over again, `ember destroy component hds/COMPONENT-NAME/index` will remove the component and the related files as appropriate.
21
23
  - [ ] **component template**
22
24
  - use semantic HTML
23
25
  - the component should have a css class that is the same as the component (e.g. `hds/button` should have a class name of `hds-button` on the component, and additional CSS classes should start with this same class name.
24
26
  - add `...attributes` unless doing so would be detrimental (e.g., a parent element and child element in the same component that both have ...attributes)
25
27
  - [ ] **component class**
26
28
  - use getters (vs template conditionals or constructors, if possible)
27
- - write API comments in the JS doc way (copy from an existing DS component)
28
- - use the same naming as the Figma file for the components API unless it conflicts with a pre-existing HTML attribute. If that is the case, document the difference in the comment.
29
- - ensure that all existing functionality (from a Structure component) is accounted for in some way. If we are not providing existing functionality at all, it should be documented (along with the reason why). If we are providing temporary functionality, explain that it's temporary and why.
30
- - check the [design system website](https://design-system-website.vercel.app/?path=/story/example-introduction--page) to see what kind of component functionality is being used across all products
29
+ - we have chosen to define the value of the `class` attribute (containing the different CSS class names) for the "root" of the element's code in the backing class, UNLESS that component only has one CSS class name.
30
+ - write API comments in the [JS doc](https://jsdoc.app/) format (look at existing components for examples)
31
+ - use the same naming as the Figma file for the components API, unless it conflicts with a pre-existing HTML attribute. If that is the case, document the difference in a comment.
32
+ - if there is an existing Structure component that is similar to the HDS component being created, ensure that all existing functionality is accounted for in some way. If we are not providing existing functionality at all, it should be documented (along with the reason why). If we are providing temporary functionality, explain that it's temporary and why.
31
33
  - booleans should start with a verb (is/has/etc)
32
- - assertion text should match the content style of the other components.
33
- - goal is a terse invocation
34
+ - assertions should match the content style of the other components, e.g., `'@text for "Hds::Button" must have a valid value'`,
35
+ - program with intent; think about the invocation for the developer who will use the component. The goal is a terse invocation, but we also want to consider the big picture. Try to get feedback when you can.
34
36
  - [ ] **component style**
35
- - create `component/COMPONENT_NAME.scss` in `app/styles`
37
+ - create `component/COMPONENT-NAME.scss` in `app/styles`
36
38
  - add `@use` to `app/styles/@hashicorp/design-system-components.scss` (see existing code for precise syntax)
37
39
  - use design tokens wherever possible, comment where they are not
38
40
  - sizes should be in relative units
@@ -42,12 +44,18 @@ The engineering checklist has six parts: creating the feature branch, component
42
44
  - [ ] test defaults
43
45
  - [ ] try not to repeat tests (i.e., don't have to test all sizes, all colors, etc.)
44
46
  - [ ] test all accessibility attributes
47
+ - [ ] test assertions
45
48
  - [ ] **documentation**
46
49
  - create component page `ember generate route components/COMPONENT_NAME --dummy`
47
50
  - add link to `templates/index.hbs` page
48
- - [ ] API docs
51
+ - [ ] Definition of component (from CRD) (this should be a paragraph under the component name, and before the first section)
52
+ - [ ] Component API
49
53
  - [ ] Usage
54
+ - [ ] Design Guidelines
50
55
  - [ ] Accessibility
56
+ - Conformance Rating [(internal document)](https://docs.google.com/document/d/1OjTKpQLB_wuZSVJNLbbguTzMDMYCHKhNdKQz7-Kfqic/edit#bookmark=id.v7r42vfu4pdd)
57
+ - Known Issues (if any)
58
+ - Related WCAG (pull from CRD or ask Melanie)
51
59
  - [ ] Showcase
52
60
 
53
61
  ### Component Review
@@ -69,5 +77,6 @@ Pre-review request checks:
69
77
  - Edge (once available)
70
78
 
71
79
  When ready for review:
80
+
72
81
  - [ ] add situationally appropriate reviewers
73
82
  - [ ] added instructions for reviewers in your PR, letting them know what kind of review you need
@@ -0,0 +1,27 @@
1
+ <Hds::Disclosure class="hds-dropdown">
2
+ <:toggle as |t|>
3
+ {{yield
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)
7
+ )
8
+ }}
9
+ </:toggle>
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
+ >
15
+ {{yield
16
+ (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")
23
+ )
24
+ }}
25
+ </ul>
26
+ </:content>
27
+ </Hds::Disclosure>
@@ -0,0 +1,84 @@
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}}
@@ -0,0 +1,120 @@
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
+ }
@@ -0,0 +1,23 @@
1
+ {{!
2
+ // >>>>>>>>>>> WARNING <<<<<<<<<<
3
+ //
4
+ // Notice: in this component we're directly using the `Hds::Button` component
5
+ // (and adding a specialized class for the "toggle-button" variant, see below)
6
+ // If you need to change the styling of the `Button` component, remember that this will impact also
7
+ // this component too.
8
+ // If instead you need to change only the styling of the `toggle-button` component, you can do it
9
+ // in the CSS file using the specialized class declared there.
10
+ // This is NOT a standard approach that we use in the HDS design system implementation, but it's been
11
+ // the least worst option we could find to solve the problem of sharing the exact same style of the
12
+ // `Button (primary)` with other components.
13
+ }}
14
+
15
+ <Hds::Button
16
+ @text={{this.text}}
17
+ @icon="chevron-{{if @isOpen 'up' 'down'}}"
18
+ @iconPosition="trailing"
19
+ @color={{this.color}}
20
+ class="hds-dropdown-toggle--with-button-component"
21
+ {{on "click" this.onClick}}
22
+ ...attributes
23
+ />
@@ -0,0 +1,59 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export const DEFAULT_COLOR = 'primary';
5
+ export const COLORS = ['primary', 'secondary'];
6
+
7
+ export default class HdsDropdownToggleButtonComponent extends Component {
8
+ /**
9
+ * @param text
10
+ * @type {string}
11
+ * @description The text of the button. 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::ToggleButton" 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 button to be used; acceptable values are `primary` and `secondary`
29
+ */
30
+ get color() {
31
+ let { color = DEFAULT_COLOR } = this.args;
32
+
33
+ assert(
34
+ `@color for "Hds::Dropdown::ToggleButton" 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
+ * @param onClick
45
+ * @type {function}
46
+ * @default () => {}
47
+ */
48
+ get onClick() {
49
+ let { onClick } = this.args;
50
+
51
+ // notice: this is a guard used in case the toggle is used as standalone element (eg. in the showcase)
52
+ // in reality it's always used inside the Dropdown main component as yielded component, so the onClick handler is always defined
53
+ if (typeof onClick === 'function') {
54
+ return onClick;
55
+ } else {
56
+ return () => {};
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,22 @@
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>
@@ -0,0 +1,59 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+
4
+ export default class HdsDropdownToggleIconComponent extends Component {
5
+ /**
6
+ * @param text
7
+ * @type {string}
8
+ * @description The text of the `aria-label` applied to the toggle
9
+ */
10
+ get text() {
11
+ let { text } = this.args;
12
+
13
+ assert(
14
+ '@text for "Hds::Dropdown::ToggleIcon" must have a valid value',
15
+ text !== undefined
16
+ );
17
+
18
+ return text;
19
+ }
20
+
21
+ /**
22
+ * Indicates if a dropdown chevron icon should be displayed; should be displayed unless the "more-horizontal" icon is used.
23
+ *
24
+ * @param hasChevron
25
+ * @type {boolean}
26
+ * @default true
27
+ */
28
+ get hasChevron() {
29
+ return this.args.hasChevron ?? true;
30
+ }
31
+
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
+ /**
44
+ * @param onClick
45
+ * @type {function}
46
+ * @default () => {}
47
+ */
48
+ get onClick() {
49
+ let { onClick } = this.args;
50
+
51
+ // notice: this is a guard used in case the toggle is used as standalone element (eg. in the showcase)
52
+ // in reality it's always used inside the Dropdown main component as yielded component, so the onClick handler is always defined
53
+ if (typeof onClick === 'function') {
54
+ return onClick;
55
+ } else {
56
+ return () => {};
57
+ }
58
+ }
59
+ }
@@ -112,19 +112,6 @@ export default class HdsLinkCtaComponent extends Component {
112
112
  return this.args.isFullWidth ?? false;
113
113
  }
114
114
 
115
- @action
116
- didInsert(el) {
117
- // we need to register the element to compare it with the one that triggered the "key/space" event
118
- this.el = el;
119
- }
120
-
121
- @action
122
- onKeySpace(event) {
123
- if (event.target === this.el) {
124
- event.target.click();
125
- }
126
- }
127
-
128
115
  /**
129
116
  * Get the class names to apply to the component.
130
117
  * @method classNames
@@ -147,4 +134,17 @@ export default class HdsLinkCtaComponent extends Component {
147
134
 
148
135
  return classes.join(' ');
149
136
  }
137
+
138
+ @action
139
+ didInsert(el) {
140
+ // we need to register the element to compare it with the one that triggered the "key/space" event
141
+ this.el = el;
142
+ }
143
+
144
+ @action
145
+ onKeySpace(event) {
146
+ if (event.target === this.el) {
147
+ event.target.click();
148
+ }
149
+ }
150
150
  }
@@ -127,19 +127,6 @@ export default class HdsLinkToCtaComponent extends Component {
127
127
  return route;
128
128
  }
129
129
 
130
- @action
131
- didInsert(el) {
132
- // we need to register the element to compare it with the one that triggered the "key/space" event
133
- this.el = el;
134
- }
135
-
136
- @action
137
- onKeySpace(event) {
138
- if (event.target === this.el) {
139
- event.target.click();
140
- }
141
- }
142
-
143
130
  /**
144
131
  * Get the class names to apply to the component.
145
132
  * @method classNames
@@ -162,4 +149,17 @@ export default class HdsLinkToCtaComponent extends Component {
162
149
 
163
150
  return classes.join(' ');
164
151
  }
152
+
153
+ @action
154
+ didInsert(el) {
155
+ // we need to register the element to compare it with the one that triggered the "key/space" event
156
+ this.el = el;
157
+ }
158
+
159
+ @action
160
+ onKeySpace(event) {
161
+ if (event.target === this.el) {
162
+ event.target.click();
163
+ }
164
+ }
165
165
  }
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/index';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-button';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-icon';
@@ -11,6 +11,7 @@
11
11
  @use "../components/button";
12
12
  @use "../components/card";
13
13
  @use "../components/disclosure";
14
+ @use "../components/dropdown";
14
15
  @use "../components/icon-tile";
15
16
  @use "../components/link/cta";
16
17
  @use "../components/link/standalone";
@@ -2,6 +2,7 @@
2
2
  // BREADCRUMB
3
3
  //
4
4
  // properties within each class are sorted alphabetically
5
+ // notice: pseudo-classes for the states *must* follow the order link > visited > hover > focus > active
5
6
  //
6
7
  //
7
8
 
@@ -75,9 +76,6 @@ $hds-breadcrumb-item-visual-horizontal-padding: 4px;
75
76
  // notice: the text decoration is applied directly to the "text" container because of a bug in Safari (see https://github.com/hashicorp/design-system-components/issues/159)
76
77
  text-decoration-color: transparent;
77
78
 
78
- // we apply the focus directly to the element, without using a pseudo-element
79
- @include hds-focus-ring-basic();
80
-
81
79
  &:hover,
82
80
  &.is-hover {
83
81
  color: var(--token-color-palette-neutral-600);
@@ -87,6 +85,9 @@ $hds-breadcrumb-item-visual-horizontal-padding: 4px;
87
85
  }
88
86
  }
89
87
 
88
+ // we apply the focus directly to the element, without using a pseudo-element
89
+ @include hds-focus-ring-basic();
90
+
90
91
  &:active,
91
92
  &.is-active {
92
93
  color: var(--token-color-foreground-secondary);
@@ -153,25 +154,27 @@ $hds-breadcrumb-item-visual-horizontal-padding: 4px;
153
154
  padding: 0;
154
155
  width: $hds-breadcrumb-item-height;
155
156
 
156
- // we apply the focus directly to the element, without using a pseudo-element
157
- @include hds-focus-ring-basic();
158
-
159
157
  &:hover,
160
158
  &.is-hover {
161
159
  border-color: var(--token-color-border-strong);
162
160
  color: var(--token-color-foreground-faint);
163
161
  }
162
+
163
+ // we apply the focus directly to the element, without using a pseudo-element
164
+ @include hds-focus-ring-basic();
165
+
166
+ &:focus,
167
+ &.is-focus {
168
+ background-color: transparent;
169
+ border: none; // important: we need to completely remove the border, of the inner box-shadow of the focus ring will be drawn inside the border
170
+ }
171
+
164
172
  &:active,
165
173
  &.is-active {
166
174
  background-color: var(--token-color-surface-interactive-active);
167
175
  border-color: var(--token-color-border-strong);
168
176
  color: var(--token-color-foreground-primary);
169
177
  }
170
- &:focus,
171
- &.is-focus {
172
- background-color: transparent;
173
- border: none; // important: we need to completely remove the border, of the inner box-shadow of the focus ring will be drawn inside the border
174
- }
175
178
  }
176
179
 
177
180
  .hds-breadcrumb__truncation-content {
@@ -2,8 +2,7 @@
2
2
  // BUTTON COMPONENT
3
3
  //
4
4
  // properties within each class are sorted alphabetically
5
- // notice: pseudo-classes for the states *must* follow the order link > visited > focus > hover > active
6
- // see https://github.com/hashicorp/design-system-components/issues/132
5
+ // notice: pseudo-classes for the states *must* follow the order link > visited > hover > focus > active
7
6
  //
8
7
  //
9
8
 
@@ -145,6 +144,14 @@ $size-props: (
145
144
  box-shadow: var(--token-elevation-low-box-shadow);
146
145
  color: var(--token-color-foreground-high-contrast);
147
146
 
147
+ &:hover,
148
+ &.is-hover {
149
+ background-color: var(--token-color-palette-blue-300);
150
+ border-color: var(--token-color-palette-blue-400);
151
+ color: var(--token-color-foreground-high-contrast);
152
+ cursor: pointer;
153
+ }
154
+
148
155
  &:focus,
149
156
  &.is-focus {
150
157
  background-color: var(--token-color-palette-blue-200);
@@ -163,13 +170,7 @@ $size-props: (
163
170
  top: -$shift;
164
171
  }
165
172
  }
166
- &:hover,
167
- &.is-hover {
168
- background-color: var(--token-color-palette-blue-300);
169
- border-color: var(--token-color-palette-blue-400);
170
- color: var(--token-color-foreground-high-contrast);
171
- cursor: pointer;
172
- }
173
+
173
174
  &:active,
174
175
  &.is-active {
175
176
  background-color: var(--token-color-palette-blue-400);
@@ -188,6 +189,14 @@ $size-props: (
188
189
  box-shadow: var(--token-elevation-low-box-shadow);
189
190
  color: var(--token-color-foreground-primary);
190
191
 
192
+ &:hover,
193
+ &.is-hover {
194
+ background-color: var(--token-color-surface-primary);
195
+ border-color: var(--token-color-border-strong);
196
+ color: var(--token-color-foreground-primary);
197
+ cursor: pointer;
198
+ }
199
+
191
200
  &:focus,
192
201
  &.is-focus {
193
202
  background-color: var(--token-color-surface-faint);
@@ -197,13 +206,7 @@ $size-props: (
197
206
  border-color: var(--token-color-focus-action-external);
198
207
  }
199
208
  }
200
- &:hover,
201
- &.is-hover {
202
- background-color: var(--token-color-surface-primary);
203
- border-color: var(--token-color-border-strong);
204
- color: var(--token-color-foreground-primary);
205
- cursor: pointer;
206
- }
209
+
207
210
  &:active,
208
211
  &.is-active {
209
212
  background-color: var(--token-color-surface-interactive-active);
@@ -221,6 +224,14 @@ $size-props: (
221
224
  border-color: transparent;
222
225
  color: var(--token-color-foreground-action);
223
226
 
227
+ &:hover,
228
+ &.is-hover {
229
+ background-color: var(--token-color-surface-primary);
230
+ border-color: var(--token-color-border-strong);
231
+ color: var(--token-color-foreground-action-hover);
232
+ cursor: pointer;
233
+ }
234
+
224
235
  &:focus,
225
236
  &.is-focus {
226
237
  border-color: var(--token-color-focus-action-internal);
@@ -229,13 +240,7 @@ $size-props: (
229
240
  border-color: var(--token-color-focus-action-external);
230
241
  }
231
242
  }
232
- &:hover,
233
- &.is-hover {
234
- background-color: var(--token-color-surface-primary);
235
- border-color: var(--token-color-border-strong);
236
- color: var(--token-color-foreground-action-hover);
237
- cursor: pointer;
238
- }
243
+
239
244
  &:active,
240
245
  &.is-active {
241
246
  background-color: var(--token-color-surface-interactive-active);
@@ -271,6 +276,14 @@ $size-props: (
271
276
  box-shadow: var(--token-elevation-low-box-shadow);
272
277
  color: var(--token-color-foreground-critical-on-surface);
273
278
 
279
+ &:hover,
280
+ &.is-hover {
281
+ background-color: var(--token-color-palette-red-300);
282
+ border-color: var(--token-color-palette-red-400);
283
+ color: var(--token-color-foreground-high-contrast);
284
+ cursor: pointer;
285
+ }
286
+
274
287
  &:focus,
275
288
  &.is-focus {
276
289
  background-color: var(--token-color-surface-critical);
@@ -280,13 +293,6 @@ $size-props: (
280
293
  border-color: var(--token-color-focus-critical-external);
281
294
  }
282
295
  }
283
- &:hover,
284
- &.is-hover {
285
- background-color: var(--token-color-palette-red-300);
286
- border-color: var(--token-color-palette-red-400);
287
- color: var(--token-color-foreground-high-contrast);
288
- cursor: pointer;
289
- }
290
296
 
291
297
  &:active,
292
298
  &.is-active {
@@ -0,0 +1,391 @@
1
+ // >>>>>>>>>> WARNING <<<<<<<<<<<
2
+ //
3
+ // Notice: in this component we're using directly the `Hds::Button` component
4
+ // (and adding a specialized class for the "toggle-button" variant, see below)
5
+ // If you need to change the styling of the `Button` component, remember that this will impact also
6
+ // this component too.
7
+ // If instead you need to change only the styling of the `toggle-button` component, you can do it here using
8
+ // the specialized class declared below.
9
+ // This is NOT a standard approach that we use in the HDS design system implementation, but it's been
10
+ // the least worst option we could find to solve the problem of sharing the exact same style of the
11
+ // `Button (primary)` with other components.
12
+
13
+ //
14
+ // DROPDOWN COMPONENT
15
+ //
16
+ // properties within each class are sorted alphabetically
17
+ // notice: pseudo-classes for the states *must* follow the order link > visited > hover > focus > active
18
+ //
19
+ //
20
+
21
+ @use '../mixins/focus-ring' as *;
22
+
23
+ // TOGGLE/ICON
24
+ .hds-dropdown-toggle-icon {
25
+ align-items: center;
26
+ background-color: transparent;
27
+ border: 1px solid transparent; // We need this to be transparent for a11y
28
+ border-radius: 5px;
29
+ display: flex;
30
+ height: 36px;
31
+ justify-content: center;
32
+ 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
+ outline-color: transparent; // We need this to be transparent for a11y
34
+ padding: 1px;
35
+ min-width: 36px;
36
+
37
+ &:hover,
38
+ &.is-hover {
39
+ background-color: var(--token-color-surface-interactive);
40
+ border-color: var(--token-color-border-strong);
41
+ cursor: pointer;
42
+ }
43
+
44
+ // this is the :focus
45
+ @include hds-focus-ring-with-pseudo-element($top: -1px, $right: -1px, $bottom: -1px, $left: -1px, $radius: 5px);
46
+
47
+ &:active,
48
+ &.is-active {
49
+ background-color: var(--token-color-surface-interactive-active);
50
+ border-color: var(--token-color-border-strong);
51
+ }
52
+ }
53
+
54
+ .hds-dropdown-toggle-icon__wrapper {
55
+ align-items: center;
56
+ border-radius: 3px; // 5px- 1px padding - 1px border
57
+ display: flex;
58
+ justify-content: center;
59
+ height: 32px;
60
+ padding: 1px;
61
+ width: 32px;
62
+
63
+ img {
64
+ border-radius: inherit;
65
+ object-fit: cover; // this will make sure it's correct even if the item isn't square
66
+ }
67
+ }
68
+
69
+ .hds-dropdown-toggle-icon__chevron {
70
+ margin-left: 0.25rem;
71
+ }
72
+
73
+ // TOGGLE/BUTTON
74
+ .hds-dropdown-toggle--with-button-component {
75
+ box-shadow: none; // we override this to remove the elevation style
76
+
77
+ .hds-button__icon {
78
+ 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`
80
+ }
81
+ }
82
+
83
+
84
+ // LIST
85
+ // UL ELEMENT
86
+ // GOES INSIDE HDS::DISCLOSURE's :content block
87
+
88
+ .hds-dropdown-list {
89
+ background-color: var(--token-color-surface-primary);
90
+ border-radius: 6px;
91
+ box-shadow: var(--token-surface-high-box-shadow);
92
+ list-style: none;
93
+ margin: 0;
94
+ max-width: 25rem;
95
+ min-width: 12.5rem;
96
+ 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
+ }
110
+ }
111
+
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;
122
+ }
123
+
124
+ .hds-dropdown-list-item--generic {
125
+ padding-left: 16px;
126
+ padding-right: 16px;
127
+ }
128
+
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;
136
+ }
137
+
138
+ .hds-dropdown-list-item--separator {
139
+ position: relative;
140
+ height: 4px;
141
+ width: 100%;
142
+
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
+ }
152
+
153
+ .hds-dropdown-list-item__copy-item-title {
154
+ 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
+ padding: 2px 0 4px;
160
+ }
161
+
162
+ .hds-dropdown-list-item--copy-item {
163
+ padding: 10px 16px 12px;
164
+ width: 100%;
165
+
166
+ button {
167
+ background-color: transparent;
168
+ border-radius: 5px;
169
+ border: 1px solid var(--token-color-border-primary);
170
+ color: var(--token-color-foreground-primary);
171
+ display: flex;
172
+ font-family: var(--token-typography-font-stack-code);
173
+ justify-content: space-between;
174
+ padding: 12px 8px;
175
+ width: 100%;
176
+
177
+ &:hover,
178
+ &.is-hover {
179
+ background-color: var(--token-color-surface-interactive-hover);
180
+ cursor: pointer;
181
+ }
182
+
183
+ @include hds-focus-ring-basic();
184
+
185
+ &:focus,
186
+ &.is-focus {
187
+ //TODO this focus is just way too complex
188
+ background-color: var(--token-color-surface-action);
189
+ border-color: var(--token-color-focus-action-internal);
190
+ }
191
+
192
+ &:active,
193
+ &.is-active {
194
+ background-color: var(--token-color-surface-interactive-active);
195
+ }
196
+
197
+ &.is-success {
198
+ border-color: var(--token-color-border-success);
199
+ background-color: var(--token-color-surface-success);
200
+
201
+ .hds-dropdown-list-item__copy-item-icon {
202
+ color: var(--token-color-foreground-success);
203
+ }
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
+ }
221
+ }
222
+ }
223
+
224
+ .hds-dropdown-list-item--interactive {
225
+ position: relative;
226
+ isolation: isolate; // used to create a new stacking context (needed to have the pseudo element below text/icon but not the parent container)
227
+ min-height: 36px;
228
+
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...
231
+ button {
232
+ border: 1px inset transparent; // cause of the extra height
233
+ background-color: transparent;
234
+ width: 100%;
235
+
236
+ &:hover {
237
+ cursor: pointer;
238
+ }
239
+
240
+ }
241
+
242
+ // shared styles for links and buttons
243
+ a, button {
244
+ align-items: center;
245
+ display: flex;
246
+ 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
+ outline-color: transparent;
248
+ padding: 8px 10px 8px 16px;
249
+ text-decoration: none;
250
+
251
+ // this is used for the left "hover" indicator
252
+ &::before {
253
+ border-radius: 1px;
254
+ bottom: 6px;
255
+ content: '';
256
+ left: 0.25rem;
257
+ position: absolute;
258
+ top: 6px;
259
+ width: 2px;
260
+ z-index: -1;
261
+ }
262
+
263
+ // Notice: this is used for the active/focus states which have very specific positions
264
+ // and also has a background color, so we can't use the existing focus-ring mixins
265
+ &::after {
266
+ border-radius: 5px;
267
+ bottom: 0px;
268
+ content: '';
269
+ left: 0.625rem;
270
+ position: absolute;
271
+ right: 0.25rem;
272
+ z-index: -1;
273
+ top: 0;
274
+ }
275
+
276
+ // Notice: to avoid too much duplication we define two local CSS variables
277
+ // and define their values in the color variants below
278
+
279
+ // default focus for browsers that still rely on ":focus"
280
+ &:focus,
281
+ &.is-focus {
282
+ &::after {
283
+ background-color: var(--current-background-color);
284
+ box-shadow: var(--current-focus-ring-box-shadow);
285
+ left: 0.25rem;
286
+ }
287
+ }
288
+ // undo the previous declaration for browsers that support ":focus-visible" but wouldn't normally show default focus styles
289
+ &:focus:not(:focus-visible) {
290
+ &::after {
291
+ box-shadow: none;
292
+ }
293
+ }
294
+ // set focus for browsers that support ":focus-visible"
295
+ &:focus-visible {
296
+ &::after {
297
+ background-color: var(--current-background-color);
298
+ box-shadow: var(--current-focus-ring-box-shadow);
299
+ left: 0.25rem;
300
+ }
301
+ }
302
+ // remove the focus ring on "active + focused" state (by design)
303
+ &:focus:active,
304
+ &.is-focus.is-active {
305
+ &::after {
306
+ box-shadow: none;
307
+ }
308
+ }
309
+ }
310
+ }
311
+
312
+ .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
+ text-align: left; // the button element was centering text
318
+ }
319
+
320
+ .hds-dropdown-list-item__interactive-icon {
321
+ margin-right: 0.5rem;
322
+ }
323
+
324
+ .hds-dropdown-list-item--color-action {
325
+ a, button {
326
+ color: var(--token-color-foreground-primary);
327
+
328
+ // assign the values to the local CSS variables used above
329
+ &::after {
330
+ --current-background-color: var(--token-color-surface-action);
331
+ --current-focus-ring-box-shadow: var(
332
+ --token-focus-ring-action-box-shadow
333
+ );
334
+ }
335
+
336
+ &:hover,
337
+ &.is-hover {
338
+ color: var(--token-color-foreground-action-hover);
339
+ &::before {
340
+ background-color: currentColor;
341
+ }
342
+ }
343
+
344
+ &:active,
345
+ &.is-active {
346
+ color: var(--token-color-foreground-action-active);
347
+ &::before {
348
+ background-color: currentColor;
349
+ }
350
+ &::after {
351
+ background-color: var(--token-color-surface-action);
352
+ }
353
+ }
354
+ }
355
+ }
356
+
357
+ .hds-dropdown-list-item--color-critical {
358
+ a, button {
359
+ color: var(--token-color-foreground-critical);
360
+
361
+ // assign the values to the local CSS variables used above
362
+ &::after {
363
+ --current-background-color: var(--token-color-surface-critical);
364
+ --current-focus-ring-box-shadow: var(
365
+ --token-focus-ring-critical-box-shadow
366
+ );
367
+ }
368
+
369
+ &:hover,
370
+ &.is-hover {
371
+ color: var(
372
+ --token-color-palette-red-300
373
+ ); // TODO we need to add this token to the design system
374
+ &::before {
375
+ background-color: currentColor;
376
+ }
377
+ }
378
+ &:active,
379
+ &.is-active {
380
+ color: var(
381
+ --token-color-palette-red-400
382
+ ); // TODO we need to add this token to the design system
383
+ &::before {
384
+ background-color: currentColor;
385
+ }
386
+ &::after {
387
+ background-color: var(--token-color-surface-critical);
388
+ }
389
+ }
390
+ }
391
+ }
@@ -2,8 +2,7 @@
2
2
  // LINK > STANDALONE COMPONENT
3
3
  //
4
4
  // properties within each class are sorted alphabetically
5
- // notice: pseudo-classes for the states *must* follow the order link > visited > focus > hover > active
6
- // see https://github.com/hashicorp/design-system-components/issues/132
5
+ // notice: pseudo-classes for the states *must* follow the order link > visited > hover > focus > active
7
6
  //
8
7
  //
9
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashicorp/design-system-components",
3
- "version": "0.8.0",
3
+ "version": "0.9.2",
4
4
  "description": "HashiCorp Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@hashicorp/design-system-tokens": "^0.8.0",
38
- "@hashicorp/ember-flight-icons": "^2.0.1",
38
+ "@hashicorp/ember-flight-icons": "^2.0.5",
39
39
  "ember-auto-import": "^2.2.3",
40
40
  "ember-cli-babel": "^7.26.6",
41
41
  "ember-cli-htmlbars": "^5.7.1",