@hashicorp/design-system-components 2.14.1 → 2.15.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 2.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1690](https://github.com/hashicorp/design-system/pull/1690) [`33df4e25e`](https://github.com/hashicorp/design-system/commit/33df4e25e7bff4dabbba3744128449e2b99d4911) Thanks [@didoo](https://github.com/didoo)! - `Button` - updated horizontal padding of icon-only variant
8
+ `Dropdown::ToggleIcon` - updated sizing of the "small" variant to match the height of the "small" variant `Button`
9
+
10
+ ### Patch Changes
11
+
12
+ - [#1736](https://github.com/hashicorp/design-system/pull/1736) [`541d22442`](https://github.com/hashicorp/design-system/commit/541d22442396d33850a68a61e2781094df1b121a) Thanks [@didoo](https://github.com/didoo)! - `Pagination` - Removed handling of query parameters from `onPageSizeChange` function.
13
+
14
+ _Notice: while technically this is a breaking change, we consider this a fast-follow fix for the previous release._
15
+
16
+ ## 2.14.2
17
+
18
+ ### Patch Changes
19
+
20
+ - [#1724](https://github.com/hashicorp/design-system/pull/1724) [`65ebe6dde`](https://github.com/hashicorp/design-system/commit/65ebe6ddeb477ebc74ad6db353aaf19e1dfb06b1) Thanks [@didoo](https://github.com/didoo)! - `Pagination` - updated the logic for “Compact” variant to expose `@currentPageSize` and handle controlled/uncontrolled changes
21
+
22
+ - [#1716](https://github.com/hashicorp/design-system/pull/1716) [`cde67bc7f`](https://github.com/hashicorp/design-system/commit/cde67bc7fc32c1025ddd22de6d0f9baeabf43961) Thanks [@didoo](https://github.com/didoo)! - `Tabs` - replace `assert` with `warn` in `setIndicator` function
23
+
3
24
  ## 2.14.1
4
25
 
5
26
  ### Patch Changes
@@ -158,6 +158,11 @@ export default class HdsButtonIndexComponent extends Component {
158
158
  classes.push('hds-button--width-full');
159
159
  }
160
160
 
161
+ // add a class if it's icon-only
162
+ if (this.isIconOnly) {
163
+ classes.push('hds-button--is-icon-only');
164
+ }
165
+
161
166
  return classes.join(' ');
162
167
  }
163
168
  }
@@ -4,17 +4,27 @@
4
4
  */
5
5
 
6
6
  import Component from '@glimmer/component';
7
+ import { tracked } from '@glimmer/tracking';
7
8
  import { action } from '@ember/object';
8
9
  import { assert } from '@ember/debug';
9
- import { inject as service } from '@ember/service';
10
10
 
11
11
  // for context about the decision to use these values, see:
12
12
  // https://hashicorp.slack.com/archives/C03A0N1QK8S/p1673546329082759
13
13
  export const DEFAULT_PAGE_SIZES = [10, 30, 50];
14
14
 
15
15
  export default class HdsPaginationCompactIndexComponent extends Component {
16
- @service router;
17
-
16
+ // This private variable is used to differentiate between
17
+ // "uncontrolled" component (where the state is handled internally) and
18
+ // "controlled" component (where the state is handled externally, by the consumer's code).
19
+ // In the first case, the variable stores the internal state of the component at any moment,
20
+ // and its value is updated internally according to the user's interaction with the component.
21
+ // In the second case, the variable stores *only* the initial state of the component (coming from the arguments)
22
+ // at rendering time, but from that moment on it's not updated anymore, no matter what interaction the user
23
+ // has with the component (the state is controlled externally, eg. via query parameters)
24
+ @tracked _currentPageSize = this.args.currentPageSize ?? this.pageSizes[0];
25
+ @tracked isControlled;
26
+
27
+ showLabels = this.args.showLabels ?? true; // if the labels for the "prev/next" controls are visible
18
28
  showSizeSelector = this.args.showSizeSelector ?? false; // if the "size selector" block is visible
19
29
 
20
30
  constructor() {
@@ -31,28 +41,16 @@ export default class HdsPaginationCompactIndexComponent extends Component {
31
41
  // initialized and updated using the arguments passed to it.
32
42
 
33
43
  if (queryFunction === undefined) {
34
- this.hasRouting = false;
44
+ this.isControlled = false;
35
45
  } else {
36
46
  assert(
37
47
  '@queryFunction for "Hds::Pagination::Numbered" must be a function',
38
48
  typeof queryFunction === 'function'
39
49
  );
40
- this.hasRouting = true;
50
+ this.isControlled = true;
41
51
  }
42
52
  }
43
53
 
44
- /**
45
- * @param showLabels
46
- * @type {boolean}
47
- * @default true
48
- * @description Show the labels for the "prev/next" controls
49
- */
50
- get showLabels() {
51
- let { showLabels = true } = this.args;
52
-
53
- return showLabels;
54
- }
55
-
56
54
  /**
57
55
  * @param ariaLabel
58
56
  * @type {string}
@@ -62,6 +60,35 @@ export default class HdsPaginationCompactIndexComponent extends Component {
62
60
  return this.args.ariaLabel ?? 'Pagination';
63
61
  }
64
62
 
63
+ // This very specific `get/set` pattern is used to handle the two different use cases of the component
64
+ // being "controlled" (when it has routing, meaning it needs to support pagination controls as links/`LinkTo`)
65
+ // vs being "uncontrolled" (see comments above for details).
66
+ //
67
+ // If it has routing (and so it's "controlled"), than the value ("state") of the `currentPageSize` variable
68
+ // is *always* determined by the controller via arguments (most of the times, connected to query parameters in the URL).
69
+ // For this reason the "get" method always returns the value from the `args`,
70
+ // while the "set" method never updates the private internal state (_variable).
71
+ //
72
+ // If instead it doesn't have routing (and so it's "uncontrolled") than the value ("state") of the `currentPageSize` variables
73
+ // is *always* determined by the component's internal logic (and updated according to the user interaction with it).
74
+ // For this reason the "get" and "set" methods always read from or write to the private internal state (_variable).
75
+
76
+ get currentPageSize() {
77
+ if (this.isControlled) {
78
+ return this.args.currentPageSize;
79
+ } else {
80
+ return this._currentPageSize;
81
+ }
82
+ }
83
+
84
+ set currentPageSize(value) {
85
+ if (this.isControlled) {
86
+ // noop
87
+ } else {
88
+ this._currentPageSize = value;
89
+ }
90
+ }
91
+
65
92
  /**
66
93
  * @param pageSizes
67
94
  * @type {array of numbers}
@@ -79,13 +106,9 @@ export default class HdsPaginationCompactIndexComponent extends Component {
79
106
  return pageSizes;
80
107
  }
81
108
 
82
- get routeQueryParams() {
83
- return this.router.currentRoute?.queryParams || {};
84
- }
85
-
86
- buildQueryParamsObject(page) {
87
- if (this.hasRouting) {
88
- return this.args.queryFunction(page, this.currentPage);
109
+ buildQueryParamsObject(page, pageSize) {
110
+ if (this.isControlled) {
111
+ return this.args.queryFunction(page, pageSize);
89
112
  } else {
90
113
  return {};
91
114
  }
@@ -100,9 +123,15 @@ export default class HdsPaginationCompactIndexComponent extends Component {
100
123
  };
101
124
 
102
125
  // the "query" is dynamic and needs to be calculated
103
- if (this.hasRouting) {
104
- routing.queryPrev = this.buildQueryParamsObject('prev');
105
- routing.queryNext = this.buildQueryParamsObject('next');
126
+ if (this.isControlled) {
127
+ routing.queryPrev = this.buildQueryParamsObject(
128
+ 'prev',
129
+ this.currentPageSize
130
+ );
131
+ routing.queryNext = this.buildQueryParamsObject(
132
+ 'next',
133
+ this.currentPageSize
134
+ );
106
135
  } else {
107
136
  routing.queryPrev = undefined;
108
137
  routing.queryNext = undefined;
@@ -121,4 +150,14 @@ export default class HdsPaginationCompactIndexComponent extends Component {
121
150
  onPageChange(newPage);
122
151
  }
123
152
  }
153
+
154
+ @action
155
+ onPageSizeChange(newPageSize) {
156
+ let { onPageSizeChange } = this.args;
157
+
158
+ // invoke the callback function
159
+ if (typeof onPageSizeChange === 'function') {
160
+ onPageSizeChange(newPageSize);
161
+ }
162
+ }
124
163
  }
@@ -85,6 +85,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
85
85
  // has with the component (the state is controlled externally, eg. via query parameters)
86
86
  @tracked _currentPage = this.args.currentPage ?? 1;
87
87
  @tracked _currentPageSize = this.args.currentPageSize ?? this.pageSizes[0];
88
+ @tracked isControlled;
88
89
 
89
90
  showInfo = this.args.showInfo ?? true; // if the "info" block is visible
90
91
  showLabels = this.args.showLabels ?? false; // if the labels for the "prev/next" controls are visible
@@ -107,7 +108,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
107
108
  // initialized and updated using the arguments passed to it.
108
109
 
109
110
  if (queryFunction === undefined) {
110
- this.hasRouting = false;
111
+ this.isControlled = false;
111
112
  } else {
112
113
  assert(
113
114
  '@queryFunction for "Hds::Pagination::Numbered" must be a function',
@@ -118,7 +119,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
118
119
  typeof this.args.currentPageSize === 'number' &&
119
120
  typeof this.args.currentPage === 'number'
120
121
  );
121
- this.hasRouting = true;
122
+ this.isControlled = true;
122
123
  }
123
124
 
124
125
  assert(
@@ -137,7 +138,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
137
138
  }
138
139
 
139
140
  // This very specific `get/set` pattern is used to handle the two different use cases of the component
140
- // being "controlled" (when it has routing, meaning it needs to support links as controls)
141
+ // being "controlled" (when it has routing, meaning it needs to support pagination controls as links/`LinkTo`)
141
142
  // vs being "uncontrolled" (see comments above for details).
142
143
  //
143
144
  // If it has routing (and so it's "controlled"), than the value ("state") of the `currentPage/currentPageSize` variables
@@ -150,7 +151,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
150
151
  // For this reason the "get" and "set" methods always read from or write to the private internal state (_variable).
151
152
 
152
153
  get currentPage() {
153
- if (this.hasRouting) {
154
+ if (this.isControlled) {
154
155
  return this.args.currentPage;
155
156
  } else {
156
157
  return this._currentPage;
@@ -158,7 +159,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
158
159
  }
159
160
 
160
161
  set currentPage(value) {
161
- if (this.hasRouting) {
162
+ if (this.isControlled) {
162
163
  // noop
163
164
  } else {
164
165
  this._currentPage = value;
@@ -166,7 +167,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
166
167
  }
167
168
 
168
169
  get currentPageSize() {
169
- if (this.hasRouting) {
170
+ if (this.isControlled) {
170
171
  return this.args.currentPageSize;
171
172
  } else {
172
173
  return this._currentPageSize;
@@ -174,7 +175,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
174
175
  }
175
176
 
176
177
  set currentPageSize(value) {
177
- if (this.hasRouting) {
178
+ if (this.isControlled) {
178
179
  // noop
179
180
  } else {
180
181
  this._currentPageSize = value;
@@ -242,7 +243,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
242
243
  }
243
244
 
244
245
  buildQueryParamsObject(page, pageSize) {
245
- if (this.hasRouting) {
246
+ if (this.isControlled) {
246
247
  return this.args.queryFunction(page, pageSize);
247
248
  } else {
248
249
  return {};
@@ -258,7 +259,7 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
258
259
  };
259
260
 
260
261
  // the "query" is dynamic and needs to be calculated
261
- if (this.hasRouting) {
262
+ if (this.isControlled) {
262
263
  routing.queryPrev = this.buildQueryParamsObject(
263
264
  this.currentPage - 1,
264
265
  this.currentPageSize
@@ -323,10 +324,9 @@ export default class HdsPaginationNumberedIndexComponent extends Component {
323
324
  let { onPageSizeChange } = this.args;
324
325
 
325
326
  // we need to manually update the query parameters in the route (it's not a link!)
326
- // notice: we agreed to reset the pagination to the first element (any alternative would result in an unpredictable UX)
327
- if (this.hasRouting) {
328
- let queryParams = Object.assign({}, this.routeQueryParams);
329
- queryParams = this.buildQueryParamsObject(1, newPageSize);
327
+ if (this.isControlled) {
328
+ // notice: we agreed to reset the pagination to the first element (any alternative would result in an unpredictable UX)
329
+ const queryParams = this.buildQueryParamsObject(1, newPageSize);
330
330
  this.router.transitionTo({ queryParams });
331
331
  } else {
332
332
  this.currentPage = 1;
@@ -6,7 +6,7 @@
6
6
  import Component from '@glimmer/component';
7
7
  import { tracked } from '@glimmer/tracking';
8
8
  import { action } from '@ember/object';
9
- import { assert } from '@ember/debug';
9
+ import { assert, warn } from '@ember/debug';
10
10
  import { next, schedule } from '@ember/runloop';
11
11
 
12
12
  export default class HdsTabsIndexComponent extends Component {
@@ -188,13 +188,23 @@ export default class HdsTabsIndexComponent extends Component {
188
188
  );
189
189
  }
190
190
  } else {
191
- assert(
192
- `"Hds::Tabs" has tried to set the indicator for an element that doesn't exist (the value ${
191
+ let message;
192
+ message +=
193
+ '"Hds::Tabs" has tried to set the indicator for an element that doesn\'t exist';
194
+ if (this.tabNodes.length === 0) {
195
+ message +=
196
+ ' (the array `this.tabNodes` is empty, there are no tabs, probably already destroyed)';
197
+ } else {
198
+ message += ` (the value ${
193
199
  this.selectedTabIndex
194
- } of \`this.selectedTabIndex\` is out of bound for the array \`this.tabNodes\`, whose index range is [0-${
200
+ } of \`this.selectedTabIndex\` is out of bound for the array \`this.tabNodes\`, whose index range is [0 - ${
195
201
  this.tabNodes.length - 1
196
- }])`
197
- );
202
+ }])`;
203
+ }
204
+ // https://api.emberjs.com/ember/5.3/classes/@ember%2Fdebug/methods/warn?anchor=warn
205
+ warn(message, true, {
206
+ id: 'hds-debug.tabs.setTabIndicator-tabElem-not-available',
207
+ });
198
208
  }
199
209
  });
200
210
  }
@@ -75,14 +75,14 @@ $hds-dropdown-toggle-border-radius: $hds-button-border-radius;
75
75
 
76
76
  .hds-dropdown-toggle-icon--size-small {
77
77
  .hds-dropdown-toggle-icon__wrapper {
78
- width: 20px;
79
- height: 20px;
78
+ width: 24px; // same as the `button[small]` minus 2*2px becaus there are a 1px padding and 1px border to take into account
79
+ height: 24px;
80
80
  }
81
81
  }
82
82
 
83
83
  .hds-dropdown-toggle-icon--size-medium {
84
84
  .hds-dropdown-toggle-icon__wrapper {
85
- width: 32px;
85
+ width: 32px; // same as the `button[medium]` minus 2*2px becaus there are a 1px padding and 1px border to take into account
86
86
  height: 32px;
87
87
  }
88
88
  }
@@ -14,21 +14,24 @@ $size-props: (
14
14
  "font-size": 0.8125rem, // 13px;
15
15
  "line-height": 0.875rem, // 14px - we need to make it even (so we set it slighly larger than the font-size; notice: in Figma is 12px but this would cut some ascendants/descendants)
16
16
  "min-height": 1.75rem, // 28px
17
- "padding": 0.375rem 0.6875rem, // 6px 11px - here we're taking into account the 1px border
17
+ "padding-vertical": 0.375rem, // 6px - here we're taking into account the 1px border
18
+ "padding-horizontal": 0.6875rem, // 11px - here we're taking into account the 1px border
18
19
  "icon-size": 0.75rem, // 12px
19
20
  ),
20
21
  "medium": (
21
22
  "font-size": 0.875rem, // 14px
22
23
  "line-height": 1rem,// 16px
23
24
  "min-height": 2.25rem, // 36px
24
- "padding": 0.5625rem 0.9375rem, // 9px 15px - here we're taking into account the 1px border
25
+ "padding-vertical": 0.5625rem, // 9px - here we're taking into account the 1px border
26
+ "padding-horizontal": 0.9375rem, // 15px - here we're taking into account the 1px border
25
27
  "icon-size": 1rem, // 16px
26
28
  ),
27
29
  "large": (
28
30
  "font-size": 1rem, // 16px
29
31
  "line-height": 1.5rem, // 24px
30
32
  "min-height": 3rem, // 48px
31
- "padding": 0.6875rem 1.1875rem, // 11px 19px - here we're taking into account the 1px border
33
+ "padding-vertical": 0.6875rem, // 11px - here we're taking into account the 1px border
34
+ "padding-horizontal": 1.1875rem, // 19px - here we're taking into account the 1px border
32
35
  "icon-size": 1.5rem, // 24px
33
36
  )
34
37
  );
@@ -271,7 +274,7 @@ $size-props: (
271
274
  @each $size in $hds-button-sizes {
272
275
  .#{$blockName}--size-#{$size} {
273
276
  min-height: map-get($size-props, $size, "min-height");
274
- padding: map-get($size-props, $size, "padding");
277
+ padding: map-get($size-props, $size, "padding-vertical") map-get($size-props, $size, "padding-horizontal");
275
278
 
276
279
  .#{$blockName}__icon {
277
280
  width: map-get($size-props, $size, "icon-size");
@@ -282,6 +285,13 @@ $size-props: (
282
285
  font-size: map-get($size-props, $size, "font-size");
283
286
  line-height: map-get($size-props, $size, "line-height");
284
287
  }
288
+
289
+ &.#{$blockName}--is-icon-only {
290
+ // overrides to have the icon-only button squared
291
+ min-width: map-get($size-props, $size, "min-height");
292
+ padding-right: map-get($size-props, $size, "padding-vertical");
293
+ padding-left: map-get($size-props, $size, "padding-vertical");
294
+ }
285
295
  }
286
296
  }
287
297
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashicorp/design-system-components",
3
- "version": "2.14.1",
3
+ "version": "2.15.0",
4
4
  "description": "Helios Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",