@hashicorp/design-system-components 1.3.1 → 1.4.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 (38) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +3 -2
  3. package/addon/components/hds/alert/index.hbs +1 -3
  4. package/addon/components/hds/dismiss-button/index.hbs +3 -0
  5. package/addon/components/hds/form/radio-card/group.hbs +1 -0
  6. package/addon/components/hds/form/radio-card/index.js +33 -0
  7. package/addon/components/hds/interactive/index.hbs +12 -13
  8. package/addon/components/hds/modal/body.hbs +3 -0
  9. package/addon/components/hds/modal/footer.hbs +3 -0
  10. package/addon/components/hds/modal/header.hbs +14 -0
  11. package/addon/components/hds/modal/index.hbs +15 -0
  12. package/addon/components/hds/modal/index.js +144 -0
  13. package/addon/components/hds/table/index.hbs +54 -0
  14. package/addon/components/hds/table/index.js +106 -0
  15. package/addon/components/hds/table/td.hbs +3 -0
  16. package/addon/components/hds/table/th-sort.hbs +8 -0
  17. package/addon/components/hds/table/th-sort.js +44 -0
  18. package/addon/components/hds/table/th.hbs +3 -0
  19. package/addon/components/hds/table/tr.hbs +3 -0
  20. package/addon/components/hds/tabs/tab.hbs +1 -1
  21. package/app/components/hds/dismiss-button/index.js +1 -0
  22. package/app/components/hds/modal/body.js +1 -0
  23. package/app/components/hds/modal/footer.js +1 -0
  24. package/app/components/hds/modal/header.js +1 -0
  25. package/app/components/hds/modal/index.js +1 -0
  26. package/app/components/hds/table/index.js +1 -0
  27. package/app/components/hds/table/td.js +1 -0
  28. package/app/components/hds/table/th-sort.js +1 -0
  29. package/app/components/hds/table/th.js +1 -0
  30. package/app/components/hds/table/tr.js +1 -0
  31. package/app/styles/@hashicorp/design-system-components.scss +3 -0
  32. package/app/styles/components/alert.scss +0 -27
  33. package/app/styles/components/dismiss-button.scss +34 -0
  34. package/app/styles/components/form/radio-card.scss +10 -1
  35. package/app/styles/components/modal.scss +134 -0
  36. package/app/styles/components/table.scss +177 -0
  37. package/index.js +11 -0
  38. package/package.json +7 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#631](https://github.com/hashicorp/design-system/pull/631) [`5d4b1811`](https://github.com/hashicorp/design-system/commit/5d4b1811a4bcdfe0c2205767aceead9286f5f159) Thanks [@alex-ju](https://github.com/alex-ju)! - Add `Modal` component and `DismissButton` utility component (used by `Alert`, `Toast` and `Modal`)
8
+
9
+ * [#722](https://github.com/hashicorp/design-system/pull/722) [`58a52103`](https://github.com/hashicorp/design-system/commit/58a521034b0e6a2a421b4c8b79f26a431e13a83b) Thanks [@MelSumner](https://github.com/MelSumner)! - Add `Table` component
10
+
11
+ ### Patch Changes
12
+
13
+ - [#681](https://github.com/hashicorp/design-system/pull/681) [`6f08ddd2`](https://github.com/hashicorp/design-system/commit/6f08ddd2b491ed13b60e153aa4cc13db8c3884da) Thanks [@KristinLBradley](https://github.com/KristinLBradley)! - Explicitly set `aria-selected` to `true` or `false`
14
+
15
+ * [#698](https://github.com/hashicorp/design-system/pull/698) [`db8a1caf`](https://github.com/hashicorp/design-system/commit/db8a1caff4553ed3240c0260a831526fd2fe6844) Thanks [@alex-ju](https://github.com/alex-ju)! - Add `@layout` parameter to `RadioCard`
16
+
17
+ * Updated dependencies [[`aeff4e02`](https://github.com/hashicorp/design-system/commit/aeff4e02e3c5c738104be326569c110dc2f79618)]:
18
+ - @hashicorp/ember-flight-icons@3.0.2
19
+
20
+ ## 1.3.2
21
+
22
+ ### Patch Changes
23
+
24
+ - [#668](https://github.com/hashicorp/design-system/pull/668) [`3c3b6706`](https://github.com/hashicorp/design-system/commit/3c3b67061d3850721525a624c14bc88ee72e32a1) Thanks [@alex-ju](https://github.com/alex-ju)! - Fix whitespace issue in `Link::Inline` and `Interactive` utility component
25
+
3
26
  ## 1.3.1
4
27
 
5
28
  ### Patch Changes
package/README.md CHANGED
@@ -9,8 +9,9 @@ A package containing the components for the HashiCorp Design System.
9
9
  Compatibility
10
10
  ------------------------------------------------------------------------------
11
11
 
12
- * Ember.js v3.24 or above
13
- * Ember CLI v3.24 or above
12
+ * Ember.js v3.28 or above
13
+ * Note: The library _should_ work with earlier versions of Ember, but we only test with Ember 3.28 and newer
14
+ * Ember CLI v3.28 or above
14
15
  * Node.js v12 or above
15
16
 
16
17
 
@@ -29,8 +29,6 @@
29
29
  </div>
30
30
 
31
31
  {{#if this.onDismiss}}
32
- <button class="hds-alert__dismiss" type="button" aria-label="Dismiss" {{on "click" this.onDismiss}}>
33
- <FlightIcon @name="x" @size="16" @isInlineBlock={{false}} />
34
- </button>
32
+ <Hds::DismissButton class="hds-alert__dismiss" {{on "click" this.onDismiss}} />
35
33
  {{/if}}
36
34
  </div>
@@ -0,0 +1,3 @@
1
+ <button class="hds-dismiss-button" type="button" aria-label="Dismiss" ...attributes>
2
+ <FlightIcon @name="x" @size="16" @isInlineBlock={{false}} />
3
+ </button>
@@ -18,6 +18,7 @@
18
18
  name=@name
19
19
  alignment=@alignment
20
20
  controlPosition=@controlPosition
21
+ layout=@layout
21
22
  extraAriaDescribedBy=F.ariaDescribedBy
22
23
  )
23
24
  )
@@ -7,8 +7,10 @@ import { schedule } from '@ember/runloop';
7
7
 
8
8
  export const DEFAULT_CONTROL_POSITION = 'bottom';
9
9
  export const DEFAULT_ALIGNMENT = 'left';
10
+ export const DEFAULT_LAYOUT = 'fluid';
10
11
  export const CONTROL_POSITIONS = ['bottom', 'left'];
11
12
  export const ALIGNMENTS = ['left', 'center'];
13
+ export const LAYOUTS = ['fluid', 'fixed'];
12
14
 
13
15
  export default class HdsFormRadioCardIndexComponent extends Component {
14
16
  @tracked ariaDescribedBy = this.args.extraAriaDescribedBy;
@@ -64,6 +66,34 @@ export default class HdsFormRadioCardIndexComponent extends Component {
64
66
  return alignment;
65
67
  }
66
68
 
69
+ /**
70
+ * Sets the layout of the card within the group
71
+ * Accepted values: fluid, fixed
72
+ *
73
+ * @param layout
74
+ * @type {string}
75
+ * @default 'fluid'
76
+ */
77
+ get layout() {
78
+ let { layout = DEFAULT_LAYOUT } = this.args;
79
+
80
+ assert(
81
+ `@layout for "Hds::Form::RadioCard" must be one of the following: ${LAYOUTS.join(
82
+ ', '
83
+ )}; received: ${layout}`,
84
+ LAYOUTS.includes(layout)
85
+ );
86
+
87
+ // if the `@layout` is set to 'fixed' we need a `@maxWidth` value to constrain the card to
88
+ if (layout === 'fixed') {
89
+ assert(
90
+ `@maxWidth for "Hds::Form::RadioCard" with @layout "fixed" is required`,
91
+ this.args.maxWidth
92
+ );
93
+ }
94
+ return layout;
95
+ }
96
+
67
97
  /**
68
98
  * Get the class names to apply to the component.
69
99
  * @method classNames
@@ -85,6 +115,9 @@ export default class HdsFormRadioCardIndexComponent extends Component {
85
115
  // add a class based on the @alignment argument
86
116
  classes.push(`hds-form-radio-card--align-${this.alignment}`);
87
117
 
118
+ // add a class based on the @layout argument
119
+ classes.push(`hds-form-radio-card--layout-${this.layout}`);
120
+
88
121
  return classes.join(' ');
89
122
  }
90
123
  }
@@ -1,7 +1,8 @@
1
1
  {{! IMPORTANT: we removed the newlines before/after the yield to reduce the issues with unexpected whitespaces (see https://github.com/hashicorp/design-system/pull/231#issuecomment-1123502499) }}
2
+ {{! IMPORTANT: we need to add "squishies" here (~) because otherwise the whitespace added by Ember becomes visible in the link (being an inline element) - See https://handlebarsjs.com/guide/expressions.html#whitespace-control }}
2
3
  {{! NOTICE: we can't support the direct use of the "href" HTML attribute via ...attributes in the <a> elements, because we need to rely on the "@href" Ember argument to differentiate between different types of generated output }}
3
- {{#if @route}}
4
- {{#if this.isRouteExternal}}
4
+ {{~#if @route~}}
5
+ {{~#if this.isRouteExternal~}}
5
6
  <LinkToExternal
6
7
  @current-when={{@current-when}}
7
8
  @models={{hds-link-to-models @model @models}}
@@ -10,7 +11,7 @@
10
11
  @route={{@route}}
11
12
  ...attributes
12
13
  >{{yield}}</LinkToExternal>
13
- {{else}}
14
+ {{~else~}}
14
15
  <LinkTo
15
16
  @current-when={{@current-when}}
16
17
  @models={{hds-link-to-models @model @models}}
@@ -19,15 +20,13 @@
19
20
  @route={{@route}}
20
21
  ...attributes
21
22
  >{{yield}}</LinkTo>
22
- {{/if}}
23
- {{else if @href}}
24
- {{#if this.isHrefExternal}}
23
+ {{~/if~}}
24
+ {{~else if @href~}}
25
+ {{~#if this.isHrefExternal~}}
25
26
  <a target="_blank" rel="noopener noreferrer" ...attributes href={{@href}} {{on "keyup" this.onKeyUp}}>{{yield}}</a>
26
- {{else}}
27
+ {{~else~}}
27
28
  <a ...attributes href={{@href}} {{on "keyup" this.onKeyUp}}>{{yield}}</a>
28
- {{/if}}
29
- {{else}}
30
- <button type="button" ...attributes>
31
- {{yield}}
32
- </button>
33
- {{/if}}
29
+ {{~/if~}}
30
+ {{~else~}}
31
+ <button type="button" ...attributes>{{yield}}</button>
32
+ {{~/if~}}
@@ -0,0 +1,3 @@
1
+ <div class="hds-modal__body" ...attributes>
2
+ {{yield}}
3
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="hds-modal__footer" ...attributes>
2
+ {{yield (hash close=@onDismiss)}}
3
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="hds-modal__header" ...attributes>
2
+ {{#if @icon}}
3
+ <FlightIcon class="hds-modal__icon" @name={{@icon}} @size="24" @isInlineBlock={{false}} />
4
+ {{/if}}
5
+ <div class="hds-modal__title hds-typography-display-300 hds-font-weight-semibold" id={{@id}}>
6
+ {{#if @tagline}}
7
+ <div class="hds-modal__tagline hds-typography-body-100 hds-font-weight-regular">
8
+ {{@tagline}}
9
+ </div>
10
+ {{/if}}
11
+ {{yield}}
12
+ </div>
13
+ <Hds::DismissButton class="hds-modal__dismiss" {{on "click" @onDismiss}} />
14
+ </div>
@@ -0,0 +1,15 @@
1
+ {{! template-lint-disable no-invalid-interactive }}
2
+ <dialog
3
+ class={{this.classNames}}
4
+ ...attributes
5
+ aria-labelledby={{this.id}}
6
+ {{did-insert this.didInsert}}
7
+ {{focus-trap isActive=this.isOpen focusTrapOptions=(hash onDeactivate=this.onDismiss clickOutsideDeactivates=true)}}
8
+ >
9
+ {{yield (hash Header=(component "hds/modal/header" id=this.id onDismiss=this.onDismiss))}}
10
+ {{yield (hash Body=(component "hds/modal/body"))}}
11
+ {{yield (hash Footer=(component "hds/modal/footer" onDismiss=this.onDismiss))}}
12
+ </dialog>
13
+ {{#if this.isOpen}}
14
+ <div class="hds-modal__overlay"></div>
15
+ {{/if}}
@@ -0,0 +1,144 @@
1
+ import Component from '@glimmer/component';
2
+ import { action } from '@ember/object';
3
+ import { assert } from '@ember/debug';
4
+ import { getElementId } from '../form/utils/getElementId';
5
+ import { tracked } from '@glimmer/tracking';
6
+
7
+ export const DEFAULT_SIZE = 'medium';
8
+ export const DEFAULT_COLOR = 'neutral';
9
+ export const SIZES = ['small', 'medium', 'large'];
10
+ export const COLORS = ['neutral', 'warning', 'critical'];
11
+
12
+ export default class HdsModalIndexComponent extends Component {
13
+ @tracked isOpen = false;
14
+ @tracked isDismissDisabled = this.args.isDismissDisabled ?? false;
15
+
16
+ /**
17
+ * Sets the size of the modal dialog
18
+ * Accepted values: small, medium, large
19
+ *
20
+ * @param size
21
+ * @type {string}
22
+ * @default 'medium'
23
+ */
24
+ get size() {
25
+ let { size = DEFAULT_SIZE } = this.args;
26
+
27
+ assert(
28
+ `@size for "Hds::Modal" must be one of the following: ${SIZES.join(
29
+ ', '
30
+ )}; received: ${size}`,
31
+ SIZES.includes(size)
32
+ );
33
+
34
+ return size;
35
+ }
36
+
37
+ /**
38
+ * Sets the color of the modal dialog
39
+ * Accepted values: neutral, warning, critical
40
+ *
41
+ * @param color
42
+ * @type {string}
43
+ * @default 'neutral'
44
+ */
45
+ get color() {
46
+ let { color = DEFAULT_COLOR } = this.args;
47
+
48
+ assert(
49
+ `@color for "Hds::Modal" must be one of the following: ${COLORS.join(
50
+ ', '
51
+ )}; received: ${color}`,
52
+ COLORS.includes(color)
53
+ );
54
+
55
+ return color;
56
+ }
57
+
58
+ /**
59
+ * Calculates the unique ID to assign to the title
60
+ */
61
+ get id() {
62
+ return getElementId(this);
63
+ }
64
+
65
+ /**
66
+ * Get the class names to apply to the component.
67
+ * @method classNames
68
+ * @return {string} The "class" attribute to apply to the component.
69
+ */
70
+ get classNames() {
71
+ let classes = ['hds-modal'];
72
+
73
+ // add a class based on the @size argument
74
+ classes.push(`hds-modal--size-${this.size}`);
75
+
76
+ // add a class based on the @color argument
77
+ classes.push(`hds-modal--color-${this.color}`);
78
+
79
+ return classes.join(' ');
80
+ }
81
+
82
+ @action
83
+ didInsert(element) {
84
+ // Store a reference of the `<dialog>` element
85
+ this.element = element;
86
+
87
+ // Register `<dialog>` element for polyfilling if no native support is available
88
+ if (!element.showModal) {
89
+ Promise.all([import('dialog-polyfill'), import('dialog-polyfill-css')])
90
+ .then(([dialogPolyfill]) => {
91
+ const dialog = dialogPolyfill.default;
92
+ if (dialog.registerDialog) {
93
+ dialog.registerDialog(element);
94
+ // This unscoped class is defined in the dialog polyfill: https://github.com/GoogleChrome/dialog-polyfill/blob/master/dist/dialog-polyfill.css#L33
95
+ element.classList.add('fixed');
96
+ }
97
+ })
98
+ .catch({});
99
+ }
100
+
101
+ // Register "onClose" callback function to be called when a native 'close' event is dispatched
102
+ this.element.addEventListener('close', () => {
103
+ if (this.args.onClose && typeof this.args.onClose === 'function') {
104
+ this.args.onClose();
105
+ }
106
+
107
+ // If the dismissal of the modal is disabled, we keep the modal open/visible otherwise we mark it as closed
108
+ if (this.isDismissDisabled) {
109
+ // If, in a chain of events, the element is not attached to the DOM, the `showModal` would fail
110
+ // so we add this safeguard condition that checks for the `<dialog>` to have a parent
111
+ if (this.element.parentElement) {
112
+ // As there is no way to `preventDefault` on `close` events, we call the `showModal` function
113
+ // preserving the state of the modal dialog
114
+ this.element.showModal();
115
+ }
116
+ } else {
117
+ this.isOpen = false;
118
+ }
119
+ });
120
+
121
+ // If the modal dialog is not already open
122
+ if (!this.element.open) {
123
+ this.open();
124
+ }
125
+ }
126
+
127
+ @action
128
+ open() {
129
+ // Make modal dialog visible using the native `showModal` method
130
+ this.element.showModal();
131
+ this.isOpen = true;
132
+
133
+ // Call "onOpen" callback function
134
+ if (this.args.onOpen && typeof this.args.onOpen === 'function') {
135
+ this.args.onOpen();
136
+ }
137
+ }
138
+
139
+ @action
140
+ onDismiss() {
141
+ // Make modal dialog invisible using the native `close` method
142
+ this.element.close();
143
+ }
144
+ }
@@ -0,0 +1,54 @@
1
+ <table class={{this.classNames}} ...attributes>
2
+ {{#if @columns}}
3
+ <caption class="sr-only" aria-live="polite">{{@caption}} {{this.sortedMessageText}}</caption>
4
+ {{else if @caption}}
5
+ <caption class="sr-only">{{@caption}}</caption>
6
+ {{/if}}
7
+ <thead class="hds-table__thead">
8
+ {{#if @columns}}
9
+ <Hds::Table::Tr>
10
+ {{#each @columns as |column|}}
11
+ {{#if @sortingKeys}}
12
+ {{#if (includes column.key @sortingKeys)}}
13
+ <Hds::Table::ThSort
14
+ @isSorted={{eq column.key this.sortBy}}
15
+ @sortOrder={{this.sortOrder}}
16
+ @onClick={{(fn this.setSortBy column.key)}}
17
+ >
18
+ {{column.label}}
19
+ </Hds::Table::ThSort>
20
+ {{else}}
21
+ <Hds::Table::Th>{{column.label}}</Hds::Table::Th>
22
+ {{/if}}
23
+ {{else}}
24
+ <Hds::Table::ThSort
25
+ @isSorted={{eq column.key this.sortBy}}
26
+ @sortOrder={{this.sortOrder}}
27
+ @onClick={{(fn this.setSortBy column.key)}}
28
+ >
29
+ {{column.label}}
30
+ </Hds::Table::ThSort>
31
+ {{/if}}
32
+ {{/each}}
33
+ </Hds::Table::Tr>
34
+ {{else}}
35
+ {{yield
36
+ (hash Tr=(component "hds/table/tr") Th=(component "hds/table/th") ThSort=(component "hds/table/th-sort"))
37
+ to="head"
38
+ }}
39
+ {{/if}}
40
+ </thead>
41
+ <tbody class="hds-table__tbody">
42
+ {{#if @columns}}
43
+ {{#each (sort-by this.getSortCriteria @model) as |data|}}
44
+ {{yield (hash Tr=(component "hds/table/tr") Td=(component "hds/table/td") data=data) to="body"}}
45
+ {{/each}}
46
+ {{else if @model}}
47
+ {{#each @model as |data|}}
48
+ {{yield (hash Tr=(component "hds/table/tr") Td=(component "hds/table/td") data=data) to="body"}}
49
+ {{/each}}
50
+ {{else}}
51
+ {{yield (hash Tr=(component "hds/table/tr") Td=(component "hds/table/td")) to="body"}}
52
+ {{/if}}
53
+ </tbody>
54
+ </table>
@@ -0,0 +1,106 @@
1
+ import Component from '@glimmer/component';
2
+ import { action } from '@ember/object';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { assert } from '@ember/debug';
5
+
6
+ const DENSITIES = ['short', 'medium', 'tall'];
7
+ const DEFAULT_DENSITY = 'medium';
8
+ const VALIGNMENTS = ['top', 'middle', 'bottom', 'baseline', 'sub', 'text-top'];
9
+ const DEFAULT_VALIGN = 'top';
10
+
11
+ export default class HdsTableIndexComponent extends Component {
12
+ @tracked sortBy = this.args.sortBy;
13
+ @tracked sortOrder = this.args.sortOrder || 'asc';
14
+ @tracked sortedMessageText = '';
15
+
16
+ get getSortCriteria() {
17
+ return `${this.sortBy}:${this.sortOrder}`;
18
+ }
19
+
20
+ /**
21
+ * @param isStriped
22
+ * @type {boolean}
23
+ * @default true
24
+ * @description Determines whether the table rows should have alternating background colors; defaults to true.
25
+ */
26
+ get isStriped() {
27
+ return this.args.isStriped ?? true;
28
+ }
29
+
30
+ /**
31
+ * @param density
32
+ * @type {string}
33
+ * @default 'medium'
34
+ * @description Determines the density of the table cells; options are "short", "medium" and "tall". If no density is defined, "medium" is used.
35
+ */
36
+ get density() {
37
+ let { density = DEFAULT_DENSITY } = this.args;
38
+
39
+ assert(
40
+ `@density for "Hds::Table" must be one of the following: ${DENSITIES.join(
41
+ ', '
42
+ )}; received: ${density}`,
43
+ DENSITIES.includes(density)
44
+ );
45
+
46
+ return density;
47
+ }
48
+
49
+ /**
50
+ * @param valign
51
+ * @type {string}
52
+ * @default 'top'
53
+ * @description Determines the vertical alignment of the table cells; options are all of the values accepted by the CSS "vertical-align" property: "top", "middle", "bottom", "baseline", "sub", and "text-top". If no valign is defined, "top" is used.
54
+ */
55
+ get valign() {
56
+ let { valign = DEFAULT_VALIGN } = this.args;
57
+
58
+ assert(
59
+ `@valign for "Hds::Table" must be one of the following: ${VALIGNMENTS.join(
60
+ ', '
61
+ )}; received: ${valign}`,
62
+ VALIGNMENTS.includes(valign)
63
+ );
64
+
65
+ return valign;
66
+ }
67
+
68
+ /**
69
+ * Get the class names to apply to the component.
70
+ * @method classNames
71
+ * @return {string} The "class" attribute to apply to the component.
72
+ */
73
+ get classNames() {
74
+ let classes = ['hds-table'];
75
+
76
+ // add a class based on the @isStriped argument
77
+ if (this.isStriped) {
78
+ classes.push('hds-table--striped');
79
+ }
80
+
81
+ // add a class based on the @density argument
82
+ if (this.density) {
83
+ classes.push(`hds-table--density-${this.density}`);
84
+ }
85
+
86
+ // add a class based on the @valign argument
87
+ if (this.valign) {
88
+ classes.push(`hds-table--valign-${this.valign}`);
89
+ }
90
+
91
+ return classes.join(' ');
92
+ }
93
+
94
+ @action
95
+ setSortBy(column) {
96
+ if (this.sortBy === column) {
97
+ //invert the sort order
98
+ this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
99
+ } else {
100
+ this.sortBy = column;
101
+ this.sortOrder = 'asc';
102
+ }
103
+ // we should allow the user to define a custom value here (e.g., for i18n) - tracked with HDS-965
104
+ this.sortedMessageText = `Sorted by ${this.sortBy} ${this.sortOrder}ending`;
105
+ }
106
+ }
@@ -0,0 +1,3 @@
1
+ <td class="hds-table__td hds-typography-body-200 hds-font-weight-regular" ...attributes>
2
+ {{yield}}
3
+ </td>
@@ -0,0 +1,8 @@
1
+ <th scope="col" class="hds-table__th-sort" aria-sort={{this.ariaSort}} ...attributes>
2
+ <button type="button" {{on "click" this.onClick}}>
3
+ <div class="hds-table__th-sort--button-content">
4
+ <span class="hds-typography-body-200 hds-font-weight-semibold">{{yield}}</span>
5
+ <FlightIcon @name={{this.icon}} />
6
+ </div>
7
+ </button>
8
+ </th>
@@ -0,0 +1,44 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ const NOOP = () => {};
4
+
5
+ export default class HdsTableThSortComponent extends Component {
6
+ /**
7
+ * @param ariaSort
8
+ * @type {string}
9
+ * @private
10
+ * @description Sets the aria-sort attribute based on the sort order defined
11
+ */
12
+ get ariaSort() {
13
+ if (this.args.isSorted) {
14
+ return this.args.sortOrder === 'asc' ? 'ascending' : 'descending';
15
+ } else {
16
+ return undefined;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * @param icon
22
+ * @type {string}
23
+ * @private
24
+ * @default swap-vertical
25
+ * @description Determines which icon to use based on the sort order defined
26
+ */
27
+ get icon() {
28
+ if (this.args.isSorted && this.args.sortOrder) {
29
+ return this.args.sortOrder === 'asc' ? 'arrow-up' : 'arrow-down';
30
+ } else {
31
+ return 'swap-vertical';
32
+ }
33
+ }
34
+
35
+ /**
36
+ * @param onClick
37
+ * @type {function}
38
+ * @default () => {}
39
+ */
40
+ get onClick() {
41
+ let { onClick } = this.args;
42
+ return onClick || NOOP;
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ <th class="hds-table__th hds-typography-body-200 hds-font-weight-semibold" ...attributes scope="col">
2
+ {{yield}}
3
+ </th>
@@ -0,0 +1,3 @@
1
+ <tr class="hds-table__tr" ...attributes>
2
+ {{yield}}
3
+ </tr>
@@ -9,7 +9,7 @@
9
9
  role="tab"
10
10
  type="button"
11
11
  id={{this.tabId}}
12
- aria-selected={{this.isSelected}}
12
+ aria-selected={{if this.isSelected "true" "false"}}
13
13
  tabindex={{unless this.isSelected "-1"}}
14
14
  data-is-selected={{this.isInitialTab}}
15
15
  {{did-insert this.didInsertNode}}
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/dismiss-button/index';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/modal/body';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/modal/footer';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/modal/header';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/modal/index';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/table/index';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/table/td';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/table/th-sort';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/table/th';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/table/tr';
@@ -16,13 +16,16 @@
16
16
  @use "../components/button-set";
17
17
  @use "../components/card";
18
18
  @use "../components/disclosure";
19
+ @use "../components/dismiss-button";
19
20
  @use "../components/dropdown";
20
21
  @use "../components/empty-state";
21
22
  @use "../components/form"; // multiple components
22
23
  @use "../components/icon-tile";
23
24
  @use "../components/link"; // multiple components
25
+ @use "../components/modal";
24
26
  @use "../components/stepper";
25
27
  @use "../components/tabs";
28
+ @use "../components/table";
26
29
  @use "../components/tag";
27
30
  @use "../components/toast";
28
31
  // END COMPONENT CSS FILES IMPORTS
@@ -2,8 +2,6 @@
2
2
  // ALERT COMPONENT
3
3
  //
4
4
 
5
- @use "../mixins/focus-ring" as *;
6
-
7
5
  .hds-alert {
8
6
  display: flex;
9
7
  align-items: flex-start;
@@ -117,39 +115,14 @@
117
115
  // DISMISS
118
116
 
119
117
  .hds-alert__dismiss {
120
- flex: none;
121
118
  margin-top: 2px; // for alignment with the main icon and text
122
119
  margin-left: 16px;
123
- padding: 0;
124
- color: var(--token-color-foreground-faint);
125
- background-color: transparent;
126
- border: none;
127
- cursor: pointer;
128
120
 
129
121
  .hds-alert--type-compact & {
130
122
  margin-top: 1px;
131
123
  }
132
-
133
- &:hover {
134
- &::before { // we re-use the pseudo-element created by the "focus-ring" mixin
135
- background-color: rgba(#dedfe3, 0.4);
136
- }
137
- }
138
-
139
- // notice: this is used not only for the focus, but also to increase the clickable area
140
- @include hds-focus-ring-with-pseudo-element($top: -4px, $right: -4px, $bottom: -4px, $left: -4px);
141
-
142
- &:active {
143
- color: var(--token-color-foreground-secondary);
144
-
145
- &::before {
146
- background-color: rgba(#dedfe3, 0.4);
147
- border: 1px solid var(--token-color-border-strong);
148
- }
149
- }
150
124
  }
151
125
 
152
-
153
126
  // TYPES
154
127
 
155
128
  .hds-alert--type-page {
@@ -0,0 +1,34 @@
1
+ //
2
+ // DISMISS-BUTTON
3
+ //
4
+
5
+ @use "../mixins/focus-ring" as *;
6
+
7
+ .hds-dismiss-button {
8
+ flex: none;
9
+ padding: 0;
10
+ color: var(--token-color-foreground-faint);
11
+ background-color: transparent;
12
+ border: none;
13
+ cursor: pointer;
14
+
15
+ &:hover,
16
+ &.mock-hover {
17
+ &::before { // we re-use the pseudo-element created by the "focus-ring" mixin
18
+ background-color: rgba(#dedfe3, 0.4);
19
+ }
20
+ }
21
+
22
+ // notice: this is used not only for the focus, but also to increase the clickable area
23
+ @include hds-focus-ring-with-pseudo-element($top: -4px, $right: -4px, $bottom: -4px, $left: -4px);
24
+
25
+ &:active,
26
+ &.mock-active {
27
+ color: var(--token-color-foreground-secondary);
28
+
29
+ &::before {
30
+ background-color: rgba(#dedfe3, 0.4);
31
+ border: 1px solid var(--token-color-border-strong);
32
+ }
33
+ }
34
+ }
@@ -15,9 +15,18 @@
15
15
  }
16
16
 
17
17
  .hds-form-radio-card {
18
- flex: 1;
19
18
  margin: calc(var(--token-form-radiocard-group-gap) / 2);
20
19
  }
20
+
21
+ // LAYOUT
22
+
23
+ .hds-form-radio-card--layout-fluid {
24
+ flex: 1 0 0;
25
+ }
26
+
27
+ .hds-form-radio-card--layout-fixed {
28
+ flex: 1 0 100%;
29
+ }
21
30
  }
22
31
 
23
32
  // RadioCard
@@ -0,0 +1,134 @@
1
+ //
2
+ // MODAL
3
+ //
4
+
5
+ .hds-modal {
6
+ z-index: 50;
7
+ flex-direction: column;
8
+ padding: 0;
9
+ background: var(--token-color-surface-primary);
10
+ border: none;
11
+ border-radius: 10px;
12
+ box-shadow: var(--token-surface-overlay-box-shadow);
13
+
14
+ &[open] {
15
+ position: fixed;
16
+ display: flex;
17
+ }
18
+
19
+ // we hide the native `::backdrop` pseudo-element in favor of using
20
+ // an `hds-modal__overlay` element to detect click events with more ease
21
+ &::backdrop {
22
+ display: none;
23
+ }
24
+ }
25
+
26
+ .hds-modal__overlay {
27
+ position: fixed;
28
+ top: 0;
29
+ right: 0;
30
+ bottom: 0;
31
+ left: 0;
32
+ z-index: 50;
33
+ background: var(--token-color-palette-neutral-700);
34
+ opacity: 0.5;
35
+ }
36
+
37
+ .hds-modal__header {
38
+ display: flex;
39
+ flex: none;
40
+ align-items: flex-start;
41
+ padding: 16px 24px;
42
+ border-top-left-radius: inherit;
43
+ border-top-right-radius: inherit;
44
+ }
45
+
46
+ .hds-modal__icon {
47
+ flex: none;
48
+ align-self: center;
49
+ margin-right: 16px;
50
+ }
51
+
52
+ .hds-modal__title {
53
+ flex-grow: 1;
54
+ }
55
+
56
+ .hds-modal__tagline {
57
+ margin-bottom: 4px;
58
+ }
59
+
60
+ .hds-modal__dismiss {
61
+ align-self: center;
62
+ margin-left: 16px;
63
+ }
64
+
65
+ .hds-modal__body {
66
+ flex: 1 1 auto;
67
+ padding: 24px;
68
+ overflow-y: auto;
69
+ overscroll-behavior: contain;
70
+ }
71
+
72
+ .hds-modal__footer {
73
+ flex: none;
74
+ padding: 16px 24px;
75
+ background: var(--token-color-surface-faint);
76
+ border-top: 1px solid var(--token-color-border-primary);
77
+ border-bottom-right-radius: inherit;
78
+ border-bottom-left-radius: inherit;
79
+
80
+ // Tertiary buttons must always be placed/aligned at the end of the row
81
+ .hds-button-set {
82
+ .hds-button--color-tertiary {
83
+ margin-left: auto;
84
+ }
85
+ }
86
+ }
87
+
88
+ .hds-modal--size-small {
89
+ width: min(400px, 95vw);
90
+ }
91
+
92
+ .hds-modal--size-medium {
93
+ width: min(600px, 95vw);
94
+ }
95
+
96
+ .hds-modal--size-large {
97
+ width: min(800px, 95vw);
98
+ }
99
+
100
+ .hds-modal--color-neutral {
101
+ .hds-modal__header {
102
+ color: var(--token-color-foreground-strong);
103
+ background: var(--token-color-surface-faint);
104
+ border-bottom: 1px solid var(--token-color-border-primary);
105
+ }
106
+
107
+ .hds-modal__tagline {
108
+ color: var(--token-color-foreground-faint);
109
+ }
110
+ }
111
+
112
+ .hds-modal--color-warning {
113
+ .hds-modal__header {
114
+ color: var(--token-color-foreground-warning-on-surface);
115
+ background: var(--token-color-surface-warning);
116
+ border-bottom: 1px solid var(--token-color-border-warning);
117
+ }
118
+
119
+ .hds-modal__tagline {
120
+ color: var(--token-color-foreground-warning-on-surface);
121
+ }
122
+ }
123
+
124
+ .hds-modal--color-critical {
125
+ .hds-modal__header {
126
+ color: var(--token-color-foreground-critical-on-surface);
127
+ background: var(--token-color-surface-critical);
128
+ border-bottom: 1px solid var(--token-color-border-critical);
129
+ }
130
+
131
+ .hds-modal__tagline {
132
+ color: var(--token-color-foreground-critical-on-surface);
133
+ }
134
+ }
@@ -0,0 +1,177 @@
1
+ //
2
+ // TABLE
3
+ //
4
+ //
5
+
6
+ @use "../mixins/focus-ring" as *;
7
+
8
+ $hds-table-border-radius: 6px;
9
+ $hds-table-border-width: 1px;
10
+ $hds-table-inner-border-radius: $hds-table-border-radius - $hds-table-border-width;
11
+ $hds-table-border-color: var(--token-color-border-primary);
12
+ $hds-table-header-height: 48px;
13
+ $hds-table-cell-padding-medium: 12px 16px;
14
+ $hds-table-cell-padding-short: 4px 16px;
15
+ $hds-table-cell-padding-tall: 20px 16px;
16
+
17
+ .hds-table {
18
+ width: 100%;
19
+ table-layout: fixed;
20
+ border: $hds-table-border-width solid $hds-table-border-color;
21
+ border-radius: $hds-table-border-radius;
22
+ border-spacing: 0;
23
+ }
24
+
25
+ // TABLE HEADER
26
+ .hds-table__thead {
27
+ .hds-table__tr {
28
+ color: var(--token-color-foreground-strong);
29
+ background-color: var(--token-color-surface-strong);
30
+
31
+ .hds-table__th,
32
+ .hds-table__th-sort {
33
+ height: $hds-table-header-height;
34
+ text-align: left;
35
+ border-top: none;
36
+ border-right: none;
37
+ border-bottom: $hds-table-border-width solid $hds-table-border-color;
38
+ border-left: none;
39
+ }
40
+
41
+ .hds-table__th {
42
+ padding: $hds-table-cell-padding-medium;
43
+ }
44
+
45
+ .hds-table__th-sort {
46
+ padding: 0;
47
+
48
+ button {
49
+ width: 100%;
50
+ height: 100%;
51
+ min-height: $hds-table-header-height;
52
+ margin: 0;
53
+ padding: $hds-table-cell-padding-medium;
54
+ text-align: inherit;
55
+ background-color: transparent;
56
+ border: 1px solid transparent;
57
+ border-radius: inherit;
58
+
59
+ .hds-table__th-sort--button-content {
60
+ display: flex; // we can't put flex on the button itself so we drop the content into its own container
61
+ align-items: center;
62
+
63
+ .flight-icon {
64
+ flex: none;
65
+ margin-left: 8px;
66
+ color: var(--token-color-foreground-action);
67
+ }
68
+ }
69
+
70
+ &:hover,
71
+ &.mock-hover {
72
+ color: var(--token-color-foreground-strong);
73
+ background-color: var(--token-color-palette-neutral-200);
74
+ cursor: pointer;
75
+ }
76
+
77
+ @include hds-focus-ring-with-pseudo-element($radius: inherit);
78
+
79
+ &:active,
80
+ &.mock-active {
81
+ color: var(--token-color-foreground-strong);
82
+ background-color: var(--token-color-palette-neutral-300);
83
+ }
84
+ }
85
+ }
86
+
87
+ // BORDER RADIUS: TARGET FIRST AND LAST TH ELEMENTS IN THE ROW
88
+ &:first-of-type {
89
+ th:first-child {
90
+ border-top-left-radius: $hds-table-inner-border-radius;
91
+ }
92
+
93
+ // LAST TH IN THE ROW, BORDER RADIUS
94
+ th:last-child {
95
+ border-top-right-radius: $hds-table-inner-border-radius;
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ // TABLE BODY
102
+
103
+ // TABLE STRIPING ROWS
104
+ .hds-table--striped {
105
+ .hds-table__tbody {
106
+ .hds-table__tr:nth-child(even) {
107
+ background-color: var(--token-color-surface-faint);
108
+ }
109
+ }
110
+ }
111
+
112
+ // TABLE DENSITY (TBODY ROW HEIGHT)
113
+ .hds-table--density-short .hds-table__tbody td {
114
+ padding: $hds-table-cell-padding-short;
115
+ }
116
+
117
+ .hds-table--density-medium .hds-table__tbody td {
118
+ padding: $hds-table-cell-padding-medium;
119
+ }
120
+
121
+ .hds-table--density-tall .hds-table__tbody td {
122
+ padding: $hds-table-cell-padding-tall;
123
+ }
124
+
125
+ // TABLE CELL VERTICAL ALIGNMENT
126
+ .hds-table--valign-top .hds-table__tbody td {
127
+ vertical-align: top;
128
+ }
129
+
130
+ .hds-table--valign-middle .hds-table__tbody td {
131
+ vertical-align: middle;
132
+ }
133
+
134
+ .hds-table--valign-bottom .hds-table__tbody td {
135
+ vertical-align: bottom;
136
+ }
137
+
138
+ .hds-table--valign-baseline .hds-table__tbody td {
139
+ vertical-align: baseline;
140
+ }
141
+
142
+ .hds-table--valign-sub .hds-table__tbody td {
143
+ vertical-align: sub;
144
+ }
145
+
146
+ .hds-table--valign-text-top .hds-table__tbody td {
147
+ vertical-align: text-top;
148
+ }
149
+
150
+ .hds-table__tbody {
151
+ .hds-table__tr {
152
+ color: var(--token-color-foreground-primary);
153
+ background-color: var(--token-color-surface-primary);
154
+
155
+ td {
156
+ border-top: none;
157
+ border-right: none;
158
+ border-bottom: $hds-table-border-width solid $hds-table-border-color;
159
+ border-left: none;
160
+ }
161
+
162
+ // BORDER RADIUS: TARGET FIRST AND LAST TD ELEMENTS IN THE LAST ROW
163
+ &:last-of-type {
164
+ td {
165
+ border-bottom: none;
166
+ }
167
+
168
+ td:first-child {
169
+ border-bottom-left-radius: $hds-table-inner-border-radius;
170
+ }
171
+
172
+ td:last-child {
173
+ border-bottom-right-radius: $hds-table-inner-border-radius;
174
+ }
175
+ }
176
+ }
177
+ }
package/index.js CHANGED
@@ -10,4 +10,15 @@ module.exports = {
10
10
  included: function (/* app */) {
11
11
  this._super.included.apply(this, arguments);
12
12
  },
13
+
14
+ options: {
15
+ babel: {
16
+ plugins: [require.resolve('ember-auto-import/babel-plugin')],
17
+ },
18
+ autoImport: {
19
+ alias: {
20
+ 'dialog-polyfill-css': 'dialog-polyfill/dist/dialog-polyfill.css',
21
+ },
22
+ },
23
+ },
13
24
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashicorp/design-system-components",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "HashiCorp Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",
@@ -37,12 +37,15 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@hashicorp/design-system-tokens": "^1.2.0",
40
- "@hashicorp/ember-flight-icons": "^3.0.1",
40
+ "@hashicorp/ember-flight-icons": "^3.0.2",
41
+ "dialog-polyfill": "^0.5.6",
41
42
  "ember-auto-import": "^2.4.2",
42
43
  "ember-cached-decorator-polyfill": "^0.1.4",
43
44
  "ember-cli-babel": "^7.26.11",
44
45
  "ember-cli-htmlbars": "^6.1.0",
45
46
  "ember-cli-sass": "^10.0.1",
47
+ "ember-composable-helpers": "^4.4.1",
48
+ "ember-focus-trap": "^1.0.1",
46
49
  "ember-keyboard": "^8.1.0",
47
50
  "ember-named-blocks-polyfill": "^0.2.5",
48
51
  "ember-style-modifier": "^0.8.0",
@@ -55,7 +58,7 @@
55
58
  "@embroider/test-setup": "^1.8.3",
56
59
  "@glimmer/component": "^1.1.2",
57
60
  "@glimmer/tracking": "^1.1.2",
58
- "@percy/cli": "^1.12.0",
61
+ "@percy/cli": "^1.13.0",
59
62
  "@percy/ember": "^4.0.0",
60
63
  "babel-eslint": "^10.1.0",
61
64
  "broccoli-asset-rev": "^3.0.0",
@@ -77,7 +80,7 @@
77
80
  "ember-prism": "^0.12.0",
78
81
  "ember-qunit": "^5.1.5",
79
82
  "ember-resolver": "^8.0.3",
80
- "ember-source": "~4.7.0",
83
+ "ember-source": "~4.8.1",
81
84
  "ember-source-channel-url": "^3.0.0",
82
85
  "ember-template-lint": "^4.14.0",
83
86
  "ember-template-lint-plugin-prettier": "^4.0.0",