@webitel/ui-sdk 26.8.139 → 26.8.140
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/{clients-Bk3DA6vL.js → clients-B87Nx03m.js} +10 -7
- package/dist/{components-Bzxyigqb.js → components-peYTVwLb.js} +1235 -1153
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.js +1 -1
- package/dist/ui-sdk.umd.cjs +524 -524
- package/dist/{wt-chat-emoji-DqLArBt8.js → wt-chat-emoji-D72hYTJU.js} +1 -1
- package/dist/{wt-display-chip-items-CYABNzDH.js → wt-display-chip-items-DwdzIwJH.js} +1 -1
- package/dist/{wt-send-message-popup-B6VaAW53.js → wt-send-message-popup-D3KQ6fqe.js} +2 -2
- package/dist/{wt-type-extension-value-input-DAPDUmhp.js → wt-type-extension-value-input-BQjSYAdt.js} +2 -2
- package/dist/{wt-vidstack-player-DSgFPUrJ.js → wt-vidstack-player-DwS6IkZJ.js} +1 -1
- package/package.json +1 -1
- package/src/components/index.js +3 -0
- package/src/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.js +90 -0
- package/src/components/wt-navigation-rail/_internals/navigation-rail-button.vue +79 -0
- package/src/components/wt-navigation-rail/_variables.css +45 -0
- package/src/components/wt-navigation-rail/types/WtNavigationRail.ts +19 -0
- package/src/components/wt-navigation-rail/wt-navigation-rail.vue +108 -0
- package/src/components/wt-page/__tests__/WtPage.spec.js +60 -1
- package/src/components/wt-page/wt-page.vue +40 -3
- package/src/css/components-variables.css +1 -0
- package/types/.tsbuildinfo +1 -1
- package/types/components/index.d.ts +3 -1
- package/types/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.d.ts +1 -0
- package/types/components/wt-navigation-rail/_internals/navigation-rail-button.vue.d.ts +12 -0
- package/types/components/wt-navigation-rail/types/WtNavigationRail.d.ts +17 -0
- package/types/components/wt-navigation-rail/wt-navigation-rail.vue.d.ts +25 -0
- package/types/components/wt-page/wt-page.vue.d.ts +20 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as e, i as t } from "./components-
|
|
1
|
+
import { C as e, i as t } from "./components-peYTVwLb.js";
|
|
2
2
|
import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
|
|
3
3
|
import { t as r } from "./utils-DQQQIpjz.js";
|
|
4
4
|
import { Fragment as i, computed as a, createCommentVNode as o, createElementBlock as s, createTextVNode as c, createVNode as l, defineComponent as u, openBlock as d, renderList as f, renderSlot as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as e } from "./components-
|
|
1
|
+
import { n as e } from "./components-peYTVwLb.js";
|
|
2
2
|
import "./convertDuration-CIPonKKW.js";
|
|
3
|
-
import { i as t, r as n } from "./clients-
|
|
3
|
+
import { i as t, r as n } from "./clients-B87Nx03m.js";
|
|
4
4
|
import { n as r } from "./vue-i18n-VBiROMZ4.js";
|
|
5
5
|
import { n as i } from "./useEventBus-B-ZeklLu.js";
|
|
6
6
|
import { createBlock as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, onMounted as u, openBlock as d, ref as f, resolveComponent as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
|
package/dist/{wt-type-extension-value-input-DAPDUmhp.js → wt-type-extension-value-input-BQjSYAdt.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as e, c as t, i as n, n as r, r as i, s as a, t as o } from "./dist-EXdgfpvN.js";
|
|
2
2
|
import { n as s } from "./genClient-CvWlQ0TZ.js";
|
|
3
3
|
import { a as c } from "./caseConverters-DGri4uJo.js";
|
|
4
|
-
import { a as l, o as u, r as d, s as f } from "./components-
|
|
5
|
-
import { a as p, n as m, t as h } from "./clients-
|
|
4
|
+
import { a as l, o as u, r as d, s as f } from "./components-peYTVwLb.js";
|
|
5
|
+
import { a as p, n as m, t as h } from "./clients-B87Nx03m.js";
|
|
6
6
|
import { S as g, _, a as v, c as y, f as b, i as x, n as S, o as C, p as w, r as T, s as E, t as D, x as O } from "./enums-CKer6GJR.js";
|
|
7
7
|
import { n as ee, t as te } from "./vue-i18n-VBiROMZ4.js";
|
|
8
8
|
import "./wt-toast-CAoQkSuJ.js";
|
package/package.json
CHANGED
package/src/components/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import WtLogo from './wt-logo/wt-logo.vue';
|
|
|
38
38
|
import WtMessage from './wt-message/wt-message.vue';
|
|
39
39
|
import WtMultiSelect from './wt-multi-select/wt-multi-select.vue';
|
|
40
40
|
import WtNavigationBar from './wt-navigation-bar/wt-navigation-bar.vue';
|
|
41
|
+
import WtNavigationRail from './wt-navigation-rail/wt-navigation-rail.vue';
|
|
41
42
|
import WtPage from './wt-page/wt-page.vue';
|
|
42
43
|
import WtPageWrapper from './wt-page-wrapper/wt-page-wrapper.vue';
|
|
43
44
|
import WtPassword from './wt-password/wt-password.vue';
|
|
@@ -279,6 +280,7 @@ const Components = {
|
|
|
279
280
|
WtHeaderActions,
|
|
280
281
|
WtErrorPage,
|
|
281
282
|
WtNotificationsBar,
|
|
283
|
+
WtNavigationRail,
|
|
282
284
|
WtPage,
|
|
283
285
|
WtPageWrapper,
|
|
284
286
|
WtDualPanel,
|
|
@@ -374,6 +376,7 @@ export {
|
|
|
374
376
|
WtMultiSelect,
|
|
375
377
|
WtNavigationBar,
|
|
376
378
|
WtNavigationMenu,
|
|
379
|
+
WtNavigationRail,
|
|
377
380
|
WtNotificationsBar,
|
|
378
381
|
WtPage,
|
|
379
382
|
WtPageHeader,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
|
|
3
|
+
import WtNavigationRail from '../wt-navigation-rail.vue';
|
|
4
|
+
|
|
5
|
+
const top = [
|
|
6
|
+
{
|
|
7
|
+
id: 'home',
|
|
8
|
+
icon: 'home',
|
|
9
|
+
label: 'Home',
|
|
10
|
+
badge: {
|
|
11
|
+
value: 1,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'calls',
|
|
16
|
+
icon: 'call',
|
|
17
|
+
label: 'Calls',
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
const bottom = [
|
|
21
|
+
{
|
|
22
|
+
id: 'dialer',
|
|
23
|
+
icon: 'call',
|
|
24
|
+
label: 'Dialer',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const mountRail = (props) =>
|
|
29
|
+
mount(WtNavigationRail, {
|
|
30
|
+
props,
|
|
31
|
+
global: {
|
|
32
|
+
stubs: {
|
|
33
|
+
WtButton: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('WtNavigationRail', () => {
|
|
39
|
+
it('renders top and bottom buttons', () => {
|
|
40
|
+
const wrapper = mountRail({
|
|
41
|
+
topItems: top,
|
|
42
|
+
bottomItems: bottom,
|
|
43
|
+
});
|
|
44
|
+
const groups = wrapper.findAll('.wt-navigation-rail__group');
|
|
45
|
+
expect(groups[0].findAll('.navigation-rail-button')).toHaveLength(2);
|
|
46
|
+
expect(groups[1].findAll('.navigation-rail-button')).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('marks active item', () => {
|
|
50
|
+
const wrapper = mountRail({
|
|
51
|
+
topItems: top,
|
|
52
|
+
bottomItems: bottom,
|
|
53
|
+
activeItemId: 'calls',
|
|
54
|
+
});
|
|
55
|
+
const active = wrapper.findAll('.navigation-rail-button--active');
|
|
56
|
+
expect(active).toHaveLength(1);
|
|
57
|
+
expect(active[0].attributes('aria-label')).toBe('Calls');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders badge only for items with badge', () => {
|
|
61
|
+
const wrapper = mountRail({
|
|
62
|
+
topItems: top,
|
|
63
|
+
});
|
|
64
|
+
expect(wrapper.findAll('.wt-badge')).toHaveLength(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders nothing while both lists are empty', () => {
|
|
68
|
+
const wrapper = mountRail({});
|
|
69
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders when only one list is not empty', () => {
|
|
73
|
+
const wrapper = mountRail({
|
|
74
|
+
bottomItems: bottom,
|
|
75
|
+
});
|
|
76
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(true);
|
|
77
|
+
expect(wrapper.findAll('.wt-navigation-rail__group')).toHaveLength(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('emits select on click', async () => {
|
|
81
|
+
const wrapper = mountRail({
|
|
82
|
+
topItems: top,
|
|
83
|
+
bottomItems: bottom,
|
|
84
|
+
});
|
|
85
|
+
await wrapper.findAll('button')[2].trigger('click');
|
|
86
|
+
expect(wrapper.emitted('select')[0]).toEqual([
|
|
87
|
+
bottom[0],
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-tooltip :disabled="!item.label" placement="right">
|
|
3
|
+
<template #activator>
|
|
4
|
+
<wt-button
|
|
5
|
+
:class="{ 'navigation-rail-button--active': active }"
|
|
6
|
+
:aria-label="item.label"
|
|
7
|
+
:aria-current="active ? 'page' : undefined"
|
|
8
|
+
:badge="item.badge ? String(item.badge.value) : undefined"
|
|
9
|
+
:badge-severity="item.badge?.severity ?? 'success'"
|
|
10
|
+
:disabled="item.disabled"
|
|
11
|
+
:icon="item.icon"
|
|
12
|
+
:size="ComponentSize.SM"
|
|
13
|
+
:variant="ButtonVariant.TEXT"
|
|
14
|
+
class="navigation-rail-button"
|
|
15
|
+
@click="emit('select', item)"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
18
|
+
{{ item.label }}
|
|
19
|
+
</wt-tooltip>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { ButtonVariant, ComponentSize } from '../../../enums';
|
|
24
|
+
import WtButton from '../../wt-button/wt-button.vue';
|
|
25
|
+
import WtTooltip from '../../wt-tooltip/wt-tooltip.vue';
|
|
26
|
+
import type { NavigationRailItem } from '../types/WtNavigationRail';
|
|
27
|
+
|
|
28
|
+
defineProps<{
|
|
29
|
+
item: NavigationRailItem;
|
|
30
|
+
active?: boolean;
|
|
31
|
+
}>();
|
|
32
|
+
|
|
33
|
+
const emit = defineEmits<{
|
|
34
|
+
select: [
|
|
35
|
+
item: NavigationRailItem,
|
|
36
|
+
];
|
|
37
|
+
}>();
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style scoped>
|
|
41
|
+
/* wt-button theme colors are overridden with navigation rail tokens */
|
|
42
|
+
.navigation-rail-button.p-button.p-button-text {
|
|
43
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: var(--wt-navigation-rail-button-icon-color);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.navigation-rail-button.p-button.p-button-text:not(:disabled):hover {
|
|
49
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
50
|
+
background: var(--wt-navigation-rail-button-background--hover);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.navigation-rail-button.p-button.p-button-text:not(:disabled):active {
|
|
54
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
55
|
+
background: var(--wt-navigation-rail-button-background--pressed);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.navigation-rail-button--active.p-button.p-button-text,
|
|
59
|
+
.navigation-rail-button--active.p-button.p-button-text:not(:disabled):active {
|
|
60
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color--active);
|
|
61
|
+
background: var(--wt-navigation-rail-button-background--active);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.navigation-rail-button--active.p-button.p-button-text:not(:disabled):hover {
|
|
65
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color--active-hover);
|
|
66
|
+
background: var(--wt-navigation-rail-button-background--active-hover);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.navigation-rail-button :deep(.wt-badge) {
|
|
70
|
+
position: absolute;
|
|
71
|
+
top: var(--wt-navigation-rail-badge-offset-y);
|
|
72
|
+
left: var(--wt-navigation-rail-badge-offset-x);
|
|
73
|
+
/* p-badge sets own width/min-width (1.5rem), override to match design */
|
|
74
|
+
width: auto;
|
|
75
|
+
min-width: var(--wt-navigation-rail-badge-size);
|
|
76
|
+
height: var(--wt-navigation-rail-badge-size);
|
|
77
|
+
padding: 0 var(--wt-navigation-rail-badge-padding-x);
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--wt-navigation-rail-width: 48px;
|
|
3
|
+
--wt-navigation-rail-padding-x: 8px;
|
|
4
|
+
--wt-navigation-rail-padding-y: 8px;
|
|
5
|
+
--wt-navigation-rail-gap: 8px;
|
|
6
|
+
--wt-navigation-rail-border-radius: 0;
|
|
7
|
+
--wt-navigation-rail-background: linear-gradient(
|
|
8
|
+
to top right,
|
|
9
|
+
rgba(20, 34, 184, 1) 0%,
|
|
10
|
+
rgba(192, 66, 215, 1) 100%
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
--wt-navigation-rail-button-icon-color: var(--p-indigo-50);
|
|
14
|
+
--wt-navigation-rail-button-background--hover: var(--p-deep-purple-600);
|
|
15
|
+
--wt-navigation-rail-button-background--pressed: var(--p-deep-purple-500);
|
|
16
|
+
--wt-navigation-rail-button-icon-color--active: var(--p-deep-purple-600);
|
|
17
|
+
--wt-navigation-rail-button-background--active: var(--p-gray-50);
|
|
18
|
+
--wt-navigation-rail-button-icon-color--active-hover: var(
|
|
19
|
+
--p-deep-purple-550
|
|
20
|
+
);
|
|
21
|
+
--wt-navigation-rail-button-background--active-hover: var(--p-white);
|
|
22
|
+
|
|
23
|
+
--wt-navigation-rail-badge-size: 16px;
|
|
24
|
+
--wt-navigation-rail-badge-padding-x: var(--spacing-2xs);
|
|
25
|
+
--wt-navigation-rail-badge-offset-x: 16px;
|
|
26
|
+
--wt-navigation-rail-badge-offset-y: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:root.theme--dark {
|
|
30
|
+
--wt-navigation-rail-background: linear-gradient(
|
|
31
|
+
to top right,
|
|
32
|
+
rgba(10, 17, 92, 1) 0%,
|
|
33
|
+
rgba(111, 27, 126, 1) 100%
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
--wt-navigation-rail-button-icon-color: var(--p-deep-purple-150);
|
|
37
|
+
--wt-navigation-rail-button-background--hover: var(--p-deep-purple-650);
|
|
38
|
+
--wt-navigation-rail-button-background--pressed: var(--p-deep-purple-500);
|
|
39
|
+
--wt-navigation-rail-button-icon-color--active: var(--p-white);
|
|
40
|
+
--wt-navigation-rail-button-background--active: var(--p-deep-purple-800);
|
|
41
|
+
--wt-navigation-rail-button-icon-color--active-hover: var(--p-deep-purple-50);
|
|
42
|
+
--wt-navigation-rail-button-background--active-hover: var(
|
|
43
|
+
--p-deep-purple-750
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BadgeSeverity } from '../../wt-badge-new/types/WtBadge';
|
|
2
|
+
|
|
3
|
+
export interface NavigationRailBadge {
|
|
4
|
+
value: string | number;
|
|
5
|
+
/** @default 'success' */
|
|
6
|
+
severity?: BadgeSeverity;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface NavigationRailItem {
|
|
10
|
+
/** Unique id, emitted on select and matched against `active` */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Icon name from icons repository */
|
|
13
|
+
icon: string;
|
|
14
|
+
/** Accessible label (aria-label + tooltip) */
|
|
15
|
+
label?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Renders badge over the button when set */
|
|
18
|
+
badge?: NavigationRailBadge | null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav
|
|
3
|
+
v-if="hasItems"
|
|
4
|
+
class="wt-navigation-rail"
|
|
5
|
+
aria-label="Navigation rail"
|
|
6
|
+
>
|
|
7
|
+
<template v-for="section of sections" :key="section.name">
|
|
8
|
+
<div
|
|
9
|
+
v-if="section.items.length"
|
|
10
|
+
:class="['wt-navigation-rail__group', `wt-navigation-rail__group--${section.name}`]"
|
|
11
|
+
>
|
|
12
|
+
<ul class="wt-navigation-rail__list">
|
|
13
|
+
<li v-for="item of section.items" :key="item.id">
|
|
14
|
+
<navigation-rail-button
|
|
15
|
+
:item="item"
|
|
16
|
+
:active="item.id === activeItemId"
|
|
17
|
+
@select="emit('select', $event)"
|
|
18
|
+
/>
|
|
19
|
+
</li>
|
|
20
|
+
</ul>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
</nav>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { computed } from 'vue';
|
|
28
|
+
|
|
29
|
+
import NavigationRailButton from './_internals/navigation-rail-button.vue';
|
|
30
|
+
import type { NavigationRailItem } from './types/WtNavigationRail';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Vertical app navigation. Holds no routing logic: emits `select`,
|
|
34
|
+
* host application decides what to do (navigate, open panel, etc.).
|
|
35
|
+
* Lists are data-driven by props. Renders nothing while both lists are empty.
|
|
36
|
+
*/
|
|
37
|
+
const props = withDefaults(
|
|
38
|
+
defineProps<{
|
|
39
|
+
/** Buttons pinned to the top */
|
|
40
|
+
topItems?: NavigationRailItem[];
|
|
41
|
+
/** Buttons pinned to the bottom */
|
|
42
|
+
bottomItems?: NavigationRailItem[];
|
|
43
|
+
/** Id of the active item */
|
|
44
|
+
activeItemId?: string;
|
|
45
|
+
}>(),
|
|
46
|
+
{
|
|
47
|
+
topItems: () => [],
|
|
48
|
+
bottomItems: () => [],
|
|
49
|
+
activeItemId: '',
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const hasItems = computed(
|
|
54
|
+
() => !!props.topItems.length || !!props.bottomItems.length,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const sections = computed(
|
|
58
|
+
() =>
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
name: 'top',
|
|
62
|
+
items: props.topItems,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'bottom',
|
|
66
|
+
items: props.bottomItems,
|
|
67
|
+
},
|
|
68
|
+
] as const,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const emit = defineEmits<{
|
|
72
|
+
/** Fires on click of enabled item */
|
|
73
|
+
select: [
|
|
74
|
+
item: NavigationRailItem,
|
|
75
|
+
];
|
|
76
|
+
}>();
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<style scoped>
|
|
80
|
+
.wt-navigation-rail {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
flex: 0 0 auto;
|
|
84
|
+
gap: var(--wt-navigation-rail-gap);
|
|
85
|
+
box-sizing: border-box;
|
|
86
|
+
background: var(--wt-navigation-rail-background);
|
|
87
|
+
padding: var(--wt-navigation-rail-padding-y) var(--wt-navigation-rail-padding-x);
|
|
88
|
+
width: var(--wt-navigation-rail-width);
|
|
89
|
+
min-height: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.wt-navigation-rail__group--bottom {
|
|
93
|
+
margin-top: auto;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.wt-navigation-rail__group,
|
|
97
|
+
.wt-navigation-rail__list {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: var(--wt-navigation-rail-gap);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.wt-navigation-rail__list {
|
|
104
|
+
margin: 0;
|
|
105
|
+
padding: 0;
|
|
106
|
+
list-style: none;
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
1
|
+
import { mount, shallowMount } from '@vue/test-utils';
|
|
2
2
|
|
|
3
3
|
import WtPage from '../wt-page.vue';
|
|
4
4
|
|
|
@@ -32,4 +32,63 @@ describe('WtPage', () => {
|
|
|
32
32
|
const wrapper = shallowMount(WtPage);
|
|
33
33
|
expect(wrapper.find('.wt-page__header').exists()).toBe(false);
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
describe('navigation rail', () => {
|
|
37
|
+
const navigationRail = {
|
|
38
|
+
topItems: [
|
|
39
|
+
{
|
|
40
|
+
id: 'home',
|
|
41
|
+
icon: 'home',
|
|
42
|
+
label: 'Home',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
activeItemId: 'home',
|
|
46
|
+
};
|
|
47
|
+
const mountPage = (props) =>
|
|
48
|
+
mount(WtPage, {
|
|
49
|
+
props,
|
|
50
|
+
global: {
|
|
51
|
+
stubs: {
|
|
52
|
+
WtButton: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('does not render rail without navigationRail prop', () => {
|
|
58
|
+
const wrapper = mountPage({});
|
|
59
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not render rail while both lists are empty', () => {
|
|
63
|
+
const wrapper = mountPage({
|
|
64
|
+
navigationRail: {
|
|
65
|
+
topItems: [],
|
|
66
|
+
bottomItems: [],
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders rail outside of the body when a list is not empty', () => {
|
|
73
|
+
const wrapper = mountPage({
|
|
74
|
+
navigationRail,
|
|
75
|
+
});
|
|
76
|
+
expect(
|
|
77
|
+
wrapper.find('.wt-page__main > .wt-navigation-rail').exists(),
|
|
78
|
+
).toBe(true);
|
|
79
|
+
expect(wrapper.find('.wt-page__body .wt-navigation-rail').exists()).toBe(
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('emits navigation-select on rail item click', async () => {
|
|
85
|
+
const wrapper = mountPage({
|
|
86
|
+
navigationRail,
|
|
87
|
+
});
|
|
88
|
+
await wrapper.find('button').trigger('click');
|
|
89
|
+
expect(wrapper.emitted('navigation-select')[0]).toEqual([
|
|
90
|
+
navigationRail.topItems[0],
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
35
94
|
});
|
|
@@ -3,18 +3,48 @@
|
|
|
3
3
|
<div v-if="$slots.header" class="wt-page__header">
|
|
4
4
|
<slot name="header" />
|
|
5
5
|
</div>
|
|
6
|
-
<div class="wt-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
<div class="wt-page__main">
|
|
7
|
+
<wt-navigation-rail
|
|
8
|
+
v-if="navigationRail"
|
|
9
|
+
:top-items="navigationRail.topItems"
|
|
10
|
+
:bottom-items="navigationRail.bottomItems"
|
|
11
|
+
:active-item-id="navigationRail.activeItemId"
|
|
12
|
+
@select="emit('navigation-select', $event)"
|
|
13
|
+
/>
|
|
14
|
+
<div class="wt-page__body">
|
|
15
|
+
<!-- @slot Layouts of the page -->
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
9
18
|
</div>
|
|
10
19
|
</div>
|
|
11
20
|
</template>
|
|
12
21
|
|
|
13
22
|
<script setup lang="ts">
|
|
23
|
+
import type { NavigationRailItem } from '../wt-navigation-rail/types/WtNavigationRail';
|
|
24
|
+
import WtNavigationRail from '../wt-navigation-rail/wt-navigation-rail.vue';
|
|
25
|
+
|
|
14
26
|
/**
|
|
15
27
|
* Top structural level of the interface: header + body, where the body
|
|
16
28
|
* distributes its width between one or more `wt-layout`s.
|
|
29
|
+
* Optional navigation rail is shown at the left of the body and
|
|
30
|
+
* is hidden while both of its lists are empty.
|
|
17
31
|
*/
|
|
32
|
+
defineProps<{
|
|
33
|
+
/** Navigation rail config, rail is not rendered when omitted */
|
|
34
|
+
navigationRail?: {
|
|
35
|
+
topItems?: NavigationRailItem[];
|
|
36
|
+
bottomItems?: NavigationRailItem[];
|
|
37
|
+
activeItemId?: string;
|
|
38
|
+
};
|
|
39
|
+
}>();
|
|
40
|
+
|
|
41
|
+
const emit = defineEmits<{
|
|
42
|
+
/** Fires on click of enabled navigation rail item */
|
|
43
|
+
'navigation-select': [
|
|
44
|
+
item: NavigationRailItem,
|
|
45
|
+
];
|
|
46
|
+
}>();
|
|
47
|
+
|
|
18
48
|
defineSlots<{
|
|
19
49
|
header?: () => unknown;
|
|
20
50
|
default?: () => unknown;
|
|
@@ -38,6 +68,13 @@ defineSlots<{
|
|
|
38
68
|
flex: 0 0 auto;
|
|
39
69
|
}
|
|
40
70
|
|
|
71
|
+
.wt-page__main {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex: 1 1 auto;
|
|
74
|
+
min-width: 0;
|
|
75
|
+
min-height: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
.wt-page__body {
|
|
42
79
|
display: flex;
|
|
43
80
|
position: relative;
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
@import "../components/wt-loader/_variables.css";
|
|
32
32
|
@import "../components/wt-logo/_variables.css";
|
|
33
33
|
@import "../components/wt-navigation-bar/_variables.css";
|
|
34
|
+
@import "../components/wt-navigation-rail/_variables.css";
|
|
34
35
|
@import "../components/wt-notifications-bar/_variables.css";
|
|
35
36
|
@import "../components/wt-page/_variables.css";
|
|
36
37
|
@import "../components/wt-page-wrapper/_variables.css";
|