@gitlab/ui 126.3.0 → 126.3.1

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 (27) hide show
  1. package/dist/components/base/avatars_inline/avatars_inline.js +21 -0
  2. package/dist/components/base/badge/badge.js +7 -7
  3. package/dist/components/base/drawer/drawer.js +19 -0
  4. package/dist/components/base/dropdown/dropdown_item.js +34 -0
  5. package/dist/components/base/form/form_input_group/form_input_group.js +6 -0
  6. package/dist/components/base/label/label.js +26 -6
  7. package/dist/components/base/popover/popover.js +15 -0
  8. package/dist/components/base/progress_bar/progress_bar.js +15 -0
  9. package/dist/components/base/search_box_by_type/search_box_by_type.js +20 -0
  10. package/dist/components/base/tabs/tab/tab.js +31 -2
  11. package/dist/components/base/token/token.js +3 -0
  12. package/dist/components/dashboards/dashboard_layout/dashboard_layout.js +27 -1
  13. package/dist/components/dashboards/dashboard_layout/grid_layout/grid_layout.js +28 -6
  14. package/package.json +2 -2
  15. package/src/components/base/avatars_inline/avatars_inline.vue +22 -0
  16. package/src/components/base/badge/badge.vue +9 -8
  17. package/src/components/base/drawer/drawer.vue +23 -0
  18. package/src/components/base/dropdown/dropdown_item.vue +35 -0
  19. package/src/components/base/form/form_input_group/form_input_group.vue +6 -0
  20. package/src/components/base/label/label.vue +26 -6
  21. package/src/components/base/popover/popover.vue +20 -1
  22. package/src/components/base/progress_bar/progress_bar.vue +15 -0
  23. package/src/components/base/search_box_by_type/search_box_by_type.vue +18 -0
  24. package/src/components/base/tabs/tab/tab.vue +46 -2
  25. package/src/components/base/token/token.vue +4 -0
  26. package/src/components/dashboards/dashboard_layout/dashboard_layout.vue +29 -0
  27. package/src/components/dashboards/dashboard_layout/grid_layout/grid_layout.vue +29 -6
@@ -12,33 +12,54 @@ var script = {
12
12
  GlTooltip
13
13
  },
14
14
  props: {
15
+ /**
16
+ * Array of avatar objects to display
17
+ */
15
18
  avatars: {
16
19
  type: Array,
17
20
  required: true
18
21
  },
22
+ /**
23
+ * Maximum number of avatars to display before collapsing
24
+ */
19
25
  maxVisible: {
20
26
  type: Number,
21
27
  required: true
22
28
  },
29
+ /**
30
+ * Size of each avatar in pixels
31
+ */
23
32
  avatarSize: {
24
33
  type: Number,
25
34
  required: true,
26
35
  validator: value => avatarsInlineSizeOptions.includes(value)
27
36
  },
37
+ /**
38
+ * Whether to show the avatars in collapsed state
39
+ */
28
40
  collapsed: {
29
41
  type: Boolean,
30
42
  required: false,
31
43
  default: false
32
44
  },
45
+ /**
46
+ * Screen reader text for the collapsed avatars badge
47
+ */
33
48
  badgeSrOnlyText: {
34
49
  type: String,
35
50
  required: true
36
51
  },
52
+ /**
53
+ * Property name to extract tooltip text from each hidden avatar object
54
+ */
37
55
  badgeTooltipProp: {
38
56
  type: String,
39
57
  required: false,
40
58
  default: ''
41
59
  },
60
+ /**
61
+ * Maximum number of characters to display in the badge tooltip
62
+ */
42
63
  badgeTooltipMaxChars: {
43
64
  type: Number,
44
65
  required: false,
@@ -1,6 +1,7 @@
1
1
  import GlLink from '../link/link';
2
2
  import { badgeVariantOptions, badgeIconSizeOptions, linkVariantUnstyled } from '../../../utils/constants';
3
3
  import GlIcon from '../icon/icon';
4
+ import { logWarning } from '../../../utils/utils';
4
5
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
5
6
 
6
7
  //
@@ -116,12 +117,6 @@ var script = {
116
117
  role() {
117
118
  return this.hasIconOnly ? 'img' : undefined;
118
119
  },
119
- ariaLabel() {
120
- if (this.$attrs['aria-label']) {
121
- return this.$attrs['aria-label'];
122
- }
123
- return this.role === 'img' ? this.icon : undefined;
124
- },
125
120
  iconSizeComputed() {
126
121
  return badgeIconSizeOptions[this.iconSize];
127
122
  },
@@ -154,6 +149,11 @@ var script = {
154
149
  '!gl-px-2': !this.$scopedSlots.default
155
150
  }];
156
151
  }
152
+ },
153
+ mounted() {
154
+ if (this.hasIconOnly && !this.$attrs['aria-label']) {
155
+ logWarning('[GlBadge] Icon-only badges require an aria-label for accessibility. The label should describe the metadata (e.g., "Due date", "Open issue"), not the icon name. See https://design.gitlab.com/components/badge#using-icon-only-badges', this.$el);
156
+ }
157
157
  }
158
158
  };
159
159
 
@@ -161,7 +161,7 @@ var script = {
161
161
  const __vue_script__ = script;
162
162
 
163
163
  /* template */
164
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.computedTag,_vm._g(_vm._b({tag:"component",class:_vm.classes,attrs:{"role":_vm.role,"aria-label":_vm.ariaLabel}},'component',_vm.computedProps,false),_vm.$listeners),[(_vm.icon)?_c('gl-icon',{staticClass:"gl-badge-icon",class:{ '-gl-ml-2': _vm.isCircularIcon },attrs:{"size":_vm.iconSizeComputed,"name":_vm.icon}}):_vm._e(),_vm._v(" "),(_vm.$slots.default)?_c('span',{staticClass:"gl-badge-content"},[_vm._t("default")],2):_vm._e()],1)};
164
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.computedTag,_vm._g(_vm._b({tag:"component",class:_vm.classes,attrs:{"role":_vm.role}},'component',_vm.computedProps,false),_vm.$listeners),[(_vm.icon)?_c('gl-icon',{staticClass:"gl-badge-icon",class:{ '-gl-ml-2': _vm.isCircularIcon },attrs:{"size":_vm.iconSizeComputed,"name":_vm.icon}}):_vm._e(),_vm._v(" "),(_vm.$slots.default)?_c('span',{staticClass:"gl-badge-content"},[_vm._t("default")],2):_vm._e()],1)};
165
165
  var __vue_staticRenderFns__ = [];
166
166
 
167
167
  /* style */
@@ -11,25 +11,40 @@ var script = {
11
11
  GlButton
12
12
  },
13
13
  props: {
14
+ /**
15
+ * Controls the visibility state of the drawer.
16
+ */
14
17
  open: {
15
18
  type: Boolean,
16
19
  required: true
17
20
  },
21
+ /**
22
+ * Height of the header element (e.g., '64px'). Used to position the drawer below a fixed header.
23
+ */
18
24
  headerHeight: {
19
25
  type: String,
20
26
  required: false,
21
27
  default: ''
22
28
  },
29
+ /**
30
+ * When true, makes the drawer header sticky so it remains visible when scrolling the drawer content.
31
+ */
23
32
  headerSticky: {
24
33
  type: Boolean,
25
34
  required: false,
26
35
  default: false
27
36
  },
37
+ /**
38
+ * Z-index value for the drawer. Useful for controlling stacking order with other elements.
39
+ */
28
40
  zIndex: {
29
41
  type: Number,
30
42
  required: false,
31
43
  default: maxZIndex
32
44
  },
45
+ /**
46
+ * Visual variant of the drawer.
47
+ */
33
48
  variant: {
34
49
  type: String,
35
50
  required: false,
@@ -91,6 +106,10 @@ var script = {
91
106
  handleEscape(e) {
92
107
  const ESC = 27;
93
108
  if (this.open && e.keyCode === ESC) {
109
+ /**
110
+ * Emits when drawer gets closed (by pressing ESC or clicking the close button).
111
+ * @event close
112
+ */
94
113
  this.$emit('close');
95
114
  }
96
115
  }
@@ -15,51 +15,81 @@ var script = {
15
15
  },
16
16
  inheritAttrs: false,
17
17
  props: {
18
+ /**
19
+ * URL for the avatar image to display
20
+ */
18
21
  avatarUrl: {
19
22
  type: String,
20
23
  required: false,
21
24
  default: ''
22
25
  },
26
+ /**
27
+ * Color variant for the icon on the left side
28
+ */
23
29
  iconColor: {
24
30
  type: String,
25
31
  required: false,
26
32
  default: ''
27
33
  },
34
+ /**
35
+ * Name of the icon to display on the left side
36
+ */
28
37
  iconName: {
29
38
  type: String,
30
39
  required: false,
31
40
  default: ''
32
41
  },
42
+ /**
43
+ * Aria label for the right icon button
44
+ */
33
45
  iconRightAriaLabel: {
34
46
  type: String,
35
47
  required: false,
36
48
  default: ''
37
49
  },
50
+ /**
51
+ * Name of the icon to display on the right side
52
+ */
38
53
  iconRightName: {
39
54
  type: String,
40
55
  required: false,
41
56
  default: ''
42
57
  },
58
+ /**
59
+ * Whether the dropdown item is checked
60
+ */
43
61
  isChecked: {
44
62
  type: Boolean,
45
63
  required: false,
46
64
  default: false
47
65
  },
66
+ /**
67
+ * Whether to show a check icon for this item
68
+ */
48
69
  isCheckItem: {
49
70
  type: Boolean,
50
71
  required: false,
51
72
  default: false
52
73
  },
74
+ /**
75
+ * Whether to center the check icon vertically
76
+ */
53
77
  isCheckCentered: {
54
78
  type: Boolean,
55
79
  required: false,
56
80
  default: false
57
81
  },
82
+ /**
83
+ * Secondary text to display below the main content
84
+ */
58
85
  secondaryText: {
59
86
  type: String,
60
87
  required: false,
61
88
  default: ''
62
89
  },
90
+ /**
91
+ * ARIA role for the dropdown item
92
+ */
63
93
  role: {
64
94
  type: String,
65
95
  required: false,
@@ -97,6 +127,10 @@ var script = {
97
127
  },
98
128
  methods: {
99
129
  handleClickIconRight() {
130
+ /**
131
+ * Emitted when right icon is clicked.
132
+ * @event handleClickIconRight
133
+ */
100
134
  this.$emit('click-icon-right');
101
135
  }
102
136
  }
@@ -34,11 +34,17 @@ var script = {
34
34
  }],
35
35
  validator: options => options.every(opt => Object.keys(opt).includes('name', 'value'))
36
36
  },
37
+ /**
38
+ * Accessible label for the input field. Used for the aria-label attribute.
39
+ */
37
40
  label: {
38
41
  type: String,
39
42
  required: false,
40
43
  default: undefined
41
44
  },
45
+ /**
46
+ * Additional CSS class(es) to apply to the input element.
47
+ */
42
48
  inputClass: {
43
49
  type: [String, Array, Object],
44
50
  required: false,
@@ -17,41 +17,65 @@ var script = {
17
17
  GlTooltip
18
18
  },
19
19
  props: {
20
+ /**
21
+ * Background color of the label in hex, rgb, or rgba format
22
+ */
20
23
  backgroundColor: {
21
24
  type: String,
22
25
  required: true,
23
26
  validator: value => /^(#|rgb|rgba)/.test(value)
24
27
  },
28
+ /**
29
+ * Title text of the label
30
+ */
25
31
  title: {
26
32
  type: String,
27
33
  required: true,
28
34
  default: ''
29
35
  },
36
+ /**
37
+ * Description text shown in tooltip
38
+ */
30
39
  description: {
31
40
  type: String,
32
41
  required: false,
33
42
  default: ''
34
43
  },
44
+ /**
45
+ * Placement of the tooltip
46
+ */
35
47
  tooltipPlacement: {
36
48
  type: String,
37
49
  required: false,
38
50
  default: 'top'
39
51
  },
52
+ /**
53
+ * Target URL for the label link
54
+ */
40
55
  target: {
41
56
  type: String,
42
57
  required: false,
43
58
  default: ''
44
59
  },
60
+ /**
61
+ * Whether the label is scoped (uses :: separator)
62
+ */
45
63
  scoped: {
46
64
  type: Boolean,
47
65
  required: false,
48
66
  default: false
49
67
  },
68
+ /**
69
+ * Whether to show the close button
70
+ */
50
71
  showCloseButton: {
51
72
  type: Boolean,
52
73
  required: false,
53
74
  default: false
54
75
  },
76
+ /**
77
+ * Whether the label is disabled
78
+ */
55
79
  disabled: {
56
80
  type: Boolean,
57
81
  required: false,
@@ -99,19 +123,15 @@ var script = {
99
123
  methods: {
100
124
  onClick(e) {
101
125
  /**
102
- * Emitted when label is clicked
103
- *
126
+ * Emitted when the label is clicked.
104
127
  * @event click
105
- * @type {object}
106
128
  */
107
129
  this.$emit('click', e);
108
130
  },
109
131
  onClose(e) {
110
132
  /**
111
- * Emitted when x is clicked
112
- *
133
+ * Emitted when the close button is clicked.
113
134
  * @event close
114
- * @type {object}
115
135
  */
116
136
  this.$emit('close', e);
117
137
  }
@@ -16,6 +16,9 @@ var script = {
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 @@ var script = {
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,
@@ -12,27 +12,42 @@ const backgroundClasses = {
12
12
  var script = {
13
13
  name: 'GlProgressBar',
14
14
  props: {
15
+ /**
16
+ * Accessible label for the progress bar. Used for the aria-label attribute.
17
+ */
15
18
  ariaLabel: {
16
19
  type: String,
17
20
  required: false,
18
21
  default: translate('GlProgressBar.ariaLabel', 'Progress bar')
19
22
  },
23
+ /**
24
+ * Current progress value. Should be between 0 and the max value.
25
+ */
20
26
  value: {
21
27
  type: [Number, String],
22
28
  required: false,
23
29
  default: 0
24
30
  },
31
+ /**
32
+ * Visual variant of the progress bar.
33
+ */
25
34
  variant: {
26
35
  type: String,
27
36
  required: false,
28
37
  default: 'primary',
29
38
  validator: value => Object.keys(progressBarVariantOptions).includes(value)
30
39
  },
40
+ /**
41
+ * Maximum value for the progress bar. The value prop is calculated as a percentage of this.
42
+ */
31
43
  max: {
32
44
  type: [Number, String],
33
45
  required: false,
34
46
  default: 100
35
47
  },
48
+ /**
49
+ * Custom height for the progress bar (e.g., '8px', '1rem').
50
+ */
36
51
  height: {
37
52
  type: String,
38
53
  required: false,
@@ -27,11 +27,17 @@ var script = {
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,
@@ -102,6 +108,10 @@ var script = {
102
108
  this.$refs.input.$el.focus();
103
109
  },
104
110
  onInput(value) {
111
+ /**
112
+ * Emitted when the input value changes or gets cleared.
113
+ * @event input
114
+ */
105
115
  this.$emit('input', value);
106
116
  },
107
117
  onFocusout(event) {
@@ -111,6 +121,11 @@ var script = {
111
121
  if (this.isInputOrClearButton(relatedTarget)) {
112
122
  return;
113
123
  }
124
+
125
+ /**
126
+ * Emitted when focus leaves the search box (input and clear button).
127
+ * @event focusout
128
+ */
114
129
  this.$emit('focusout', event);
115
130
  },
116
131
  onFocusin(event) {
@@ -120,6 +135,11 @@ var script = {
120
135
  if (this.isInputOrClearButton(relatedTarget)) {
121
136
  return;
122
137
  }
138
+
139
+ /**
140
+ * Emitted when focus enters the search box (input or clear button).
141
+ * @event focusin
142
+ */
123
143
  this.$emit('focusin', event);
124
144
  }
125
145
  }
@@ -1,5 +1,6 @@
1
1
  import isPlainObject from 'lodash/isPlainObject';
2
2
  import { BTab } from '../../../../vendor/bootstrap-vue/src/components/tabs/tab';
3
+ import GlBadge from '../../badge/badge';
3
4
  import { DEFAULT_TAB_TITLE_LINK_CLASS } from '../constants';
4
5
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
5
6
 
@@ -8,7 +9,8 @@ import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
8
9
  var script = {
9
10
  name: 'GlTab',
10
11
  components: {
11
- BTab
12
+ BTab,
13
+ GlBadge
12
14
  },
13
15
  inheritAttrs: false,
14
16
  props: {
@@ -24,6 +26,24 @@ var script = {
24
26
  type: String,
25
27
  required: false,
26
28
  default: null
29
+ },
30
+ /**
31
+ * Display a count badge next to the tab title.
32
+ */
33
+ tabCount: {
34
+ type: Number,
35
+ required: false,
36
+ default: null
37
+ },
38
+ /**
39
+ * Screen reader text to provide context for the tab count value.
40
+ * Should be the result of calling n__() with the count.
41
+ * Example: :tab-count-sr-text="n__('%d changed file', '%d changed files', count)"
42
+ */
43
+ tabCountSrText: {
44
+ type: String,
45
+ required: false,
46
+ default: null
27
47
  }
28
48
  },
29
49
  computed: {
@@ -41,6 +61,15 @@ var script = {
41
61
  };
42
62
  }
43
63
  return `${titleLinkClass} ${DEFAULT_TAB_TITLE_LINK_CLASS}`.trim();
64
+ },
65
+ hasTabCount() {
66
+ return this.tabCount != null && this.tabCount >= 0;
67
+ }
68
+ },
69
+ created() {
70
+ if (process.env.NODE_ENV !== 'production' && this.hasTabCount && !this.tabCountSrText) {
71
+ // eslint-disable-next-line no-console
72
+ console.warn('[GlTab] 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)"');
44
73
  }
45
74
  }
46
75
  };
@@ -49,7 +78,7 @@ var script = {
49
78
  const __vue_script__ = script;
50
79
 
51
80
  /* template */
52
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-tab',_vm._g(_vm._b({attrs:{"title-link-class":_vm.linkClass,"query-param-value":_vm.queryParamValue},scopedSlots:_vm._u([_vm._l((Object.keys(_vm.$slots)),function(slot){return {key:slot,fn:function(){return [_vm._t(slot)]},proxy:true}})],null,true)},'b-tab',_vm.$attrs,false),_vm.$listeners))};
81
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-tab',_vm._g(_vm._b({attrs:{"title":_vm.hasTabCount ? null : _vm.$attrs.title,"title-link-class":_vm.linkClass,"query-param-value":_vm.queryParamValue},scopedSlots:_vm._u([(_vm.hasTabCount)?{key:"title",fn:function(){return [_vm._t("title",function(){return [_vm._v(_vm._s(_vm.$attrs.title))]}),_vm._v(" "),_c('gl-badge',{staticClass:"gl-ml-2",attrs:{"variant":"neutral","aria-hidden":"true","data-testid":"tab-counter-badge"}},[_vm._v("\n "+_vm._s(_vm.tabCount)+"\n ")]),_vm._v(" "),(_vm.tabCountSrText)?_c('span',{staticClass:"gl-sr-only"},[_vm._v(_vm._s(_vm.tabCountSrText))]):_vm._e()]},proxy:true}:null,_vm._l((Object.keys(_vm.$scopedSlots)),function(slot){return {key:slot,fn:function(){return [_vm._t(slot)]},proxy:true}})],null,true)},'b-tab',_vm.$attrs,false),_vm.$listeners))};
53
82
  var __vue_staticRenderFns__ = [];
54
83
 
55
84
  /* style */
@@ -11,6 +11,9 @@ var script = {
11
11
  CloseButton
12
12
  },
13
13
  props: {
14
+ /**
15
+ * When true, hides the close button and makes the token non-removable.
16
+ */
14
17
  viewOnly: {
15
18
  type: Boolean,
16
19
  required: false,
@@ -34,6 +34,32 @@ var script = {
34
34
  type: Boolean,
35
35
  required: false,
36
36
  default: true
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
+ /* Magic number:
48
+ * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
49
+ * This means text/content with our spacing scale can fit up to 49px without scrolling.
50
+ */
51
+ default: 137,
52
+ validator: value => value > 0
53
+ },
54
+ /**
55
+ * Sets a default minimum height for grid panels. This can still be overriden on a per-panel
56
+ * basis by setting `value.panels[].gridAttributes.minHeight`
57
+ */
58
+ minCellHeight: {
59
+ type: Number,
60
+ required: false,
61
+ default: 1,
62
+ validator: value => value > 0
37
63
  }
38
64
  },
39
65
  computed: {
@@ -56,7 +82,7 @@ var script = {
56
82
  const __vue_script__ = script;
57
83
 
58
84
  /* template */
59
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('section',{staticClass:"gl-my-4 gl-flex gl-items-center"},[_vm._t("header",function(){return [_c('div',{staticClass:"gl-flex gl-w-full gl-flex-col"},[_c('div',{staticClass:"gl-flex gl-items-center"},[_vm._t("title",function(){return [_c('h2',{staticClass:"gl-my-0",attrs:{"data-testid":"title"}},[_vm._v(_vm._s(_vm.config.title))])]})],2),_vm._v(" "),(_vm.dashboardHasDescription)?_c('div',{staticClass:"gl-mt-3 gl-flex"},[_vm._t("description",function(){return [_c('p',{staticClass:"gl-mb-0",attrs:{"data-testid":"description"}},[_vm._v("\n "+_vm._s(_vm.config.description)+"\n ")])]})],2):_vm._e()])]}),_vm._v(" "),(_vm.$scopedSlots.actions)?_c('div',{attrs:{"data-testid":"actions-container"}},[_vm._t("actions")],2):_vm._e()],2),_vm._v(" "),_c('div',{staticClass:"gl-flex"},[_c('div',{staticClass:"gl-flex gl-grow gl-flex-col"},[_vm._t("alert"),_vm._v(" "),(_vm.$scopedSlots.filters)?_c('section',{staticClass:"gl-flex gl-flex-row gl-flex-wrap gl-gap-5 gl-pb-3 gl-pt-4",attrs:{"data-testid":"filters-container"}},[_vm._t("filters")],2):_vm._e(),_vm._v(" "),(_vm.dashboardHasPanels)?_c('grid-layout',{staticClass:"-gl-mx-3",attrs:{"value":_vm.config,"is-static-grid":_vm.isStaticGrid},on:{"input":_vm.emitChanges},scopedSlots:_vm._u([{key:"panel",fn:function(ref){
85
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('section',{staticClass:"gl-my-4 gl-flex gl-items-center"},[_vm._t("header",function(){return [_c('div',{staticClass:"gl-flex gl-w-full gl-flex-col"},[_c('div',{staticClass:"gl-flex gl-items-center"},[_vm._t("title",function(){return [_c('h2',{staticClass:"gl-my-0",attrs:{"data-testid":"title"}},[_vm._v(_vm._s(_vm.config.title))])]})],2),_vm._v(" "),(_vm.dashboardHasDescription)?_c('div',{staticClass:"gl-mt-3 gl-flex"},[_vm._t("description",function(){return [_c('p',{staticClass:"gl-mb-0",attrs:{"data-testid":"description"}},[_vm._v("\n "+_vm._s(_vm.config.description)+"\n ")])]})],2):_vm._e()])]}),_vm._v(" "),(_vm.$scopedSlots.actions)?_c('div',{attrs:{"data-testid":"actions-container"}},[_vm._t("actions")],2):_vm._e()],2),_vm._v(" "),_c('div',{staticClass:"gl-flex"},[_c('div',{staticClass:"gl-flex gl-grow gl-flex-col"},[_vm._t("alert"),_vm._v(" "),(_vm.$scopedSlots.filters)?_c('section',{staticClass:"gl-flex gl-flex-row gl-flex-wrap gl-gap-5 gl-pb-3 gl-pt-4",attrs:{"data-testid":"filters-container"}},[_vm._t("filters")],2):_vm._e(),_vm._v(" "),(_vm.dashboardHasPanels)?_c('grid-layout',{staticClass:"-gl-mx-3",attrs:{"value":_vm.config,"is-static-grid":_vm.isStaticGrid,"cell-height":_vm.cellHeight,"min-cell-height":_vm.minCellHeight},on:{"input":_vm.emitChanges},scopedSlots:_vm._u([{key:"panel",fn:function(ref){
60
86
  var panel = ref.panel;
61
87
  return [_vm._t("panel",null,null,{ panel: panel })]}}],null,true)}):_vm._t("empty-state")],2)]),_vm._v(" "),_vm._t("footer")],2)};
62
88
  var __vue_staticRenderFns__ = [];
@@ -33,6 +33,32 @@ var script = {
33
33
  type: Boolean,
34
34
  required: false,
35
35
  default: true
36
+ },
37
+ /**
38
+ * Adjusts the cell height of the grid. Setting this too high can leave unwanted whitespace
39
+ * between grid panels. Reduce the number to allow for a more compact grid.
40
+ * For more information, see:
41
+ * https://gitlab.com/gitlab-org/gitlab-services/design.gitlab.com/-/issues/3051
42
+ */
43
+ cellHeight: {
44
+ type: Number,
45
+ required: false,
46
+ /* Magic number:
47
+ * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
48
+ * This means text/content with our spacing scale can fit up to 49px without scrolling.
49
+ */
50
+ default: 137,
51
+ validator: value => value > 0
52
+ },
53
+ /**
54
+ * Sets a default minimum height for grid panels. This can still be overriden on a per-panel
55
+ * basis by setting `value.panels[].gridAttributes.minHeight`
56
+ */
57
+ minCellHeight: {
58
+ type: Number,
59
+ required: false,
60
+ default: 1,
61
+ validator: value => value > 0
36
62
  }
37
63
  },
38
64
  data() {
@@ -144,11 +170,6 @@ var script = {
144
170
  margin: '8px',
145
171
  // CSS Selector for finding the drag handle element
146
172
  handle: '.grid-stack-item-handle',
147
- /* Magic number:
148
- * After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
149
- * This means text/content with our spacing scale can fit up to 49px without scrolling.
150
- */
151
- cellHeight: '137px',
152
173
  // Setting 1 in minRow prevents the grid collapsing when all panels are removed
153
174
  minRow: 1,
154
175
  // Define the number of columns for anything below a set width, defaults to fill the available space
@@ -158,6 +179,7 @@ var script = {
158
179
  c: 1
159
180
  }]
160
181
  },
182
+ cellHeight: this.cellHeight,
161
183
  alwaysShowResizeHandle: true,
162
184
  animate: true,
163
185
  float: true,
@@ -206,7 +228,7 @@ var script = {
206
228
  y: yPos,
207
229
  w: width,
208
230
  h: height,
209
- minH: minHeight,
231
+ minH: minHeight || this.minCellHeight,
210
232
  minW: minWidth,
211
233
  maxH: maxHeight,
212
234
  maxW: maxWidth,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "126.3.0",
3
+ "version": "126.3.1",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -159,7 +159,7 @@
159
159
  "rollup-plugin-string": "^3.0.0",
160
160
  "rollup-plugin-svg": "^2.0.0",
161
161
  "rollup-plugin-vue": "^5.1.9",
162
- "sass": "^1.94.1",
162
+ "sass": "^1.94.2",
163
163
  "sass-loader": "^10.5.2",
164
164
  "sass-true": "^9",
165
165
  "start-server-and-test": "^2.1.3",