@carbon/vue 3.0.29 → 3.0.31

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "@carbon/vue",
4
- "version": "3.0.28",
4
+ "version": "3.0.30",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/vue",
3
3
  "license": "Apache-2.0",
4
- "version": "3.0.29",
4
+ "version": "3.0.31",
5
5
  "description": "A collection of components for the Carbon Design System built using Vue.js",
6
6
  "packageManager": "yarn@3.2.0",
7
7
  "main": "dist/carbon-vue-3.umd.min.js",
@@ -77,7 +77,7 @@ const domTemplate = `
77
77
  const domCode = domTemplate.replace("v-bind='args'", '');
78
78
  const multipleTemplate = `
79
79
  <div>
80
- <cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected" id="star-wars">
80
+ <cv-content-switcher tabindex="0" aria-label='Choose content' v-bind='args' @selected="onSelected" id="star-wars">
81
81
  <cv-content-switcher-button parent-switcher="star-wars" owner-id="episode-1" :selected="selectedIndex === 0">Episode 1</cv-content-switcher-button>
82
82
  <cv-content-switcher-button parent-switcher="star-wars" owner-id="episode-2" :selected="selectedIndex === 1">Episode 2</cv-content-switcher-button>
83
83
  <cv-content-switcher-button parent-switcher="star-wars" :icon="iconSample" owner-id="episode-3" :selected="selectedIndex === 2">Episode 3</cv-content-switcher-button>
@@ -96,7 +96,7 @@ const multipleTemplate = `
96
96
  <p>Yoda</p>
97
97
  </cv-content-switcher-content>
98
98
  </section>
99
- <cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected" id="LotR">
99
+ <cv-content-switcher tabindex="1" aria-label='Choose content' v-bind='args' @selected="onSelected" id="LotR">
100
100
  <cv-content-switcher-button parent-switcher="LotR" owner-id="book-1" :selected="selectedIndex === 0">Book 1</cv-content-switcher-button>
101
101
  <cv-content-switcher-button parent-switcher="LotR" owner-id="book-2" :selected="selectedIndex === 1">Book 2</cv-content-switcher-button>
102
102
  <cv-content-switcher-button parent-switcher="LotR" :icon="iconSample" owner-id="book-3" :selected="selectedIndex === 2">Book 3</cv-content-switcher-button>
@@ -18,7 +18,7 @@
18
18
  import { carbonPrefix } from '../../global/settings';
19
19
  import { props as propsCvId, useCvId } from '../../use/cvId';
20
20
  import { useIsLight, props as propsTheme } from '../../use/cvTheme';
21
- import { onUnmounted, watch } from 'vue';
21
+ import { onUnmounted, watch, onMounted, nextTick } from 'vue';
22
22
  import store from './cvContentSwitcherStore';
23
23
 
24
24
  const emit = defineEmits(['selected']);
@@ -36,14 +36,26 @@ const cvId = useCvId(props, true);
36
36
  const isLight = useIsLight(props);
37
37
  store.addParent(cvId.value);
38
38
  store.setContentSelected('global', undefined);
39
+
40
+ // Check for initial selection after child components have set up
41
+ let hadInitialSelection = null;
42
+ onMounted(async () => {
43
+ // Use nextTick to ensure child components have mounted and set their initial state
44
+ await nextTick();
45
+ hadInitialSelection =
46
+ store.state[cvId.value]?.selected !== undefined &&
47
+ store.state[cvId.value]?.selected !== '';
48
+ });
49
+
39
50
  onUnmounted(() => {
40
51
  store.removeParent(cvId.value);
41
52
  });
53
+
42
54
  watch(
43
55
  () => store.state.global.latest,
44
56
  (val, prev) => {
45
57
  // if previous is not set, do not emit. This mimics the current v2 behaviour where the initial content does not generate an event
46
- if (prev) emit('selected', val);
58
+ if (prev || (val && hadInitialSelection === false)) emit('selected', val);
47
59
  }
48
60
  );
49
61
 
@@ -53,7 +65,9 @@ watch(
53
65
  val => {
54
66
  if (ourLatest !== val?.latest) {
55
67
  // if ourLatest is not set, do not emit. This mimics the current v2 behaviour where the initial content does not generate an event
56
- if (ourLatest) emit('selected', val?.latest);
68
+ // However, if there was no initial selection (selectedIndex=-1), we should emit on first click
69
+ if (ourLatest || (val?.latest && hadInitialSelection === false))
70
+ emit('selected', val?.latest);
57
71
  ourLatest = val?.latest;
58
72
  }
59
73
  },
@@ -36,8 +36,9 @@
36
36
  `${carbonPrefix}--label`,
37
37
  { [`${carbonPrefix}--label--disabled`]: disabled },
38
38
  ]"
39
- >{{ label }}</label
40
39
  >
40
+ {{ label }}
41
+ </label>
41
42
 
42
43
  <div
43
44
  ref="listBox"
@@ -110,7 +111,10 @@
110
111
  ref="dropList"
111
112
  :aria-hidden="!open ? 'true' : 'false'"
112
113
  :aria-labelledby="`${uid}-label`"
113
- :class="`${carbonPrefix}--list-box__menu`"
114
+ :class="[
115
+ `${carbonPrefix}--list-box__menu`,
116
+ { 'cv-dropdown__menu--transition-done': transitionDone },
117
+ ]"
114
118
  role="menu"
115
119
  tabindex="-1"
116
120
  >
@@ -268,6 +272,7 @@ provide('dropdown-caption', dataCaption);
268
272
  const itemsList = ref([]);
269
273
  provide('dropdown-items', itemsList);
270
274
  const open = ref(false);
275
+ const transitionDone = ref(false);
271
276
  const data = reactive({
272
277
  isHelper: false,
273
278
  isInvalid: undefined,
@@ -301,6 +306,15 @@ watch(dataValue, () => {
301
306
  });
302
307
  watch(open, () => {
303
308
  focusItem.value = '';
309
+ if (open.value) {
310
+ // Wait for the transition to complete before allowing overflow
311
+ transitionDone.value = false;
312
+ setTimeout(() => {
313
+ transitionDone.value = true;
314
+ }, 200); // Increase delay to ensure animation completes
315
+ } else {
316
+ transitionDone.value = false;
317
+ }
304
318
  });
305
319
  const ariaLabeledBy = computed(() => {
306
320
  if (props.label) {
@@ -432,3 +446,13 @@ function onClick(ev) {
432
446
  }
433
447
  }
434
448
  </script>
449
+
450
+ <style lang="scss">
451
+ .bx--list-box .bx--list-box__menu {
452
+ overflow-y: hidden !important;
453
+ }
454
+
455
+ .bx--list-box--expanded .bx--list-box__menu.cv-dropdown__menu--transition-done {
456
+ overflow-y: auto !important;
457
+ }
458
+ </style>
@@ -229,7 +229,8 @@ function doFocus() {
229
229
  !(
230
230
  tryOn.classList.contains('cv-overflow-menu__before-content') ||
231
231
  tryOn.classList.contains('cv-overflow-menu__after-content') ||
232
- tryOn.tabindex < 0
232
+ tryOn.tabindex < 0 ||
233
+ tryOn.disabled
233
234
  )
234
235
  ) {
235
236
  focusOn = tryOn;
@@ -11,14 +11,16 @@
11
11
  },
12
12
  ]"
13
13
  role="navigation"
14
- v-bind="$attrs"
14
+ :aria-label="attrs['aria-label'] || 'Tabs'"
15
+ v-bind="attrsWithoutAriaLabel"
15
16
  @keydown.right.prevent.stop="onRight"
16
17
  @keydown.left.prevent.stop="onLeft"
17
18
  >
18
19
  <button
19
20
  ref="elLeftOverflow"
20
- aria-hidden="true"
21
- aria-label="scroll left"
21
+ :aria-label="
22
+ leftOverflowNavButtonHidden ? undefined : 'Scroll tabs left'
23
+ "
22
24
  :class="[
23
25
  {
24
26
  [`${carbonPrefix}--tab--overflow-nav-button`]: horizontalOverflow,
@@ -26,7 +28,7 @@
26
28
  leftOverflowNavButtonHidden,
27
29
  },
28
30
  ]"
29
- tabIndex="-1"
31
+ :tabIndex="leftOverflowNavButtonHidden ? -1 : 0"
30
32
  type="button"
31
33
  @click.stop.prevent="e => onOverflowClick(e, { direction: -1 })"
32
34
  @mousedown.stop.prevent="e => onOverflowMouseDown(e, { direction: -1 })"
@@ -89,8 +91,9 @@
89
91
  />
90
92
  <button
91
93
  ref="elRightOverflow"
92
- aria-hidden="true"
93
- aria-label="scroll right"
94
+ :aria-label="
95
+ rightOverflowNavButtonHidden ? undefined : 'Scroll tabs right'
96
+ "
94
97
  :class="[
95
98
  {
96
99
  [`${carbonPrefix}--tab--overflow-nav-button`]: horizontalOverflow,
@@ -98,7 +101,7 @@
98
101
  rightOverflowNavButtonHidden,
99
102
  },
100
103
  ]"
101
- tabIndex="-1"
104
+ :tabIndex="rightOverflowNavButtonHidden ? -1 : 0"
102
105
  type="button"
103
106
  @click="e => onOverflowClick(e, { direction: 1 })"
104
107
  @mousedown="e => onOverflowMouseDown(e, { direction: 1 })"
@@ -117,15 +120,21 @@
117
120
  import { carbonPrefix } from '../../global/settings';
118
121
  import { ChevronLeft16, ChevronRight16 } from '@carbon/icons-vue';
119
122
  import {
123
+ computed,
120
124
  nextTick,
121
125
  onBeforeUnmount,
122
126
  onMounted,
123
127
  onUpdated,
124
128
  provide,
125
129
  ref,
130
+ useAttrs,
126
131
  watch,
127
132
  } from 'vue';
128
133
 
134
+ defineOptions({
135
+ inheritAttrs: false,
136
+ });
137
+
129
138
  const OVERFLOW_BUTTON_OFFSET = 40;
130
139
 
131
140
  const props = defineProps({
@@ -152,6 +161,12 @@ const props = defineProps({
152
161
  });
153
162
  const emit = defineEmits(['tab-selected', 'tab-selected-id']);
154
163
 
164
+ const attrs = useAttrs();
165
+ const attrsWithoutAriaLabel = computed(() => {
166
+ const { 'aria-label': _, ...rest } = attrs;
167
+ return rest;
168
+ });
169
+
155
170
  const selectedId = ref('');
156
171
  provide('tab-selected', selectedId);
157
172
  /**
@@ -1,8 +1,8 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`CvTabs CvTabs - test slotted tab buttons to match snapshot 1`] = `
4
- <div class="cv-tabs ABC-class-123" style="width: 100%;" aria-label="ABC-aria-label-123">
5
- <div data-tabs="" class="cv-tab bx--tabs--scrollable ABC-class-123" role="navigation" aria-label="ABC-aria-label-123"><button aria-hidden="true" aria-label="scroll left" class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
4
+ <div class="cv-tabs" style="width: 100%;">
5
+ <div data-tabs="" class="cv-tab bx--tabs--scrollable ABC-class-123" role="navigation" aria-label="ABC-aria-label-123"><button class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
6
6
  <path d="M5 8L10 3 10.7 3.7 6.4 8 10.7 12.3 10 13z"></path>
7
7
  </svg></button>
8
8
  <!--v-if-->
@@ -11,7 +11,7 @@ exports[`CvTabs CvTabs - test slotted tab buttons to match snapshot 1`] = `
11
11
  <li class="cv-tabs-button bx--tabs--scrollable__nav-item" role="presentation"><button id="tab-2-link" class="bx--tabs--scrollable__nav-link" role="tab" aria-controls="tab-2" aria-selected="false" tabindex="-1" type="button">Origin<strong style="color: orange;">(!)</strong></button></li>
12
12
  <li class="cv-tabs-button bx--tabs--scrollable__nav-item" role="presentation"><button id="tab-3-link" class="bx--tabs--scrollable__nav-link" role="tab" aria-controls="tab-3" aria-selected="false" tabindex="-1" type="button">Tab 3 label</button></li>
13
13
  </ul>
14
- <!--v-if--><button aria-hidden="true" aria-label="scroll right" class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
14
+ <!--v-if--><button class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
15
15
  <path d="M11 8L6 13 5.3 12.3 9.6 8 5.3 3.7 6 3z"></path>
16
16
  </svg></button>
17
17
  </div>
@@ -13,8 +13,8 @@
13
13
  [`${carbonPrefix}--side-nav--ux`]: isChildOfHeader,
14
14
  },
15
15
  ]"
16
- :aria-hidden="!panelExpanded && !fixed ? 'true' : 'false'"
17
- :inert="!panelExpanded && !fixed"
16
+ :aria-hidden="isHidden ? 'true' : 'false'"
17
+ :inert="isHidden"
18
18
  @focusout="onFocusout"
19
19
  @mousedown="onMouseDown"
20
20
  @mouseenter="onHoverToggle(true)"
@@ -79,6 +79,9 @@ const panelExpanded = computed({
79
79
  data.dataExpanded = val;
80
80
  },
81
81
  });
82
+ const isHidden = computed(() => {
83
+ return !panelExpanded.value && !props.fixed && !props.rail;
84
+ });
82
85
  /**
83
86
  * @type {CvHeaderPanel}
84
87
  */