@hashicorp/design-system-components 2.14.1 → 2.14.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,13 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 2.14.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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
8
+
9
+ - [#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
10
+
3
11
  ## 2.14.1
4
12
 
5
13
  ### Patch Changes
@@ -4,6 +4,7 @@
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
10
  import { inject as service } from '@ember/service';
@@ -15,6 +16,18 @@ export const DEFAULT_PAGE_SIZES = [10, 30, 50];
15
16
  export default class HdsPaginationCompactIndexComponent extends Component {
16
17
  @service router;
17
18
 
19
+ // This private variable is used to differentiate between
20
+ // "uncontrolled" component (where the state is handled internally) and
21
+ // "controlled" component (where the state is handled externally, by the consumer's code).
22
+ // In the first case, the variable stores the internal state of the component at any moment,
23
+ // and its value is updated internally according to the user's interaction with the component.
24
+ // In the second case, the variable stores *only* the initial state of the component (coming from the arguments)
25
+ // at rendering time, but from that moment on it's not updated anymore, no matter what interaction the user
26
+ // has with the component (the state is controlled externally, eg. via query parameters)
27
+ @tracked _currentPageSize = this.args.currentPageSize ?? this.pageSizes[0];
28
+ @tracked isControlled;
29
+
30
+ showLabels = this.args.showLabels ?? true; // if the labels for the "prev/next" controls are visible
18
31
  showSizeSelector = this.args.showSizeSelector ?? false; // if the "size selector" block is visible
19
32
 
20
33
  constructor() {
@@ -31,28 +44,16 @@ export default class HdsPaginationCompactIndexComponent extends Component {
31
44
  // initialized and updated using the arguments passed to it.
32
45
 
33
46
  if (queryFunction === undefined) {
34
- this.hasRouting = false;
47
+ this.isControlled = false;
35
48
  } else {
36
49
  assert(
37
50
  '@queryFunction for "Hds::Pagination::Numbered" must be a function',
38
51
  typeof queryFunction === 'function'
39
52
  );
40
- this.hasRouting = true;
53
+ this.isControlled = true;
41
54
  }
42
55
  }
43
56
 
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
57
  /**
57
58
  * @param ariaLabel
58
59
  * @type {string}
@@ -62,6 +63,35 @@ export default class HdsPaginationCompactIndexComponent extends Component {
62
63
  return this.args.ariaLabel ?? 'Pagination';
63
64
  }
64
65
 
66
+ // This very specific `get/set` pattern is used to handle the two different use cases of the component
67
+ // being "controlled" (when it has routing, meaning it needs to support pagination controls as links/`LinkTo`)
68
+ // vs being "uncontrolled" (see comments above for details).
69
+ //
70
+ // If it has routing (and so it's "controlled"), than the value ("state") of the `currentPageSize` variable
71
+ // is *always* determined by the controller via arguments (most of the times, connected to query parameters in the URL).
72
+ // For this reason the "get" method always returns the value from the `args`,
73
+ // while the "set" method never updates the private internal state (_variable).
74
+ //
75
+ // If instead it doesn't have routing (and so it's "uncontrolled") than the value ("state") of the `currentPageSize` variables
76
+ // is *always* determined by the component's internal logic (and updated according to the user interaction with it).
77
+ // For this reason the "get" and "set" methods always read from or write to the private internal state (_variable).
78
+
79
+ get currentPageSize() {
80
+ if (this.isControlled) {
81
+ return this.args.currentPageSize;
82
+ } else {
83
+ return this._currentPageSize;
84
+ }
85
+ }
86
+
87
+ set currentPageSize(value) {
88
+ if (this.isControlled) {
89
+ // noop
90
+ } else {
91
+ this._currentPageSize = value;
92
+ }
93
+ }
94
+
65
95
  /**
66
96
  * @param pageSizes
67
97
  * @type {array of numbers}
@@ -83,9 +113,9 @@ export default class HdsPaginationCompactIndexComponent extends Component {
83
113
  return this.router.currentRoute?.queryParams || {};
84
114
  }
85
115
 
86
- buildQueryParamsObject(page) {
87
- if (this.hasRouting) {
88
- return this.args.queryFunction(page, this.currentPage);
116
+ buildQueryParamsObject(page, pageSize) {
117
+ if (this.isControlled) {
118
+ return this.args.queryFunction(page, pageSize);
89
119
  } else {
90
120
  return {};
91
121
  }
@@ -100,9 +130,15 @@ export default class HdsPaginationCompactIndexComponent extends Component {
100
130
  };
101
131
 
102
132
  // 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');
133
+ if (this.isControlled) {
134
+ routing.queryPrev = this.buildQueryParamsObject(
135
+ 'prev',
136
+ this.currentPageSize
137
+ );
138
+ routing.queryNext = this.buildQueryParamsObject(
139
+ 'next',
140
+ this.currentPageSize
141
+ );
106
142
  } else {
107
143
  routing.queryPrev = undefined;
108
144
  routing.queryNext = undefined;
@@ -121,4 +157,23 @@ export default class HdsPaginationCompactIndexComponent extends Component {
121
157
  onPageChange(newPage);
122
158
  }
123
159
  }
160
+
161
+ @action
162
+ onPageSizeChange(newPageSize) {
163
+ let { onPageSizeChange } = this.args;
164
+
165
+ // we need to manually update the query parameters in the route (it's not a link!)
166
+ if (this.isControlled) {
167
+ // we pass `null` as value for the `page` argument, so consumers can handle this condition accordingly (probably will just change the side of the data/array slice)
168
+ const queryParams = this.buildQueryParamsObject(null, newPageSize);
169
+ this.router.transitionTo({ queryParams });
170
+ } else {
171
+ this.currentPageSize = newPageSize;
172
+ }
173
+
174
+ // invoke the callback function
175
+ if (typeof onPageSizeChange === 'function') {
176
+ onPageSizeChange(newPageSize);
177
+ }
178
+ }
124
179
  }
@@ -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
  }
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.14.2",
4
4
  "description": "Helios Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",