@carbon/vue 3.0.26 → 3.0.29
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/dist/carbon-vue-3.common.js +250 -37
- package/dist/carbon-vue-3.common.js.map +1 -1
- package/dist/carbon-vue-3.umd.js +246 -33
- package/dist/carbon-vue-3.umd.js.map +1 -1
- package/dist/carbon-vue-3.umd.min.js +2 -2
- package/dist/carbon-vue-3.umd.min.js.map +1 -1
- package/dist/web-types.json +26 -2
- package/package.json +1 -1
- package/src/components/CvDatePicker/CvDatePicker.vue +4 -2
- package/src/components/CvDropdown/CvDropdown.vue +25 -5
- package/src/components/CvUIShell/CvContent.vue +7 -1
- package/src/components/CvUIShell/CvHeader.vue +3 -3
- package/src/components/CvUIShell/CvHeaderMenu.vue +2 -1
- package/src/components/CvUIShell/CvHeaderMenuItem.vue +8 -3
- package/src/components/CvUIShell/CvHeaderNav.vue +10 -1
- package/src/components/CvUIShell/CvHeaderPanel.vue +42 -0
- package/src/components/CvUIShell/CvSideNav.vue +36 -0
- package/src/components/CvUIShell/CvUIShell.stories.mdx +19 -15
- package/src/components/CvUIShell/__tests__/PanelFocusTestComponent.vue +47 -0
- package/src/index.js +1 -0
package/dist/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"framework": "vue",
|
|
3
3
|
"name": "@carbon/vue",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.28",
|
|
5
5
|
"contributions": {
|
|
6
6
|
"html": {
|
|
7
7
|
"description-markup": "markdown",
|
|
@@ -770,6 +770,14 @@
|
|
|
770
770
|
"type": "string"
|
|
771
771
|
},
|
|
772
772
|
"default": "'main'"
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
"name": "id",
|
|
776
|
+
"value": {
|
|
777
|
+
"kind": "expression",
|
|
778
|
+
"type": "string"
|
|
779
|
+
},
|
|
780
|
+
"default": "'main-content'"
|
|
773
781
|
}
|
|
774
782
|
],
|
|
775
783
|
"slots": [
|
|
@@ -2280,7 +2288,7 @@
|
|
|
2280
2288
|
"kind": "expression",
|
|
2281
2289
|
"type": "string"
|
|
2282
2290
|
},
|
|
2283
|
-
"default": "
|
|
2291
|
+
"default": "'menuitem'"
|
|
2284
2292
|
}
|
|
2285
2293
|
],
|
|
2286
2294
|
"slots": [
|
|
@@ -2320,6 +2328,14 @@
|
|
|
2320
2328
|
"name": "CvHeaderNav",
|
|
2321
2329
|
"description": "",
|
|
2322
2330
|
"attributes": [
|
|
2331
|
+
{
|
|
2332
|
+
"name": "ariaLabel",
|
|
2333
|
+
"value": {
|
|
2334
|
+
"kind": "expression",
|
|
2335
|
+
"type": "string"
|
|
2336
|
+
},
|
|
2337
|
+
"default": "undefined"
|
|
2338
|
+
},
|
|
2323
2339
|
{
|
|
2324
2340
|
"name": "ariaLabelledBy",
|
|
2325
2341
|
"value": {
|
|
@@ -5595,6 +5611,14 @@
|
|
|
5595
5611
|
"module": "./src/components/CvTooltip/CvTooltip.vue",
|
|
5596
5612
|
"symbol": "default"
|
|
5597
5613
|
}
|
|
5614
|
+
},
|
|
5615
|
+
{
|
|
5616
|
+
"name": "PanelFocusTestComponent",
|
|
5617
|
+
"description": "",
|
|
5618
|
+
"source": {
|
|
5619
|
+
"module": "./src/components/CvUIShell/__tests__/PanelFocusTestComponent.vue",
|
|
5620
|
+
"symbol": "default"
|
|
5621
|
+
}
|
|
5598
5622
|
}
|
|
5599
5623
|
]
|
|
5600
5624
|
}
|
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.
|
|
4
|
+
"version": "3.0.29",
|
|
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",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
ref="date"
|
|
41
41
|
type="text"
|
|
42
42
|
data-date-picker-input
|
|
43
|
-
|
|
43
|
+
data-testid="datepicker"
|
|
44
|
+
:aria-description="placeholder"
|
|
44
45
|
:data-invalid="isInvalid || null"
|
|
45
46
|
:disabled="disabled || null"
|
|
46
47
|
:data-date-picker-input-from="`${getKind === 'range'}`"
|
|
@@ -85,7 +86,8 @@
|
|
|
85
86
|
ref="todate"
|
|
86
87
|
type="text"
|
|
87
88
|
data-date-picker-input
|
|
88
|
-
|
|
89
|
+
data-testid="todatepicker"
|
|
90
|
+
:aria-description="placeholder"
|
|
89
91
|
:data-date-picker-input-to="`${kind === 'range'}`"
|
|
90
92
|
:data-invalid="isInvalid || null"
|
|
91
93
|
:disabled="disabled || null"
|
|
@@ -323,6 +323,13 @@ const wrapperStyleOverride = computed(() => {
|
|
|
323
323
|
});
|
|
324
324
|
const el = ref(null);
|
|
325
325
|
function onClickOut() {
|
|
326
|
+
// Move focus to button if focus is inside dropdown list
|
|
327
|
+
const dropListEl = dropList.value;
|
|
328
|
+
if (dropListEl && dropListEl.contains(document.activeElement)) {
|
|
329
|
+
if (button.value && typeof button.value.focus === 'function') {
|
|
330
|
+
button.value.focus();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
326
333
|
open.value = false;
|
|
327
334
|
}
|
|
328
335
|
onClickOutside(el, onClickOut);
|
|
@@ -372,8 +379,14 @@ function onUp() {
|
|
|
372
379
|
}
|
|
373
380
|
}
|
|
374
381
|
function onEsc() {
|
|
382
|
+
// Move focus to button if focus is inside dropdown list
|
|
383
|
+
const dropListEl = dropList.value;
|
|
384
|
+
if (dropListEl && dropListEl.contains(document.activeElement)) {
|
|
385
|
+
if (button.value && typeof button.value.focus === 'function') {
|
|
386
|
+
button.value.focus();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
375
389
|
open.value = false;
|
|
376
|
-
doFocus();
|
|
377
390
|
}
|
|
378
391
|
const button = ref(null);
|
|
379
392
|
const listBox = ref(null);
|
|
@@ -384,8 +397,10 @@ function onTab(ev) {
|
|
|
384
397
|
open.value = false;
|
|
385
398
|
} else if (ev.target === null || listBox.value?.contains(ev.target)) {
|
|
386
399
|
// list has focus, close and return focus to dropdown
|
|
400
|
+
if (button.value && typeof button.value.focus === 'function') {
|
|
401
|
+
button.value.focus();
|
|
402
|
+
}
|
|
387
403
|
open.value = false;
|
|
388
|
-
doFocus();
|
|
389
404
|
ev.preventDefault();
|
|
390
405
|
}
|
|
391
406
|
}
|
|
@@ -394,9 +409,6 @@ function onClick(ev) {
|
|
|
394
409
|
if (props.disabled) {
|
|
395
410
|
ev.preventDefault();
|
|
396
411
|
} else {
|
|
397
|
-
open.value = !open.value;
|
|
398
|
-
doFocus(); // open or close (Some browsers do not focus on button when clicked)
|
|
399
|
-
|
|
400
412
|
let target = ev.target;
|
|
401
413
|
while (
|
|
402
414
|
!target.classList.contains(`${carbonPrefix}--dropdown-link`) &&
|
|
@@ -408,6 +420,14 @@ function onClick(ev) {
|
|
|
408
420
|
if (target.classList.contains(`${carbonPrefix}--dropdown-link`)) {
|
|
409
421
|
const targetItemEl = target.parentNode;
|
|
410
422
|
dataValue.value = targetItemEl.getAttribute('data-value');
|
|
423
|
+
// Move focus to button before closing
|
|
424
|
+
if (button.value && typeof button.value.focus === 'function') {
|
|
425
|
+
button.value.focus();
|
|
426
|
+
}
|
|
427
|
+
open.value = false;
|
|
428
|
+
} else {
|
|
429
|
+
open.value = !open.value;
|
|
430
|
+
doFocus(); // open or close (Some browsers do not focus on button when clicked)
|
|
411
431
|
}
|
|
412
432
|
}
|
|
413
433
|
}
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
import { carbonPrefix } from '../../global/settings';
|
|
3
3
|
defineProps({
|
|
4
4
|
tagType: { type: String, default: 'main' },
|
|
5
|
+
id: { type: String, default: 'main-content' },
|
|
5
6
|
});
|
|
6
7
|
</script>
|
|
7
8
|
|
|
8
9
|
<template>
|
|
9
|
-
<component
|
|
10
|
+
<component
|
|
11
|
+
:is="tagType"
|
|
12
|
+
:id="id"
|
|
13
|
+
:class="`${carbonPrefix}--content`"
|
|
14
|
+
tabindex="-1"
|
|
15
|
+
>
|
|
10
16
|
<slot />
|
|
11
17
|
</component>
|
|
12
18
|
</template>
|
|
@@ -192,10 +192,10 @@ function onCvPanelFocusout(payload) {
|
|
|
192
192
|
item => item.ariaControls === srcComponent.id
|
|
193
193
|
);
|
|
194
194
|
if (
|
|
195
|
-
srcComponent.el !== srcEvent.relatedTarget &&
|
|
196
|
-
!srcComponent.el.contains(srcEvent.relatedTarget) &&
|
|
195
|
+
srcComponent.el.value !== srcEvent.relatedTarget &&
|
|
196
|
+
!srcComponent.el.value.contains(srcEvent.relatedTarget) &&
|
|
197
197
|
found &&
|
|
198
|
-
found.el !== srcEvent.relatedTarget
|
|
198
|
+
found.el.value !== srcEvent.relatedTarget
|
|
199
199
|
) {
|
|
200
200
|
onCvPanelControlToggle(found, false);
|
|
201
201
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<li
|
|
3
3
|
ref="el"
|
|
4
4
|
:class="`cv-header-menu ${carbonPrefix}--header__submenu`"
|
|
5
|
+
role="menuitem"
|
|
5
6
|
@mouseenter="doHoverToggle(true)"
|
|
6
7
|
@mouseleave="doHoverToggle(false)"
|
|
7
8
|
>
|
|
@@ -10,9 +11,9 @@
|
|
|
10
11
|
:aria-expanded="data.expanded ? 'true' : 'false'"
|
|
11
12
|
:class="`${carbonPrefix}--header__menu-item ${carbonPrefix}--header__menu-title`"
|
|
12
13
|
href="javascript:void(0)"
|
|
13
|
-
role="menuitem"
|
|
14
14
|
tabindex="0"
|
|
15
15
|
:aria-label="$attrs.ariaLabel"
|
|
16
|
+
role="button"
|
|
16
17
|
@click="doToggle"
|
|
17
18
|
@keydown.space.prevent
|
|
18
19
|
@keyup.space.prevent="doToggle"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<li class="cv-header-menu-item"
|
|
2
|
+
<li class="cv-header-menu-item" role="none">
|
|
3
3
|
<component
|
|
4
4
|
:is="tagType"
|
|
5
5
|
v-bind="{ ...$attrs, ...linkProps }"
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
`${carbonPrefix}--header__menu-item`,
|
|
8
8
|
{ [`${carbonPrefix}--header__menu-item--current`]: activePage },
|
|
9
9
|
]"
|
|
10
|
-
role="menuitem"
|
|
11
10
|
:aria-current="ariaCurrent"
|
|
11
|
+
:role="role"
|
|
12
|
+
:tabindex="tabindex"
|
|
12
13
|
>
|
|
13
14
|
<span :class="`${carbonPrefix}--text-truncate--end`">
|
|
14
15
|
<slot />
|
|
@@ -25,7 +26,7 @@ import { computed } from 'vue';
|
|
|
25
26
|
const props = defineProps({
|
|
26
27
|
active: Boolean,
|
|
27
28
|
ariaCurrent: { type: String, default: undefined },
|
|
28
|
-
role: { type: String, default:
|
|
29
|
+
role: { type: String, default: 'menuitem' },
|
|
29
30
|
...propsLink,
|
|
30
31
|
});
|
|
31
32
|
|
|
@@ -35,4 +36,8 @@ const linkProps = useLinkProps(props);
|
|
|
35
36
|
const activePage = computed(() => {
|
|
36
37
|
return props.active && props.ariaCurrent !== 'page';
|
|
37
38
|
});
|
|
39
|
+
|
|
40
|
+
const tabindex = computed(() => {
|
|
41
|
+
return props.role === 'menuitem' ? '0' : undefined;
|
|
42
|
+
});
|
|
38
43
|
</script>
|
|
@@ -3,16 +3,25 @@
|
|
|
3
3
|
<ul
|
|
4
4
|
:class="`${carbonPrefix}--header__menu-bar`"
|
|
5
5
|
role="menubar"
|
|
6
|
-
|
|
6
|
+
aria-orientation="horizontal"
|
|
7
|
+
:aria-label="ariaLabel || $attrs['aria-label']"
|
|
8
|
+
:aria-labelledby="ariaLabelledBy || $attrs['aria-labelledby']"
|
|
7
9
|
>
|
|
8
10
|
<slot />
|
|
9
11
|
</ul>
|
|
10
12
|
</nav>
|
|
11
13
|
</template>
|
|
12
14
|
|
|
15
|
+
<script>
|
|
16
|
+
export default {
|
|
17
|
+
inheritAttrs: false,
|
|
18
|
+
};
|
|
19
|
+
</script>
|
|
20
|
+
|
|
13
21
|
<script setup>
|
|
14
22
|
import { carbonPrefix } from '../../global/settings';
|
|
15
23
|
defineProps({
|
|
24
|
+
ariaLabel: { type: String, default: undefined },
|
|
16
25
|
ariaLabelledBy: { type: String, default: undefined },
|
|
17
26
|
});
|
|
18
27
|
</script>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
{ [`${carbonPrefix}--header-panel--expanded`]: panelExpanded },
|
|
9
9
|
]"
|
|
10
10
|
:aria-hidden="!panelExpanded ? 'true' : 'false'"
|
|
11
|
+
:inert="!panelExpanded"
|
|
11
12
|
@focusout="onFocusout"
|
|
12
13
|
@mousedown="onMouseDown"
|
|
13
14
|
>
|
|
@@ -20,6 +21,7 @@ import { carbonPrefix } from '../../global/settings';
|
|
|
20
21
|
import {
|
|
21
22
|
computed,
|
|
22
23
|
inject,
|
|
24
|
+
nextTick,
|
|
23
25
|
onBeforeUnmount,
|
|
24
26
|
onMounted,
|
|
25
27
|
reactive,
|
|
@@ -99,9 +101,49 @@ watch(
|
|
|
99
101
|
}
|
|
100
102
|
);
|
|
101
103
|
|
|
104
|
+
function manageFocusableElements(isExpanded) {
|
|
105
|
+
if (!el.value) return;
|
|
106
|
+
|
|
107
|
+
const focusableElements = el.value.querySelectorAll(
|
|
108
|
+
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
focusableElements.forEach(element => {
|
|
112
|
+
if (isExpanded) {
|
|
113
|
+
// Restore original tabindex when expanded
|
|
114
|
+
const originalTabindex = element.getAttribute('data-original-tabindex');
|
|
115
|
+
if (originalTabindex !== null) {
|
|
116
|
+
if (originalTabindex === 'null') {
|
|
117
|
+
element.removeAttribute('tabindex');
|
|
118
|
+
} else {
|
|
119
|
+
element.setAttribute('tabindex', originalTabindex);
|
|
120
|
+
}
|
|
121
|
+
element.removeAttribute('data-original-tabindex');
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
// Store original tabindex and set to -1 when collapsed
|
|
125
|
+
const currentTabindex = element.getAttribute('tabindex');
|
|
126
|
+
element.setAttribute('data-original-tabindex', currentTabindex || 'null');
|
|
127
|
+
element.setAttribute('tabindex', '-1');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
102
132
|
const emit = defineEmits(['update:expanded', 'panel-resize']);
|
|
103
133
|
watch(panelExpanded, current => {
|
|
104
134
|
emit('update:expanded', current);
|
|
105
135
|
emit('panel-resize', { id: props.id, expanded: current });
|
|
136
|
+
|
|
137
|
+
// Manage focusable elements
|
|
138
|
+
nextTick(() => {
|
|
139
|
+
manageFocusableElements(current);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Initial setup of focusable elements
|
|
144
|
+
onMounted(() => {
|
|
145
|
+
nextTick(() => {
|
|
146
|
+
manageFocusableElements(panelExpanded.value);
|
|
147
|
+
});
|
|
106
148
|
});
|
|
107
149
|
</script>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
]"
|
|
16
16
|
:aria-hidden="!panelExpanded && !fixed ? 'true' : 'false'"
|
|
17
|
+
:inert="!panelExpanded && !fixed"
|
|
17
18
|
@focusout="onFocusout"
|
|
18
19
|
@mousedown="onMouseDown"
|
|
19
20
|
@mouseenter="onHoverToggle(true)"
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
import {
|
|
34
35
|
computed,
|
|
35
36
|
inject,
|
|
37
|
+
nextTick,
|
|
36
38
|
onBeforeUnmount,
|
|
37
39
|
onMounted,
|
|
38
40
|
reactive,
|
|
@@ -110,6 +112,35 @@ watch(
|
|
|
110
112
|
panelExpanded.value = props.expanded;
|
|
111
113
|
}
|
|
112
114
|
);
|
|
115
|
+
// Manage focusable elements when panel visibility changes
|
|
116
|
+
function manageFocusableElements(isExpanded, isFixed) {
|
|
117
|
+
if (!el.value || isFixed) return;
|
|
118
|
+
|
|
119
|
+
const focusableElements = el.value.querySelectorAll(
|
|
120
|
+
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
focusableElements.forEach(element => {
|
|
124
|
+
if (isExpanded) {
|
|
125
|
+
// Restore original tabindex when expanded
|
|
126
|
+
const originalTabindex = element.getAttribute('data-original-tabindex');
|
|
127
|
+
if (originalTabindex !== null) {
|
|
128
|
+
if (originalTabindex === 'null') {
|
|
129
|
+
element.removeAttribute('tabindex');
|
|
130
|
+
} else {
|
|
131
|
+
element.setAttribute('tabindex', originalTabindex);
|
|
132
|
+
}
|
|
133
|
+
element.removeAttribute('data-original-tabindex');
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
// Store original tabindex and set to -1 when collapsed
|
|
137
|
+
const currentTabindex = element.getAttribute('tabindex');
|
|
138
|
+
element.setAttribute('data-original-tabindex', currentTabindex || 'null');
|
|
139
|
+
element.setAttribute('tabindex', '-1');
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
113
144
|
const emit = defineEmits(['update:expanded', 'panel-resize']);
|
|
114
145
|
const isPanelExpanded = computed(
|
|
115
146
|
() => panelExpanded.value || expandedViaHoverState.value
|
|
@@ -117,6 +148,11 @@ const isPanelExpanded = computed(
|
|
|
117
148
|
watch(isPanelExpanded, current => {
|
|
118
149
|
emit('update:expanded', current);
|
|
119
150
|
emit('panel-resize', { id: props.id, expanded: current });
|
|
151
|
+
|
|
152
|
+
// Manage focusable elements for non-fixed nav
|
|
153
|
+
nextTick(() => {
|
|
154
|
+
manageFocusableElements(current, props.fixed);
|
|
155
|
+
});
|
|
120
156
|
});
|
|
121
157
|
|
|
122
158
|
const isChildOfHeader = computed(() => {
|
|
@@ -19,6 +19,7 @@ import CvSideNavLink from './CvSideNavLink.vue';
|
|
|
19
19
|
import CvSideNavMenu from './CvSideNavMenu.vue';
|
|
20
20
|
import CvSideNavMenuDivider from './CvSideNavMenuDivider.vue';
|
|
21
21
|
import CvSideNavMenuItem from './CvSideNavMenuItem.vue';
|
|
22
|
+
import CvContent from './CvContent.vue';
|
|
22
23
|
import {
|
|
23
24
|
Notification20,
|
|
24
25
|
UserAvatar20,
|
|
@@ -55,6 +56,7 @@ export const Template = args => ({
|
|
|
55
56
|
CvSideNavMenu,
|
|
56
57
|
CvSideNavMenuDivider,
|
|
57
58
|
CvSideNavMenuItem,
|
|
59
|
+
CvContent,
|
|
58
60
|
Notification20,
|
|
59
61
|
UserAvatar20,
|
|
60
62
|
Login20,
|
|
@@ -89,22 +91,12 @@ const defaultTemplate = `
|
|
|
89
91
|
[Platform]
|
|
90
92
|
</cv-header-name>
|
|
91
93
|
<cv-header-nav aria-label="Carbon nav">
|
|
92
|
-
<cv-header-menu-item href="javascript:void(0)">
|
|
93
|
-
|
|
94
|
-
</cv-header-menu-item>
|
|
95
|
-
<cv-header-menu-item href="javascript:void(0)">
|
|
96
|
-
Link 2
|
|
97
|
-
</cv-header-menu-item>
|
|
94
|
+
<cv-header-menu-item href="javascript:void(0)">Link 1</cv-header-menu-item>
|
|
95
|
+
<cv-header-menu-item href="javascript:void(0)">Link 2</cv-header-menu-item>
|
|
98
96
|
<cv-header-menu aria-label="Link 3" title="Link 3">
|
|
99
|
-
<cv-header-menu-item href="javascript:void(0)">
|
|
100
|
-
|
|
101
|
-
</cv-header-menu-item>
|
|
102
|
-
<cv-header-menu-item href="javascript:void(0)">
|
|
103
|
-
Submenu Link 2
|
|
104
|
-
</cv-header-menu-item>
|
|
105
|
-
<cv-header-menu-item href="javascript:void(0)">
|
|
106
|
-
Submenu Link 3
|
|
107
|
-
</cv-header-menu-item>
|
|
97
|
+
<cv-header-menu-item href="javascript:void(0)">Submenu Link 1</cv-header-menu-item>
|
|
98
|
+
<cv-header-menu-item href="javascript:void(0)">Submenu Link 2</cv-header-menu-item>
|
|
99
|
+
<cv-header-menu-item href="javascript:void(0)">Submenu Link 3</cv-header-menu-item>
|
|
108
100
|
</cv-header-menu>
|
|
109
101
|
</cv-header-nav>
|
|
110
102
|
<template v-slot:header-global>
|
|
@@ -162,6 +154,10 @@ const defaultTemplate = `
|
|
|
162
154
|
</cv-header-panel>
|
|
163
155
|
</template>
|
|
164
156
|
</cv-header>
|
|
157
|
+
<cv-content>
|
|
158
|
+
<h1>Main Content</h1>
|
|
159
|
+
<p>This is the main content area that the skip link targets.</p>
|
|
160
|
+
</cv-content>
|
|
165
161
|
`;
|
|
166
162
|
const railTemplate = `
|
|
167
163
|
<cv-header aria-label="Carbon header">
|
|
@@ -196,6 +192,10 @@ const railTemplate = `
|
|
|
196
192
|
</cv-side-nav>
|
|
197
193
|
</template>
|
|
198
194
|
</cv-header>
|
|
195
|
+
<cv-content>
|
|
196
|
+
<h1>Main Content</h1>
|
|
197
|
+
<p>This is the main content area that the skip link targets.</p>
|
|
198
|
+
</cv-content>
|
|
199
199
|
`;
|
|
200
200
|
const fixedSideNavTemplate = `
|
|
201
201
|
<cv-header aria-label="Carbon header">
|
|
@@ -231,6 +231,10 @@ const fixedSideNavTemplate = `
|
|
|
231
231
|
</cv-side-nav-link>
|
|
232
232
|
</cv-side-nav-items>
|
|
233
233
|
</cv-side-nav>
|
|
234
|
+
<cv-content>
|
|
235
|
+
<h1>Main Content</h1>
|
|
236
|
+
<p>This is the main content area that the skip link targets.</p>
|
|
237
|
+
</cv-content>
|
|
234
238
|
`;
|
|
235
239
|
|
|
236
240
|
# UI Shell components
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import CvHeader from '../CvHeader.vue';
|
|
3
|
+
import CvHeaderGlobalAction from '../CvHeaderGlobalAction.vue';
|
|
4
|
+
import CvHeaderPanel from '../CvHeaderPanel.vue';
|
|
5
|
+
import CvSwitcher from '../CvSwitcher.vue';
|
|
6
|
+
import CvSwitcherItem from '../CvSwitcherItem.vue';
|
|
7
|
+
import CvSwitcherItemLink from '../CvSwitcherItemLink.vue';
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<cv-header>
|
|
12
|
+
<template #header-global>
|
|
13
|
+
<cv-header-global-action
|
|
14
|
+
data-testid="action-button"
|
|
15
|
+
aria-label="List links"
|
|
16
|
+
aria-controls="links-panel"
|
|
17
|
+
label="Links"
|
|
18
|
+
>
|
|
19
|
+
Links
|
|
20
|
+
</cv-header-global-action>
|
|
21
|
+
</template>
|
|
22
|
+
<template #right-panels>
|
|
23
|
+
<cv-header-panel id="links-panel" data-testid="links-panel">
|
|
24
|
+
<cv-switcher>
|
|
25
|
+
<cv-switcher-item>
|
|
26
|
+
<cv-switcher-item-link
|
|
27
|
+
href="javascript:void(0)"
|
|
28
|
+
data-testid="link-1"
|
|
29
|
+
selected
|
|
30
|
+
>
|
|
31
|
+
Selected app
|
|
32
|
+
</cv-switcher-item-link>
|
|
33
|
+
</cv-switcher-item>
|
|
34
|
+
<cv-switcher-item>
|
|
35
|
+
<cv-switcher-item-link
|
|
36
|
+
href="javascript:void(0)"
|
|
37
|
+
data-testid="link-2"
|
|
38
|
+
>
|
|
39
|
+
Other app
|
|
40
|
+
</cv-switcher-item-link>
|
|
41
|
+
</cv-switcher-item>
|
|
42
|
+
</cv-switcher>
|
|
43
|
+
</cv-header-panel>
|
|
44
|
+
</template>
|
|
45
|
+
</cv-header>
|
|
46
|
+
<input type="text" data-testid="outer-input" />
|
|
47
|
+
</template>
|
package/src/index.js
CHANGED
|
@@ -158,3 +158,4 @@ export { default as CvSkipToContent } from './components/CvUIShell/CvSkipToConte
|
|
|
158
158
|
export { default as CvSwitcher } from './components/CvUIShell/CvSwitcher.vue';
|
|
159
159
|
export { default as CvSwitcherItem } from './components/CvUIShell/CvSwitcherItem.vue';
|
|
160
160
|
export { default as CvSwitcherItemLink } from './components/CvUIShell/CvSwitcherItemLink.vue';
|
|
161
|
+
export { default as PanelFocusTestComponent } from './components/CvUIShell/__tests__/PanelFocusTestComponent.vue';
|