@gitlab/ui 79.3.0 → 79.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [79.4.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v79.3.0...v79.4.0) (2024-04-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **GlBreadcrumb:** Fix containerWidth calc with ResizeObserver ([daa8013](https://gitlab.com/gitlab-org/gitlab-ui/commit/daa80133e1e56383fb857c12cc9384c553a2b389))
7
+ * **GlBreadcrumb:** Fix details in resize calc ([4025e2e](https://gitlab.com/gitlab-org/gitlab-ui/commit/4025e2e0af2a41aa843ccd7c6f1fae25fd2ac170))
8
+ * **GlBreadcrumb:** Watch autoResize prop and react to change ([b4808bf](https://gitlab.com/gitlab-org/gitlab-ui/commit/b4808bfaa59646a6155b394d4c22d4a1c85c6cfb))
9
+
10
+
11
+ ### Features
12
+
13
+ * **GlBreadcrumb:** Add boolean prop to disable auto-resize ([0dad528](https://gitlab.com/gitlab-org/gitlab-ui/commit/0dad528cb2774c3c7bd9ccdafb7f9c8b00a88c23))
14
+
1
15
  # [79.3.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v79.2.0...v79.3.0) (2024-04-25)
2
16
 
3
17
 
@@ -50,6 +50,14 @@ var script = {
50
50
  type: String,
51
51
  required: false,
52
52
  default: () => translate('GlBreadcrumb.showMoreLabel', 'Show more breadcrumbs')
53
+ },
54
+ /**
55
+ * Allows to disable auto-resize behavior. Items will then overflow their container instead of being collapsed into a dropdown.
56
+ */
57
+ autoResize: {
58
+ type: Boolean,
59
+ required: false,
60
+ default: true
53
61
  }
54
62
  },
55
63
  data() {
@@ -97,17 +105,23 @@ var script = {
97
105
  items: {
98
106
  handler: 'measureAndMakeBreadcrumbsFit',
99
107
  deep: true
108
+ },
109
+ autoResize(newValue) {
110
+ if (newValue) this.enableAutoResize();else this.disableAutoResize();
100
111
  }
101
112
  },
102
113
  created() {
103
114
  this.debounceMakeBreadcrumbsFit = debounce(this.makeBreadcrumbsFit, 25);
104
115
  },
105
116
  mounted() {
106
- window.addEventListener('resize', this.debounceMakeBreadcrumbsFit);
107
- this.measureAndMakeBreadcrumbsFit();
117
+ if (this.autoResize) {
118
+ this.enableAutoResize();
119
+ } else {
120
+ this.resizeDone = true;
121
+ }
108
122
  },
109
123
  beforeDestroy() {
110
- window.removeEventListener('resize', this.debounceMakeBreadcrumbsFit);
124
+ this.disableAutoResize();
111
125
  },
112
126
  methods: {
113
127
  resetItems() {
@@ -132,9 +146,9 @@ var script = {
132
146
  this.resizeDone = false;
133
147
  this.resetItems();
134
148
  const containerWidth = this.$el.clientWidth;
135
- const buttonWidth = 50; // px
149
+ const buttonWidth = 40; // px
136
150
 
137
- if (this.totalBreadcrumbsWidth + buttonWidth > containerWidth) {
151
+ if (this.totalBreadcrumbsWidth > containerWidth) {
138
152
  // Not all breadcrumb items fit. Start moving items over to the dropdown.
139
153
  const startSlicingAt = 0;
140
154
 
@@ -158,6 +172,18 @@ var script = {
158
172
  },
159
173
  getAriaCurrentAttr(index) {
160
174
  return this.isLastItem(index) ? 'page' : false;
175
+ },
176
+ enableAutoResize() {
177
+ this.resizeObserver || (this.resizeObserver = new ResizeObserver(this.debounceMakeBreadcrumbsFit));
178
+ this.resizeObserver.observe(this.$el);
179
+ this.measureAndMakeBreadcrumbsFit();
180
+ },
181
+ disableAutoResize() {
182
+ if (this.resizeObserver) {
183
+ this.resizeObserver.unobserve(this.$el);
184
+ this.resizeObserver = null;
185
+ }
186
+ this.resetItems();
161
187
  }
162
188
  }
163
189
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ * Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ * Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
  */
5
5
 
6
6
  :root.gl-dark {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ * Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#133a03";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ * Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#ddfab7";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ // Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
 
5
5
  $gl-text-color-disabled: #89888d !default; // Used for disabled text.
6
6
  $gl-text-color-link: #63a6e9 !default; // Used for default text links.
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Thu, 25 Apr 2024 08:42:27 GMT
3
+ // Generated on Thu, 25 Apr 2024 15:30:17 GMT
4
4
 
5
5
  $gl-text-color-disabled: #89888d !default; // Used for disabled text.
6
6
  $gl-text-color-success: #217645 !default; // Used for text indicating success or validity.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "79.3.0",
3
+ "version": "79.4.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -29,6 +29,7 @@ describe('Breadcrumb component', () => {
29
29
  const findAllAvatars = () => wrapper.findAll('[data-testid="avatar"]');
30
30
  const findBreadcrumbItems = () => wrapper.findAllComponents(GlBreadcrumbItem);
31
31
  const findOverflowDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
32
+ const findOverflowingItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
32
33
 
33
34
  const findVisibleBreadcrumbItems = () =>
34
35
  findBreadcrumbItems().wrappers.filter((item) => item.isVisible());
@@ -43,21 +44,24 @@ describe('Breadcrumb component', () => {
43
44
  });
44
45
  };
45
46
 
46
- const mockWrapperWidth = (widthInPx) => {
47
- wrapper.element.style.width = `${widthInPx}px`;
48
-
49
- Object.defineProperty(wrapper.element, 'clientWidth', {
47
+ const mockElementWidth = (element, widthInPx) => {
48
+ Object.defineProperty(element, 'clientWidth', {
50
49
  get: () => widthInPx,
51
- configurable: true,
52
50
  });
53
51
  };
54
52
 
55
53
  const mockWideWrapperWidth = () => {
56
- mockWrapperWidth(1000);
54
+ mockElementWidth(wrapper.element, 1000);
57
55
  };
58
56
 
59
57
  const mockSmallWrapperWidth = () => {
60
- mockWrapperWidth(1);
58
+ mockElementWidth(wrapper.element, 1);
59
+ };
60
+
61
+ const mockItemsWidths = () => {
62
+ findBreadcrumbItems().wrappers.forEach((item) => {
63
+ mockElementWidth(item.element, 100);
64
+ });
61
65
  };
62
66
 
63
67
  describe('items', () => {
@@ -88,6 +92,7 @@ describe('Breadcrumb component', () => {
88
92
  beforeEach(async () => {
89
93
  createComponent({ items, showMoreLabel: 'More...' });
90
94
  mockSmallWrapperWidth();
95
+ mockItemsWidths();
91
96
  await wrapper.vm.$nextTick();
92
97
  });
93
98
 
@@ -100,6 +105,7 @@ describe('Breadcrumb component', () => {
100
105
  beforeEach(async () => {
101
106
  createComponent();
102
107
  mockSmallWrapperWidth();
108
+ mockItemsWidths();
103
109
  await wrapper.vm.$nextTick();
104
110
  });
105
111
 
@@ -109,6 +115,53 @@ describe('Breadcrumb component', () => {
109
115
  });
110
116
  });
111
117
 
118
+ describe('autoResize', () => {
119
+ describe('by default', () => {
120
+ beforeEach(async () => {
121
+ createComponent();
122
+ mockSmallWrapperWidth();
123
+ mockItemsWidths();
124
+ await wrapper.vm.$nextTick();
125
+ });
126
+
127
+ it('moves overflowing items into dropdown', () => {
128
+ expect(findOverflowDropdown().exists()).toBe(true);
129
+ expect(findOverflowingItems().length).toBeGreaterThan(0);
130
+ });
131
+
132
+ it('reacts to prop changing to false', async () => {
133
+ expect(findOverflowDropdown().exists()).toBe(true);
134
+ await wrapper.setProps({ autoResize: false });
135
+ await wrapper.vm.$nextTick();
136
+ expect(findOverflowDropdown().exists()).toBe(false);
137
+ expect(findBreadcrumbItems().length).toEqual(items.length);
138
+ });
139
+ });
140
+
141
+ describe('when set to false', () => {
142
+ beforeEach(async () => {
143
+ createComponent({ items, autoResize: false });
144
+ mockSmallWrapperWidth();
145
+ mockItemsWidths();
146
+ await wrapper.vm.$nextTick();
147
+ });
148
+
149
+ it('keeps all items visible', () => {
150
+ expect(findOverflowDropdown().exists()).toBe(false);
151
+ expect(findBreadcrumbItems().length).toEqual(items.length);
152
+ });
153
+
154
+ it('reacts to prop changing to true', async () => {
155
+ expect(findOverflowDropdown().exists()).toBe(false);
156
+ await wrapper.setProps({ autoResize: true });
157
+ await wrapper.vm.$nextTick();
158
+ await wrapper.vm.$nextTick(); // otherwise test fails with VUE_VERSION=3
159
+ expect(findOverflowDropdown().exists()).toBe(true);
160
+ expect(findOverflowingItems().length).toBeGreaterThan(0);
161
+ });
162
+ });
163
+ });
164
+
112
165
  describe('avatars', () => {
113
166
  it('renders 2 avatars when 2 avatarPaths are passed', async () => {
114
167
  createComponent();
@@ -169,6 +222,7 @@ describe('Breadcrumb component', () => {
169
222
  beforeEach(async () => {
170
223
  createComponent();
171
224
  mockSmallWrapperWidth();
225
+ mockItemsWidths();
172
226
  await wrapper.vm.$nextTick();
173
227
  });
174
228
 
@@ -7,6 +7,7 @@ const template = `
7
7
  <gl-breadcrumb
8
8
  :items="items"
9
9
  :aria-label="ariaLabel"
10
+ :auto-resize="autoResize"
10
11
  />
11
12
  `;
12
13
 
@@ -15,6 +16,7 @@ const collapsedTemplate = `
15
16
  <gl-breadcrumb
16
17
  :items="items"
17
18
  :aria-label="ariaLabel"
19
+ :auto-resize="autoResize"
18
20
  />
19
21
  </div>
20
22
  `;
@@ -48,6 +48,14 @@ export default {
48
48
  required: false,
49
49
  default: () => translate('GlBreadcrumb.showMoreLabel', 'Show more breadcrumbs'),
50
50
  },
51
+ /**
52
+ * Allows to disable auto-resize behavior. Items will then overflow their container instead of being collapsed into a dropdown.
53
+ */
54
+ autoResize: {
55
+ type: Boolean,
56
+ required: false,
57
+ default: true,
58
+ },
51
59
  },
52
60
  data() {
53
61
  return {
@@ -89,16 +97,23 @@ export default {
89
97
  handler: 'measureAndMakeBreadcrumbsFit',
90
98
  deep: true,
91
99
  },
100
+ autoResize(newValue) {
101
+ if (newValue) this.enableAutoResize();
102
+ else this.disableAutoResize();
103
+ },
92
104
  },
93
105
  created() {
94
106
  this.debounceMakeBreadcrumbsFit = debounce(this.makeBreadcrumbsFit, 25);
95
107
  },
96
108
  mounted() {
97
- window.addEventListener('resize', this.debounceMakeBreadcrumbsFit);
98
- this.measureAndMakeBreadcrumbsFit();
109
+ if (this.autoResize) {
110
+ this.enableAutoResize();
111
+ } else {
112
+ this.resizeDone = true;
113
+ }
99
114
  },
100
115
  beforeDestroy() {
101
- window.removeEventListener('resize', this.debounceMakeBreadcrumbsFit);
116
+ this.disableAutoResize();
102
117
  },
103
118
  methods: {
104
119
  resetItems() {
@@ -126,9 +141,9 @@ export default {
126
141
  this.resetItems();
127
142
 
128
143
  const containerWidth = this.$el.clientWidth;
129
- const buttonWidth = 50; // px
144
+ const buttonWidth = 40; // px
130
145
 
131
- if (this.totalBreadcrumbsWidth + buttonWidth > containerWidth) {
146
+ if (this.totalBreadcrumbsWidth > containerWidth) {
132
147
  // Not all breadcrumb items fit. Start moving items over to the dropdown.
133
148
  const startSlicingAt = 0;
134
149
 
@@ -156,6 +171,18 @@ export default {
156
171
  getAriaCurrentAttr(index) {
157
172
  return this.isLastItem(index) ? 'page' : false;
158
173
  },
174
+ enableAutoResize() {
175
+ this.resizeObserver ||= new ResizeObserver(this.debounceMakeBreadcrumbsFit);
176
+ this.resizeObserver.observe(this.$el);
177
+ this.measureAndMakeBreadcrumbsFit();
178
+ },
179
+ disableAutoResize() {
180
+ if (this.resizeObserver) {
181
+ this.resizeObserver.unobserve(this.$el);
182
+ this.resizeObserver = null;
183
+ }
184
+ this.resetItems();
185
+ },
159
186
  },
160
187
  };
161
188
  </script>
@@ -1,10 +1,13 @@
1
+ import GlFormGroup from '../form_group/form_group.vue';
1
2
  import readme from './form_date.md';
2
3
  import GlFormDate from './form_date.vue';
3
4
 
4
5
  const defaultValue = (prop) => GlFormDate.props[prop].default;
5
6
 
6
7
  const template = `
8
+ <gl-form-group :label="labelText" :label-for="inputId">
7
9
  <gl-form-date
10
+ :id="inputId"
8
11
  v-model="localValue"
9
12
  :disabled="disabled"
10
13
  :min="min"
@@ -13,9 +16,12 @@ const template = `
13
16
  :max-invalid-feedback="maxInvalidFeedback"
14
17
  :readonly="readonly"
15
18
  :value="value"
16
- />`;
19
+ />
20
+ </gl-form-group>`;
17
21
 
18
22
  const generateProps = ({
23
+ inputId = 'input-id',
24
+ labelText = 'Label',
19
25
  disabled = false,
20
26
  min = '',
21
27
  max = '',
@@ -24,6 +30,8 @@ const generateProps = ({
24
30
  readonly = false,
25
31
  value = '',
26
32
  } = {}) => ({
33
+ inputId,
34
+ labelText,
27
35
  disabled,
28
36
  min,
29
37
  max,
@@ -34,7 +42,7 @@ const generateProps = ({
34
42
  });
35
43
 
36
44
  const Template = (args) => ({
37
- components: { GlFormDate },
45
+ components: { GlFormDate, GlFormGroup },
38
46
  props: Object.keys(args),
39
47
  watch: {
40
48
  value(newValue) {