@webitel/ui-sdk 26.8.139 → 26.8.141

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.
Files changed (33) hide show
  1. package/dist/{clients-Bk3DA6vL.js → clients-7bjVyMfb.js} +10 -7
  2. package/dist/{components-Bzxyigqb.js → components-BGwoOBLk.js} +1235 -1153
  3. package/dist/ui-sdk.css +1 -1
  4. package/dist/ui-sdk.js +1 -1
  5. package/dist/ui-sdk.umd.cjs +524 -524
  6. package/dist/{wt-chat-emoji-DqLArBt8.js → wt-chat-emoji-CQt62UaT.js} +1 -1
  7. package/dist/{wt-display-chip-items-CYABNzDH.js → wt-display-chip-items-D4sngyGn.js} +1 -1
  8. package/dist/{wt-send-message-popup-B6VaAW53.js → wt-send-message-popup-BlKGak9b.js} +2 -2
  9. package/dist/{wt-type-extension-value-input-DAPDUmhp.js → wt-type-extension-value-input-PyuOjjR2.js} +8 -8
  10. package/dist/{wt-vidstack-player-DSgFPUrJ.js → wt-vidstack-player-CHjMJ7iv.js} +1 -1
  11. package/package.json +1 -1
  12. package/src/components/index.js +3 -0
  13. package/src/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.js +90 -0
  14. package/src/components/wt-navigation-rail/_internals/navigation-rail-button.vue +79 -0
  15. package/src/components/wt-navigation-rail/_variables.css +45 -0
  16. package/src/components/wt-navigation-rail/types/WtNavigationRail.ts +19 -0
  17. package/src/components/wt-navigation-rail/wt-navigation-rail.vue +108 -0
  18. package/src/components/wt-page/__tests__/WtPage.spec.js +60 -1
  19. package/src/components/wt-page/wt-page.vue +40 -3
  20. package/src/css/components-variables.css +1 -0
  21. package/src/locale/es/es.js +1 -1
  22. package/src/locale/kz/kz.js +1 -1
  23. package/src/locale/pl/pl.js +1 -1
  24. package/src/locale/ro/ro.js +1 -1
  25. package/src/locale/uz/uz.js +1 -1
  26. package/src/locale/vi/vi.js +1 -1
  27. package/types/.tsbuildinfo +1 -1
  28. package/types/components/index.d.ts +3 -1
  29. package/types/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.d.ts +1 -0
  30. package/types/components/wt-navigation-rail/_internals/navigation-rail-button.vue.d.ts +12 -0
  31. package/types/components/wt-navigation-rail/types/WtNavigationRail.d.ts +17 -0
  32. package/types/components/wt-navigation-rail/wt-navigation-rail.vue.d.ts +25 -0
  33. package/types/components/wt-page/wt-page.vue.d.ts +20 -1
@@ -1,4 +1,4 @@
1
- import "./components-Bzxyigqb.js";
1
+ import "./components-BGwoOBLk.js";
2
2
  import { t as e } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
3
  import { E as t, h as n } from "./enums-CKer6GJR.js";
4
4
  import { t as r } from "./wt-button-DSs8WAg-.js";
@@ -1,4 +1,4 @@
1
- import { C as e, i as t } from "./components-Bzxyigqb.js";
1
+ import { C as e, i as t } from "./components-BGwoOBLk.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-Bzxyigqb.js";
1
+ import { n as e } from "./components-BGwoOBLk.js";
2
2
  import "./convertDuration-CIPonKKW.js";
3
- import { i as t, r as n } from "./clients-Bk3DA6vL.js";
3
+ import { i as t, r as n } from "./clients-7bjVyMfb.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";
@@ -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-Bzxyigqb.js";
5
- import { a as p, n as m, t as h } from "./clients-Bk3DA6vL.js";
4
+ import { a as l, o as u, r as d, s as f } from "./components-BGwoOBLk.js";
5
+ import { a as p, n as m, t as h } from "./clients-7bjVyMfb.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";
@@ -9346,7 +9346,7 @@ var J = /* @__PURE__ */ function(e) {
9346
9346
  [T.Agent]: { name: "Espacio de trabajo del agente" },
9347
9347
  [T.Audit]: {
9348
9348
  name: "Auditoría",
9349
- sections: { [E.Scorecards]: "Tarjetas de puntuación" }
9349
+ sections: { [E.Scorecards]: "Hojas de puntuación" }
9350
9350
  },
9351
9351
  [T.Crm]: {
9352
9352
  name: "CRM",
@@ -10058,7 +10058,7 @@ var J = /* @__PURE__ */ function(e) {
10058
10058
  [T.Agent]: { name: "Оператор жұмыс орны" },
10059
10059
  [T.Audit]: {
10060
10060
  name: "Аудит",
10061
- sections: { [E.Scorecards]: "Бағалау карточкалары" }
10061
+ sections: { [E.Scorecards]: "Сауалнамалар" }
10062
10062
  },
10063
10063
  [T.Crm]: {
10064
10064
  name: "CRM",
@@ -10770,7 +10770,7 @@ var J = /* @__PURE__ */ function(e) {
10770
10770
  [T.Agent]: { name: "Panel Agenta" },
10771
10771
  [T.Audit]: {
10772
10772
  name: "Audyt",
10773
- sections: { [E.Scorecards]: "Karty wyników" }
10773
+ sections: { [E.Scorecards]: "Arkusze ocen" }
10774
10774
  },
10775
10775
  [T.Crm]: {
10776
10776
  name: "CRM",
@@ -11482,7 +11482,7 @@ var J = /* @__PURE__ */ function(e) {
11482
11482
  [T.Agent]: { name: "Spațiu Agent" },
11483
11483
  [T.Audit]: {
11484
11484
  name: "Audit",
11485
- sections: { [E.Scorecards]: "Fișe evaluare" }
11485
+ sections: { [E.Scorecards]: "Fișe de evaluare" }
11486
11486
  },
11487
11487
  [T.Crm]: {
11488
11488
  name: "CRM",
@@ -13631,7 +13631,7 @@ te({
13631
13631
  [T.Agent]: { name: "Agent ish joyi" },
13632
13632
  [T.Audit]: {
13633
13633
  name: "Audit",
13634
- sections: { [E.Scorecards]: "Baholash jadvallari" }
13634
+ sections: { [E.Scorecards]: "Baholash varaqalari" }
13635
13635
  },
13636
13636
  [T.Crm]: {
13637
13637
  name: "CRM",
@@ -14344,7 +14344,7 @@ te({
14344
14344
  [T.Agent]: { name: "Không gian làm việc của tổng đài viên" },
14345
14345
  [T.Audit]: {
14346
14346
  name: "Kiểm toán",
14347
- sections: { [E.Scorecards]: "Bảng điểm" }
14347
+ sections: { [E.Scorecards]: "Các phiếu chấm điểm" }
14348
14348
  },
14349
14349
  [T.Crm]: {
14350
14350
  name: "CRM",
@@ -1,4 +1,4 @@
1
- import { w as e } from "./components-Bzxyigqb.js";
1
+ import { w as e } from "./components-BGwoOBLk.js";
2
2
  import "./convertDuration-CIPonKKW.js";
3
3
  import { t } from "./_plugin-vue_export-helper-B3ysoDQm.js";
4
4
  import { h as n } from "./enums-CKer6GJR.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "26.8.139",
3
+ "version": "26.8.141",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "npm run docs:dev",
@@ -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-page__body">
7
- <!-- @slot Layouts of the page -->
8
- <slot />
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";
@@ -372,7 +372,7 @@ export default {
372
372
  [WtApplication.Audit]: {
373
373
  name: 'Auditoría',
374
374
  sections: {
375
- [AuditorSections.Scorecards]: 'Tarjetas de puntuación',
375
+ [AuditorSections.Scorecards]: 'Hojas de puntuación',
376
376
  },
377
377
  },
378
378
  [WtApplication.Crm]: {
@@ -375,7 +375,7 @@ export default {
375
375
  [WtApplication.Audit]: {
376
376
  name: 'Аудит',
377
377
  sections: {
378
- [AuditorSections.Scorecards]: 'Бағалау карточкалары',
378
+ [AuditorSections.Scorecards]: 'Сауалнамалар',
379
379
  },
380
380
  },
381
381
  [WtApplication.Crm]: {
@@ -375,7 +375,7 @@ export default {
375
375
  [WtApplication.Audit]: {
376
376
  name: 'Audyt',
377
377
  sections: {
378
- [AuditorSections.Scorecards]: 'Karty wyników',
378
+ [AuditorSections.Scorecards]: 'Arkusze ocen',
379
379
  },
380
380
  },
381
381
  [WtApplication.Crm]: {
@@ -379,7 +379,7 @@ export default {
379
379
  [WtApplication.Audit]: {
380
380
  name: 'Audit',
381
381
  sections: {
382
- [AuditorSections.Scorecards]: 'Fișe evaluare',
382
+ [AuditorSections.Scorecards]: 'Fișe de evaluare',
383
383
  },
384
384
  },
385
385
  [WtApplication.Crm]: {
@@ -379,7 +379,7 @@ export default {
379
379
  [WtApplication.Audit]: {
380
380
  name: 'Audit',
381
381
  sections: {
382
- [AuditorSections.Scorecards]: 'Baholash jadvallari',
382
+ [AuditorSections.Scorecards]: 'Baholash varaqalari',
383
383
  },
384
384
  },
385
385
  [WtApplication.Crm]: {
@@ -379,7 +379,7 @@ export default {
379
379
  [WtApplication.Audit]: {
380
380
  name: 'Kiểm toán',
381
381
  sections: {
382
- [AuditorSections.Scorecards]: 'Bảng điểm',
382
+ [AuditorSections.Scorecards]: 'Các phiếu chấm điểm',
383
383
  },
384
384
  },
385
385
  [WtApplication.Crm]: {