@gitlab/ui 126.3.0 → 126.3.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.
Files changed (46) hide show
  1. package/dist/components/base/avatars_inline/avatars_inline.js +21 -0
  2. package/dist/components/base/badge/badge.js +10 -7
  3. package/dist/components/base/button/button.js +4 -1
  4. package/dist/components/base/drawer/drawer.js +19 -0
  5. package/dist/components/base/dropdown/dropdown_item.js +34 -0
  6. package/dist/components/base/filtered_search/filtered_search.js +4 -2
  7. package/dist/components/base/form/form_input_group/form_input_group.js +6 -0
  8. package/dist/components/base/icon/icon.js +4 -2
  9. package/dist/components/base/illustration/illustration.js +4 -2
  10. package/dist/components/base/label/label.js +26 -6
  11. package/dist/components/base/modal/modal.js +4 -1
  12. package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +8 -4
  13. package/dist/components/base/new_dropdowns/listbox/listbox.js +4 -3
  14. package/dist/components/base/popover/popover.js +15 -0
  15. package/dist/components/base/progress_bar/progress_bar.js +15 -0
  16. package/dist/components/base/search_box_by_type/search_box_by_type.js +20 -0
  17. package/dist/components/base/table/table.js +4 -1
  18. package/dist/components/base/tabs/tab/tab.js +33 -2
  19. package/dist/components/base/token/token.js +3 -0
  20. package/dist/components/dashboards/dashboard_layout/dashboard_layout.js +27 -1
  21. package/dist/components/dashboards/dashboard_layout/grid_layout/grid_layout.js +28 -6
  22. package/dist/utils/utils.js +14 -7
  23. package/package.json +4 -4
  24. package/src/components/base/avatars_inline/avatars_inline.vue +22 -0
  25. package/src/components/base/badge/badge.vue +9 -8
  26. package/src/components/base/button/button.vue +4 -4
  27. package/src/components/base/drawer/drawer.vue +23 -0
  28. package/src/components/base/dropdown/dropdown.vue +2 -2
  29. package/src/components/base/dropdown/dropdown_item.vue +35 -0
  30. package/src/components/base/filtered_search/filtered_search.vue +3 -2
  31. package/src/components/base/form/form_input_group/form_input_group.vue +6 -0
  32. package/src/components/base/icon/icon.vue +2 -2
  33. package/src/components/base/illustration/illustration.vue +4 -2
  34. package/src/components/base/label/label.vue +26 -6
  35. package/src/components/base/modal/modal.vue +8 -8
  36. package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +7 -4
  37. package/src/components/base/new_dropdowns/listbox/listbox.vue +3 -3
  38. package/src/components/base/popover/popover.vue +20 -1
  39. package/src/components/base/progress_bar/progress_bar.vue +15 -0
  40. package/src/components/base/search_box_by_type/search_box_by_type.vue +18 -0
  41. package/src/components/base/table/table.vue +1 -1
  42. package/src/components/base/tabs/tab/tab.vue +47 -2
  43. package/src/components/base/token/token.vue +4 -0
  44. package/src/components/dashboards/dashboard_layout/dashboard_layout.vue +29 -0
  45. package/src/components/dashboards/dashboard_layout/grid_layout/grid_layout.vue +29 -6
  46. package/src/utils/utils.js +10 -5
@@ -16,41 +16,65 @@ export default {
16
16
  GlTooltip,
17
17
  },
18
18
  props: {
19
+ /**
20
+ * Background color of the label in hex, rgb, or rgba format
21
+ */
19
22
  backgroundColor: {
20
23
  type: String,
21
24
  required: true,
22
25
  validator: (value) => /^(#|rgb|rgba)/.test(value),
23
26
  },
27
+ /**
28
+ * Title text of the label
29
+ */
24
30
  title: {
25
31
  type: String,
26
32
  required: true,
27
33
  default: '',
28
34
  },
35
+ /**
36
+ * Description text shown in tooltip
37
+ */
29
38
  description: {
30
39
  type: String,
31
40
  required: false,
32
41
  default: '',
33
42
  },
43
+ /**
44
+ * Placement of the tooltip
45
+ */
34
46
  tooltipPlacement: {
35
47
  type: String,
36
48
  required: false,
37
49
  default: 'top',
38
50
  },
51
+ /**
52
+ * Target URL for the label link
53
+ */
39
54
  target: {
40
55
  type: String,
41
56
  required: false,
42
57
  default: '',
43
58
  },
59
+ /**
60
+ * Whether the label is scoped (uses :: separator)
61
+ */
44
62
  scoped: {
45
63
  type: Boolean,
46
64
  required: false,
47
65
  default: false,
48
66
  },
67
+ /**
68
+ * Whether to show the close button
69
+ */
49
70
  showCloseButton: {
50
71
  type: Boolean,
51
72
  required: false,
52
73
  default: false,
53
74
  },
75
+ /**
76
+ * Whether the label is disabled
77
+ */
54
78
  disabled: {
55
79
  type: Boolean,
56
80
  required: false,
@@ -98,19 +122,15 @@ export default {
98
122
  methods: {
99
123
  onClick(e) {
100
124
  /**
101
- * Emitted when label is clicked
102
- *
125
+ * Emitted when the label is clicked.
103
126
  * @event click
104
- * @type {object}
105
127
  */
106
128
  this.$emit('click', e);
107
129
  },
108
130
  onClose(e) {
109
131
  /**
110
- * Emitted when x is clicked
111
- *
132
+ * Emitted when the close button is clicked.
112
133
  * @event close
113
- * @type {object}
114
134
  */
115
135
  this.$emit('close', e);
116
136
  },
@@ -141,19 +141,19 @@ export default {
141
141
  shouldRenderModalFooter() {
142
142
  return Boolean(
143
143
  this.actionCancel ||
144
- this.actionSecondary ||
145
- this.actionPrimary ||
146
- // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
147
- this.$slots['modal-footer'],
144
+ this.actionSecondary ||
145
+ this.actionPrimary ||
146
+ // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
147
+ this.$slots['modal-footer'],
148
148
  );
149
149
  },
150
150
  },
151
151
  mounted() {
152
152
  if (!this.ariaLabel && !this.title) {
153
- logWarning(
154
- '[gl-modal]: Accessible name for modal missing. Please add title prop or aria-label.',
155
- this.$el,
156
- );
153
+ logWarning('Accessible name for modal missing. Please add title prop or aria-label.', {
154
+ name: 'GlModal',
155
+ element: this.$el,
156
+ });
157
157
  }
158
158
  },
159
159
  methods: {
@@ -121,8 +121,8 @@ export default {
121
121
  validator: (value) => {
122
122
  if (['left', 'center', 'right'].includes(value)) {
123
123
  logWarning(
124
- `GlDisclosureDropdown/GlCollapsibleListbox: "${value}" placement is deprecated.
125
- Use ${dropdownPlacements[value]} instead.`,
124
+ `"${value}" placement is deprecated. Use ${dropdownPlacements[value]} instead.`,
125
+ { name: 'GlDisclosureDropdown/GlCollapsibleListbox' },
126
126
  );
127
127
  }
128
128
 
@@ -347,9 +347,12 @@ export default {
347
347
  checkToggleFocusable() {
348
348
  if (!isElementFocusable(this.toggleElement) && !isElementTabbable(this.toggleElement)) {
349
349
  logWarning(
350
- `GlDisclosureDropdown/GlCollapsibleListbox: Toggle is missing a 'tabindex' and cannot be focused.
350
+ `Toggle is missing a 'tabindex' and cannot be focused.
351
351
  Use 'a' or 'button' element instead or make sure to add 'role="button"' along with 'tabindex' otherwise.`,
352
- this.$el,
352
+ {
353
+ name: 'GlDisclosureDropdown/GlCollapsibleListbox',
354
+ element: this.$el,
355
+ },
353
356
  );
354
357
  }
355
358
  },
@@ -2,7 +2,7 @@
2
2
  <script>
3
3
  import clamp from 'lodash/clamp';
4
4
  import uniqueId from 'lodash/uniqueId';
5
- import { stopEvent } from '../../../../utils/utils';
5
+ import { stopEvent, logWarning } from '../../../../utils/utils';
6
6
  import {
7
7
  GL_DROPDOWN_SHOWN,
8
8
  GL_DROPDOWN_HIDDEN,
@@ -587,9 +587,9 @@ export default {
587
587
  immediate: true,
588
588
  handler(newValue) {
589
589
  if (newValue && this.items.some((item) => !isOption(item))) {
590
- // eslint-disable-next-line no-console
591
- console.warn(
590
+ logWarning(
592
591
  'When using grouped options infinite scroll can only be used on the last group.',
592
+ { name: 'GlCollapsibleListbox' },
593
593
  );
594
594
  }
595
595
  },
@@ -16,6 +16,9 @@ export default {
16
16
  mixins: [tooltipMixin(popoverRefName)],
17
17
  inheritAttrs: false,
18
18
  props: {
19
+ /**
20
+ * Additional CSS class(es) to apply to the popover.
21
+ */
19
22
  cssClasses: {
20
23
  type: [Array, String, Object],
21
24
  required: false,
@@ -31,21 +34,33 @@ export default {
31
34
  required: false,
32
35
  default: 'hover focus',
33
36
  },
37
+ /**
38
+ * Title text to display in the popover header.
39
+ */
34
40
  title: {
35
41
  type: String,
36
42
  required: false,
37
43
  default: '',
38
44
  },
45
+ /**
46
+ * When true, displays a close button in the popover header.
47
+ */
39
48
  showCloseButton: {
40
49
  type: Boolean,
41
50
  required: false,
42
51
  default: false,
43
52
  },
53
+ /**
54
+ * Placement of the popover relative to the target element.
55
+ */
44
56
  placement: {
45
57
  type: String,
46
58
  required: false,
47
59
  default: popoverPlacements.top,
48
60
  },
61
+ /**
62
+ * Padding (in pixels) between the popover and the viewport boundary.
63
+ */
49
64
  boundaryPadding: {
50
65
  type: [Number, String],
51
66
  required: false,
@@ -113,6 +128,7 @@ export default {
113
128
  v-on="$listeners"
114
129
  >
115
130
  <template v-if="shouldShowTitle" #title>
131
+ <!-- @slot Custom content for the popover title -->
116
132
  <slot name="title">
117
133
  {{ title }}
118
134
  </slot>
@@ -124,6 +140,9 @@ export default {
124
140
  />
125
141
  </div>
126
142
  </template>
127
- <template v-if="$scopedSlots.default" #default><slot></slot></template>
143
+ <template v-if="$scopedSlots.default" #default>
144
+ <!-- @slot Main content of the popover body-->
145
+ <slot></slot>
146
+ </template>
128
147
  </b-popover>
129
148
  </template>
@@ -13,27 +13,42 @@ const backgroundClasses = {
13
13
  export default {
14
14
  name: 'GlProgressBar',
15
15
  props: {
16
+ /**
17
+ * Accessible label for the progress bar. Used for the aria-label attribute.
18
+ */
16
19
  ariaLabel: {
17
20
  type: String,
18
21
  required: false,
19
22
  default: translate('GlProgressBar.ariaLabel', 'Progress bar'),
20
23
  },
24
+ /**
25
+ * Current progress value. Should be between 0 and the max value.
26
+ */
21
27
  value: {
22
28
  type: [Number, String],
23
29
  required: false,
24
30
  default: 0,
25
31
  },
32
+ /**
33
+ * Visual variant of the progress bar.
34
+ */
26
35
  variant: {
27
36
  type: String,
28
37
  required: false,
29
38
  default: 'primary',
30
39
  validator: (value) => Object.keys(progressBarVariantOptions).includes(value),
31
40
  },
41
+ /**
42
+ * Maximum value for the progress bar. The value prop is calculated as a percentage of this.
43
+ */
32
44
  max: {
33
45
  type: [Number, String],
34
46
  required: false,
35
47
  default: 100,
36
48
  },
49
+ /**
50
+ * Custom height for the progress bar (e.g., '8px', '1rem').
51
+ */
37
52
  height: {
38
53
  type: String,
39
54
  required: false,
@@ -27,11 +27,17 @@ export default {
27
27
  required: false,
28
28
  default: '',
29
29
  },
30
+ /**
31
+ * Whether to render the search box without borders
32
+ */
30
33
  borderless: {
31
34
  type: Boolean,
32
35
  required: false,
33
36
  default: false,
34
37
  },
38
+ /**
39
+ * Title text for the clear button
40
+ */
35
41
  clearButtonTitle: {
36
42
  type: String,
37
43
  required: false,
@@ -104,6 +110,10 @@ export default {
104
110
  this.$refs.input.$el.focus();
105
111
  },
106
112
  onInput(value) {
113
+ /**
114
+ * Emitted when the input value changes or gets cleared.
115
+ * @event input
116
+ */
107
117
  this.$emit('input', value);
108
118
  },
109
119
  onFocusout(event) {
@@ -113,6 +123,10 @@ export default {
113
123
  return;
114
124
  }
115
125
 
126
+ /**
127
+ * Emitted when focus leaves the search box (input and clear button).
128
+ * @event focusout
129
+ */
116
130
  this.$emit('focusout', event);
117
131
  },
118
132
  onFocusin(event) {
@@ -122,6 +136,10 @@ export default {
122
136
  return;
123
137
  }
124
138
 
139
+ /**
140
+ * Emitted when focus enters the search box (input or clear button).
141
+ * @event focusin
142
+ */
125
143
  this.$emit('focusin', event);
126
144
  },
127
145
  },
@@ -105,7 +105,7 @@ export default {
105
105
  // logWarning will call isDev before logging any message
106
106
  // this additional call to isDev is being made to exit the condition early when run in production
107
107
  if (isDev() && !shouldUseFullTable(this)) {
108
- logWarning(glTableLiteWarning, this.$el);
108
+ logWarning(glTableLiteWarning, { name: 'GlTable', element: this.$el });
109
109
  }
110
110
  },
111
111
  methods: {
@@ -2,6 +2,8 @@
2
2
  <script>
3
3
  import isPlainObject from 'lodash/isPlainObject';
4
4
  import { BTab } from '../../../../vendor/bootstrap-vue/src/components/tabs/tab';
5
+ import GlBadge from '../../badge/badge.vue';
6
+ import { logWarning } from '../../../../utils/utils';
5
7
 
6
8
  import { DEFAULT_TAB_TITLE_LINK_CLASS } from '../constants';
7
9
 
@@ -9,6 +11,7 @@ export default {
9
11
  name: 'GlTab',
10
12
  components: {
11
13
  BTab,
14
+ GlBadge,
12
15
  },
13
16
  inheritAttrs: false,
14
17
  props: {
@@ -25,6 +28,24 @@ export default {
25
28
  required: false,
26
29
  default: null,
27
30
  },
31
+ /**
32
+ * Display a count badge next to the tab title.
33
+ */
34
+ tabCount: {
35
+ type: Number,
36
+ required: false,
37
+ default: null,
38
+ },
39
+ /**
40
+ * Screen reader text to provide context for the tab count value.
41
+ * Should be the result of calling n__() with the count.
42
+ * Example: :tab-count-sr-text="n__('%d changed file', '%d changed files', count)"
43
+ */
44
+ tabCountSrText: {
45
+ type: String,
46
+ required: false,
47
+ default: null,
48
+ },
28
49
  },
29
50
  computed: {
30
51
  linkClass() {
@@ -38,19 +59,43 @@ export default {
38
59
  }
39
60
  return `${titleLinkClass} ${DEFAULT_TAB_TITLE_LINK_CLASS}`.trim();
40
61
  },
62
+ hasTabCount() {
63
+ return this.tabCount != null && this.tabCount >= 0;
64
+ },
65
+ },
66
+ created() {
67
+ if (this.hasTabCount && !this.tabCountSrText) {
68
+ logWarning(
69
+ 'When using "tab-count", you should also provide "tab-count-sr-text" for screen reader accessibility. Example: :tab-count-sr-text="n__(\'%d item\', \'%d items\', count)"',
70
+ { name: 'GlTab' },
71
+ );
72
+ }
41
73
  },
42
74
  };
43
75
  </script>
44
76
 
45
77
  <template>
46
78
  <b-tab
79
+ :title="hasTabCount ? null : $attrs.title"
47
80
  :title-link-class="linkClass"
48
81
  :query-param-value="queryParamValue"
49
82
  v-bind="$attrs"
50
83
  v-on="$listeners"
51
84
  >
52
- <!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots -->
53
- <template v-for="slot in Object.keys($slots)" #[slot]>
85
+ <template v-if="hasTabCount" #title>
86
+ <slot name="title">{{ $attrs.title }}</slot>
87
+ <gl-badge
88
+ class="gl-ml-2"
89
+ variant="neutral"
90
+ aria-hidden="true"
91
+ data-testid="tab-counter-badge"
92
+ >
93
+ {{ tabCount }}
94
+ </gl-badge>
95
+ <span v-if="tabCountSrText" class="gl-sr-only">{{ tabCountSrText }}</span>
96
+ </template>
97
+
98
+ <template v-for="slot in Object.keys($scopedSlots)" #[slot]>
54
99
  <slot :name="slot"></slot>
55
100
  </template>
56
101
  </b-tab>
@@ -10,6 +10,9 @@ export default {
10
10
  CloseButton,
11
11
  },
12
12
  props: {
13
+ /**
14
+ * When true, hides the close button and makes the token non-removable.
15
+ */
13
16
  viewOnly: {
14
17
  type: Boolean,
15
18
  required: false,
@@ -60,6 +63,7 @@ export default {
60
63
  <template>
61
64
  <span :class="['gl-token', variantClass, viewOnlyClass]" v-on="$listeners">
62
65
  <span class="gl-token-content">
66
+ <!-- @slot Content to display inside the token -->
63
67
  <slot></slot>
64
68
  <close-button v-if="!viewOnly" class="gl-token-close" :label="removeLabel" @click="close" />
65
69
  </span>
@@ -35,6 +35,33 @@ export default {
35
35
  required: false,
36
36
  default: true,
37
37
  },
38
+ /**
39
+ * Adjusts the cell height of the grid. Setting this too high can leave unwanted whitespace
40
+ * between grid panels. Reduce the number to allow for a more compact grid.
41
+ * For more information, see:
42
+ * https://gitlab.com/gitlab-org/gitlab-services/design.gitlab.com/-/issues/3051
43
+ */
44
+ cellHeight: {
45
+ type: Number,
46
+ required: false,
47
+
48
+ /* Magic number:
49
+ * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
50
+ * This means text/content with our spacing scale can fit up to 49px without scrolling.
51
+ */
52
+ default: 137,
53
+ validator: (value) => value > 0,
54
+ },
55
+ /**
56
+ * Sets a default minimum height for grid panels. This can still be overriden on a per-panel
57
+ * basis by setting `value.panels[].gridAttributes.minHeight`
58
+ */
59
+ minCellHeight: {
60
+ type: Number,
61
+ required: false,
62
+ default: 1,
63
+ validator: (value) => value > 0,
64
+ },
38
65
  },
39
66
  computed: {
40
67
  dashboardHasPanels() {
@@ -104,6 +131,8 @@ export default {
104
131
  v-if="dashboardHasPanels"
105
132
  :value="config"
106
133
  :is-static-grid="isStaticGrid"
134
+ :cell-height="cellHeight"
135
+ :min-cell-height="minCellHeight"
107
136
  class="-gl-mx-3"
108
137
  @input="emitChanges"
109
138
  >
@@ -35,6 +35,33 @@ export default {
35
35
  required: false,
36
36
  default: true,
37
37
  },
38
+ /**
39
+ * Adjusts the cell height of the grid. Setting this too high can leave unwanted whitespace
40
+ * between grid panels. Reduce the number to allow for a more compact grid.
41
+ * For more information, see:
42
+ * https://gitlab.com/gitlab-org/gitlab-services/design.gitlab.com/-/issues/3051
43
+ */
44
+ cellHeight: {
45
+ type: Number,
46
+ required: false,
47
+
48
+ /* Magic number:
49
+ * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
50
+ * This means text/content with our spacing scale can fit up to 49px without scrolling.
51
+ */
52
+ default: 137,
53
+ validator: (value) => value > 0,
54
+ },
55
+ /**
56
+ * Sets a default minimum height for grid panels. This can still be overriden on a per-panel
57
+ * basis by setting `value.panels[].gridAttributes.minHeight`
58
+ */
59
+ minCellHeight: {
60
+ type: Number,
61
+ required: false,
62
+ default: 1,
63
+ validator: (value) => value > 0,
64
+ },
38
65
  },
39
66
  data() {
40
67
  return {
@@ -143,15 +170,11 @@ export default {
143
170
  margin: '8px',
144
171
  // CSS Selector for finding the drag handle element
145
172
  handle: '.grid-stack-item-handle',
146
- /* Magic number:
147
- * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
148
- * This means text/content with our spacing scale can fit up to 49px without scrolling.
149
- */
150
- cellHeight: '137px',
151
173
  // Setting 1 in minRow prevents the grid collapsing when all panels are removed
152
174
  minRow: 1,
153
175
  // Define the number of columns for anything below a set width, defaults to fill the available space
154
176
  columnOpts: { breakpoints: [{ w: breakpoints.md, c: 1 }] },
177
+ cellHeight: this.cellHeight,
155
178
  alwaysShowResizeHandle: true,
156
179
  animate: true,
157
180
  float: true,
@@ -194,7 +217,7 @@ export default {
194
217
  y: yPos,
195
218
  w: width,
196
219
  h: height,
197
- minH: minHeight,
220
+ minH: minHeight || this.minCellHeight,
198
221
  minW: minWidth,
199
222
  maxH: maxHeight,
200
223
  maxW: maxWidth,
@@ -173,12 +173,17 @@ export function isDev() {
173
173
 
174
174
  /**
175
175
  * Prints a warning message to the console in non-test and non-production environments.
176
- * @param {string} message message to print to the console
177
- * @param {HTMLElement} element component that triggered the warning
176
+ * @param {string} message Message to print to the console.
177
+ * @param {Object} [context] Optional object with additional context.
178
+ * @param {string} [context.name] The name of the context of the message. Usually the component's name.
179
+ * @param {HTMLElement} [context.element] The element relevant to the message.
178
180
  */
179
- export function logWarning(message = '', element = '') {
180
- if (message.length && isDev()) {
181
- console.warn(message, element); // eslint-disable-line no-console
181
+ export function logWarning(message, context = {}) {
182
+ if (isDev()) {
183
+ const { name, element } = context;
184
+ const formattedMessage = name ? `[${name}] ${message}` : message;
185
+ const args = element ? [formattedMessage, element] : [formattedMessage];
186
+ console.warn(...args); // eslint-disable-line no-console
182
187
  }
183
188
  }
184
189