@gitlab/ui 103.4.1 → 103.5.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [103.5.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v103.5.0...v103.5.1) (2024-11-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **GlIntersperse:** Ignore empty nodes in Vue 3 ([91ec35b](https://gitlab.com/gitlab-org/gitlab-ui/commit/91ec35b664aa9c8a8959247e92dd29c1409e57ea))
7
+
8
+ # [103.5.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v103.4.1...v103.5.0) (2024-11-22)
9
+
10
+
11
+ ### Features
12
+
13
+ * **GlAccordion:** add chevron animation ([fee4ef4](https://gitlab.com/gitlab-org/gitlab-ui/commit/fee4ef4dac649ee09ce3f9a9a2610d68fe9a8e88))
14
+
1
15
  ## [103.4.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v103.4.0...v103.4.1) (2024-11-22)
2
16
 
3
17
 
@@ -1,5 +1,6 @@
1
1
  import uniqueId from 'lodash/uniqueId';
2
2
  import { BCollapse } from '../../../vendor/bootstrap-vue/src/components/collapse/collapse';
3
+ import GlAnimatedChevronRightDownIcon from '../animated_icon/animated_chevron_right_down_icon';
3
4
  import { GlCollapseToggleDirective } from '../../../directives/collapse_toggle';
4
5
  import GlButton from '../button/button';
5
6
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
@@ -8,7 +9,8 @@ var script = {
8
9
  name: 'GlAccordionItem',
9
10
  components: {
10
11
  BCollapse,
11
- GlButton
12
+ GlButton,
13
+ GlAnimatedChevronRightDownIcon
12
14
  },
13
15
  directives: {
14
16
  GlCollapseToggle: GlCollapseToggleDirective
@@ -77,9 +79,6 @@ var script = {
77
79
  accordion() {
78
80
  return this.accordionSetId() || undefined;
79
81
  },
80
- icon() {
81
- return this.isVisible ? 'chevron-down' : 'chevron-right';
82
- },
83
82
  buttonTitle() {
84
83
  return this.isVisible && this.titleVisible ? this.titleVisible : this.title;
85
84
  }
@@ -98,7 +97,7 @@ var script = {
98
97
  const __vue_script__ = script;
99
98
 
100
99
  /* template */
101
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-accordion-item"},[_c(_vm.headerComponent,{tag:"component",staticClass:"gl-accordion-item-header",class:_vm.headerClass},[_c('gl-button',{directives:[{name:"gl-collapse-toggle",rawName:"v-gl-collapse-toggle",value:(_vm.accordionItemId),expression:"accordionItemId"}],attrs:{"variant":"link","button-text-classes":"gl-flex","icon":_vm.icon}},[_vm._v("\n "+_vm._s(_vm.buttonTitle)+"\n ")])],1),_vm._v(" "),_c('b-collapse',{staticClass:"gl-mt-3 gl-text-base",attrs:{"id":_vm.accordionItemId,"visible":_vm.isVisible,"accordion":_vm.accordion,"data-testid":("accordion-item-collapse-" + _vm.accordionItemId)},model:{value:(_vm.isVisible),callback:function ($$v) {_vm.isVisible=$$v;},expression:"isVisible"}},[_vm._t("default")],2)],1)};
100
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-accordion-item"},[_c(_vm.headerComponent,{tag:"component",staticClass:"gl-accordion-item-header",class:_vm.headerClass},[_c('gl-button',{directives:[{name:"gl-collapse-toggle",rawName:"v-gl-collapse-toggle",value:(_vm.accordionItemId),expression:"accordionItemId"}],attrs:{"variant":"link","button-text-classes":"gl-flex"}},[_c('gl-animated-chevron-right-down-icon',{attrs:{"is-on":_vm.isVisible}}),_vm._v("\n "+_vm._s(_vm.buttonTitle)+"\n ")],1)],1),_vm._v(" "),_c('b-collapse',{staticClass:"gl-mt-3 gl-text-base",attrs:{"id":_vm.accordionItemId,"visible":_vm.isVisible,"accordion":_vm.accordion,"data-testid":("accordion-item-collapse-" + _vm.accordionItemId)},model:{value:(_vm.isVisible),callback:function ($$v) {_vm.isVisible=$$v;},expression:"isVisible"}},[_vm._t("default")],2)],1)};
102
101
  var __vue_staticRenderFns__ = [];
103
102
 
104
103
  /* style */
@@ -1,13 +1,27 @@
1
+ import Vue from 'vue';
1
2
  import compose from 'lodash/fp/compose';
2
3
  import fill from 'lodash/fp/fill';
3
4
  import filter from 'lodash/fp/filter';
4
5
  import { insert, intersperse } from '../../../utils/data_utils';
6
+ import { isVnodeEmpty } from '../../../utils/is_slot_empty';
5
7
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
6
8
 
7
9
  //
8
- const containsWhitespaceOnly = vNode => vNode.text.trim() === '';
9
- const isTag = vNode => typeof vNode.tag === 'string';
10
- const filterWhitespaceNodes = filter(vNode => isTag(vNode) || !containsWhitespaceOnly(vNode));
10
+ const filterEmptyNodesVue2 = filter(vNode => typeof vNode.tag === 'string' || vNode.text.trim() !== '');
11
+ const {
12
+ Fragment
13
+ } = Vue;
14
+ const filterEmptyNodesVue3 = vNode => {
15
+ return vNode.reduce((acc, node) => {
16
+ if (Fragment && node.type === Fragment && Array.isArray(node.children)) {
17
+ acc.push(...node.children);
18
+ } else {
19
+ acc.push(node);
20
+ }
21
+ return acc;
22
+ }, []).filter(node => !isVnodeEmpty(node));
23
+ };
24
+ const filterEmptyNodes = Vue.version.startsWith('3') ? filterEmptyNodesVue3 : filterEmptyNodesVue2;
11
25
  const insertAfterSecondLastItem = insert(-1);
12
26
  const replaceSecondLastItem = fill(-2, -1);
13
27
 
@@ -44,7 +58,7 @@ var script = {
44
58
  slots,
45
59
  data
46
60
  } = context;
47
- const filterAndSeparate = compose(addLastSeparator(lastSeparator), intersperse(separator), filterWhitespaceNodes);
61
+ const filterAndSeparate = compose(addLastSeparator(lastSeparator), intersperse(separator), filterEmptyNodes);
48
62
  return createElement('span', data, filterAndSeparate(slots().default));
49
63
  }
50
64
  };
@@ -3,23 +3,28 @@ import Vue from 'vue';
3
3
  // Fragment will be available only in Vue.js 3
4
4
  const {
5
5
  Fragment,
6
- Comment
6
+ Comment,
7
+ Text
7
8
  } = Vue;
8
9
  function callIfNeeded(fnOrResult, args) {
9
10
  return fnOrResult instanceof Function ? fnOrResult(args) : fnOrResult;
10
11
  }
11
- function isEmpty(vnode) {
12
+ function isVnodeEmpty(vnode) {
12
13
  if (!vnode || Comment && vnode.type === Comment) {
13
14
  return true;
14
15
  }
16
+ if (Text && vnode.type === Text && !vnode.children.trim()) {
17
+ // Vue.js 3 text string is located in the children
18
+ return true;
19
+ }
15
20
  if (Array.isArray(vnode)) {
16
21
  // eslint-disable-next-line unicorn/no-array-callback-reference
17
- return vnode.every(isEmpty);
22
+ return vnode.every(isVnodeEmpty);
18
23
  }
19
24
  if (Fragment && vnode.type === Fragment) {
20
25
  // Vue.js 3 fragment, check children
21
26
  // eslint-disable-next-line unicorn/no-array-callback-reference
22
- return vnode.children.every(isEmpty);
27
+ return vnode.children.every(isVnodeEmpty);
23
28
  }
24
29
  return false;
25
30
  }
@@ -32,7 +37,7 @@ function isSlotEmpty(vueInstance, slot, slotArgs) {
32
37
  callIfNeeded(vueInstance.$slots[slot] || vueInstance.$scopedSlots[slot], slotArgs) : (_vueInstance$$scopedS = (_vueInstance$$scopedS2 = vueInstance.$scopedSlots)[slot]) === null || _vueInstance$$scopedS === void 0 ? void 0 : _vueInstance$$scopedS.call(_vueInstance$$scopedS2, slotArgs);
33
38
 
34
39
  // eslint-disable-next-line unicorn/no-array-callback-reference
35
- return isEmpty(slotContent);
40
+ return isVnodeEmpty(slotContent);
36
41
  }
37
42
 
38
- export { isSlotEmpty };
43
+ export { isSlotEmpty, isVnodeEmpty };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "103.4.1",
3
+ "version": "103.5.1",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -143,16 +143,17 @@
143
143
  "axe-core": "^4.2.3",
144
144
  "babel-jest": "29.0.1",
145
145
  "babel-loader": "^8.0.5",
146
- "cypress": "13.15.2",
146
+ "cypress": "13.16.0",
147
147
  "cypress-axe": "^1.4.0",
148
148
  "cypress-real-events": "^1.11.0",
149
149
  "dompurify": "^3.1.2",
150
150
  "emoji-regex": "^10.0.0",
151
151
  "esbuild": "^0.18.0",
152
152
  "eslint": "8.57.1",
153
+ "eslint-formatter-gitlab": "^5.1.0",
153
154
  "eslint-import-resolver-jest": "3.0.2",
154
155
  "eslint-plugin-cypress": "3.6.0",
155
- "eslint-plugin-storybook": "0.10.1",
156
+ "eslint-plugin-storybook": "0.11.1",
156
157
  "gitlab-api-async-iterator": "^1.3.1",
157
158
  "glob": "10.3.3",
158
159
  "globby": "^11.1.0",
@@ -167,8 +168,8 @@
167
168
  "module-alias": "^2.2.2",
168
169
  "npm-run-all": "^4.1.5",
169
170
  "pikaday": "^1.8.0",
170
- "playwright": "^1.48.2",
171
- "playwright-core": "^1.48.2",
171
+ "playwright": "^1.49.0",
172
+ "playwright-core": "^1.49.0",
172
173
  "postcss": "8.4.28",
173
174
  "postcss-loader": "^7.0.2",
174
175
  "postcss-scss": "4.0.4",
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import uniqueId from 'lodash/uniqueId';
3
3
  import { BCollapse } from '../../../vendor/bootstrap-vue/src/components/collapse/collapse';
4
+ import GlAnimatedChevronRightDownIcon from '../animated_icon/animated_chevron_right_down_icon.vue';
4
5
  import { GlCollapseToggleDirective } from '../../../directives/collapse_toggle';
5
6
  import GlButton from '../button/button.vue';
6
7
 
@@ -9,6 +10,7 @@ export default {
9
10
  components: {
10
11
  BCollapse,
11
12
  GlButton,
13
+ GlAnimatedChevronRightDownIcon,
12
14
  },
13
15
  directives: {
14
16
  GlCollapseToggle: GlCollapseToggleDirective,
@@ -77,9 +79,6 @@ export default {
77
79
  accordion() {
78
80
  return this.accordionSetId() || undefined;
79
81
  },
80
- icon() {
81
- return this.isVisible ? 'chevron-down' : 'chevron-right';
82
- },
83
82
  buttonTitle() {
84
83
  return this.isVisible && this.titleVisible ? this.titleVisible : this.title;
85
84
  },
@@ -102,8 +101,8 @@ export default {
102
101
  v-gl-collapse-toggle="accordionItemId"
103
102
  variant="link"
104
103
  button-text-classes="gl-flex"
105
- :icon="icon"
106
104
  >
105
+ <gl-animated-chevron-right-down-icon :is-on="isVisible" />
107
106
  {{ buttonTitle }}
108
107
  </gl-button>
109
108
  </component>
@@ -1,15 +1,31 @@
1
1
  <!-- eslint-disable vue/multi-word-component-names -->
2
2
  <script>
3
+ import Vue from 'vue';
3
4
  import compose from 'lodash/fp/compose';
4
5
  import fill from 'lodash/fp/fill';
5
6
  import filter from 'lodash/fp/filter';
6
-
7
7
  import { intersperse, insert } from '../../../utils/data_utils';
8
+ import { isVnodeEmpty } from '../../../utils/is_slot_empty';
9
+
10
+ const filterEmptyNodesVue2 = filter(
11
+ (vNode) => typeof vNode.tag === 'string' || vNode.text.trim() !== ''
12
+ );
8
13
 
9
- const containsWhitespaceOnly = (vNode) => vNode.text.trim() === '';
10
- const isTag = (vNode) => typeof vNode.tag === 'string';
11
- const filterWhitespaceNodes = filter((vNode) => isTag(vNode) || !containsWhitespaceOnly(vNode));
14
+ const { Fragment } = Vue;
15
+ const filterEmptyNodesVue3 = (vNode) => {
16
+ return vNode
17
+ .reduce((acc, node) => {
18
+ if (Fragment && node.type === Fragment && Array.isArray(node.children)) {
19
+ acc.push(...node.children);
20
+ } else {
21
+ acc.push(node);
22
+ }
23
+ return acc;
24
+ }, [])
25
+ .filter((node) => !isVnodeEmpty(node));
26
+ };
12
27
 
28
+ const filterEmptyNodes = Vue.version.startsWith('3') ? filterEmptyNodesVue3 : filterEmptyNodesVue2;
13
29
  const insertAfterSecondLastItem = insert(-1);
14
30
  const replaceSecondLastItem = fill(-2, -1);
15
31
 
@@ -51,7 +67,7 @@ export default {
51
67
  const filterAndSeparate = compose(
52
68
  addLastSeparator(lastSeparator),
53
69
  intersperse(separator),
54
- filterWhitespaceNodes
70
+ filterEmptyNodes
55
71
  );
56
72
 
57
73
  return createElement('span', data, filterAndSeparate(slots().default));
package/src/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
  // builds. We do this to avoid having the stylesheet included multiple times in Storybook.
5
5
 
6
6
  // Components
7
- // ADD COMPONENT EXPORTS - needed for yarn generate:component. Do not remove
8
7
  export { default as GlDuoWorkflowPrompt } from './components/experimental/duo/workflow/components/duo_workflow_prompt/duo_workflow_prompt.vue';
9
8
  export { default as GlDuoWorkflowPanel } from './components/experimental/duo/workflow/components/duo_workflow_panel/duo_workflow_panel.vue';
10
9
  export { default as GlTableLite } from './components/base/table_lite/table_lite.vue';
@@ -2,7 +2,6 @@
2
2
  // Import component stylesheets located in components here. i.e.
3
3
  // @import '../components/base/dropdown/dropdown'
4
4
  //
5
- // ADD COMPONENT IMPORTS - needed for yarn generate:component. Do not remove
6
5
  @import '../components/experimental/duo/chat/duo_chat';
7
6
  @import '../components/experimental/duo/chat/components/duo_chat_message/duo_chat_message';
8
7
  @import '../components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader';
@@ -1,26 +1,31 @@
1
1
  import Vue from 'vue';
2
2
 
3
3
  // Fragment will be available only in Vue.js 3
4
- const { Fragment, Comment } = Vue;
4
+ const { Fragment, Comment, Text } = Vue;
5
5
 
6
6
  function callIfNeeded(fnOrResult, args) {
7
7
  return fnOrResult instanceof Function ? fnOrResult(args) : fnOrResult;
8
8
  }
9
9
 
10
- function isEmpty(vnode) {
10
+ export function isVnodeEmpty(vnode) {
11
11
  if (!vnode || (Comment && vnode.type === Comment)) {
12
12
  return true;
13
13
  }
14
14
 
15
+ if (Text && vnode.type === Text && !vnode.children.trim()) {
16
+ // Vue.js 3 text string is located in the children
17
+ return true;
18
+ }
19
+
15
20
  if (Array.isArray(vnode)) {
16
21
  // eslint-disable-next-line unicorn/no-array-callback-reference
17
- return vnode.every(isEmpty);
22
+ return vnode.every(isVnodeEmpty);
18
23
  }
19
24
 
20
25
  if (Fragment && vnode.type === Fragment) {
21
26
  // Vue.js 3 fragment, check children
22
27
  // eslint-disable-next-line unicorn/no-array-callback-reference
23
- return vnode.children.every(isEmpty);
28
+ return vnode.children.every(isVnodeEmpty);
24
29
  }
25
30
 
26
31
  return false;
@@ -36,5 +41,5 @@ export function isSlotEmpty(vueInstance, slot, slotArgs) {
36
41
  : vueInstance.$scopedSlots[slot]?.(slotArgs);
37
42
 
38
43
  // eslint-disable-next-line unicorn/no-array-callback-reference
39
- return isEmpty(slotContent);
44
+ return isVnodeEmpty(slotContent);
40
45
  }