@volverjs/ui-vue 0.0.6-beta.2 → 0.0.6-beta.4

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 (39) hide show
  1. package/README.md +2 -1
  2. package/dist/components/VvAvatarGroup/VvAvatarGroup.es.js +4 -2
  3. package/dist/components/VvAvatarGroup/VvAvatarGroup.umd.js +1 -1
  4. package/dist/components/VvNav/VvNav.es.js +450 -0
  5. package/dist/components/VvNav/VvNav.umd.js +1 -0
  6. package/dist/components/VvNav/VvNav.vue.d.ts +47 -0
  7. package/dist/components/VvNav/index.d.ts +32 -0
  8. package/dist/components/VvNavItemTitle/VvNavItemTitle.es.js +19 -0
  9. package/dist/components/VvNavItemTitle/VvNavItemTitle.umd.js +1 -0
  10. package/dist/components/VvNavItemTitle/VvNavItemTitle.vue.d.ts +6 -0
  11. package/dist/components/VvNavSeparator/VvNavSeparator.d.ts +2 -0
  12. package/dist/components/VvTab/VvTab.es.js +505 -0
  13. package/dist/components/VvTab/VvTab.umd.js +1 -0
  14. package/dist/components/VvTab/VvTab.vue.d.ts +52 -0
  15. package/dist/components/VvTab/index.d.ts +23 -0
  16. package/dist/icons.es.js +3 -3
  17. package/dist/icons.umd.js +1 -1
  18. package/dist/stories/Nav/Nav.settings.d.ts +33 -0
  19. package/dist/stories/Nav/Nav.test.d.ts +2 -0
  20. package/dist/stories/Tab/Tab.settings.d.ts +37 -0
  21. package/dist/stories/Tab/Tab.test.d.ts +2 -0
  22. package/package.json +25 -1
  23. package/src/assets/icons/detailed.json +1 -1
  24. package/src/assets/icons/normal.json +1 -1
  25. package/src/assets/icons/simple.json +1 -1
  26. package/src/components/VvAvatarGroup/VvAvatarGroup.vue +2 -2
  27. package/src/components/VvNav/VvNav.vue +66 -0
  28. package/src/components/VvNav/index.ts +21 -0
  29. package/src/components/VvNavItemTitle/VvNavItemTitle.vue +11 -0
  30. package/src/components/VvNavSeparator/VvNavSeparator.ts +8 -0
  31. package/src/components/VvTab/VvTab.vue +53 -0
  32. package/src/components/VvTab/index.ts +13 -0
  33. package/src/stories/Nav/Nav.settings.ts +34 -0
  34. package/src/stories/Nav/Nav.stories.mdx +28 -0
  35. package/src/stories/Nav/Nav.test.ts +32 -0
  36. package/src/stories/Nav/NavModifiers.stories.mdx +48 -0
  37. package/src/stories/Tab/Tab.settings.ts +41 -0
  38. package/src/stories/Tab/Tab.stories.mdx +65 -0
  39. package/src/stories/Tab/Tab.test.ts +37 -0
@@ -17,7 +17,7 @@
17
17
  })
18
18
 
19
19
  const avatarItems = computed(() => {
20
- return items.value.slice(0, toShow.value).map((item, index) => {
20
+ return items.value.slice(0, toShow.value).map((item) => {
21
21
  let modifiers: string[] = []
22
22
  let itemModifiers: string[] = []
23
23
 
@@ -35,7 +35,7 @@
35
35
 
36
36
  return {
37
37
  ...item,
38
- key: item.key || `${index}_${new Date().getTime()}`,
38
+ key: item.key || useUniqueId().value,
39
39
  modifiers: [...modifiers, ...itemModifiers],
40
40
  }
41
41
  })
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ import { VvNavProps, VvNavEvents, type NavItem } from '@/components/VvNav'
3
+ import VvAction from '@/components/VvAction/VvAction.vue'
4
+
5
+ const props = defineProps(VvNavProps)
6
+ const emit = defineEmits(VvNavEvents)
7
+ const { modifiers, items } = toRefs(props)
8
+ const activeItem: Ref<string | null> = ref(null)
9
+
10
+ // bem css classes
11
+ const bemCssClasses = useModifiers('vv-nav', modifiers)
12
+
13
+ const localItems = computed(() => {
14
+ return items.value.map((item, index) => {
15
+ return {
16
+ ...item,
17
+ id: item.id || `nav-item_${index}`,
18
+ }
19
+ })
20
+ })
21
+
22
+ /**
23
+ * Triggers when the item is clicked.
24
+ * @private
25
+ * @event click
26
+ * @param [NavItem, string] item - the clicked item
27
+ */
28
+ function onClick(item: NavItem) {
29
+ if (!item.disabled) {
30
+ emit('click', item)
31
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
32
+ activeItem.value = item.id!
33
+ }
34
+ }
35
+ </script>
36
+
37
+ <template>
38
+ <nav :class="bemCssClasses">
39
+ <ul class="vv-nav__menu" role="menu" aria-busy="true">
40
+ <li
41
+ v-for="navItem in localItems"
42
+ :key="navItem.id"
43
+ class="vv-nav__item"
44
+ role="presentation"
45
+ >
46
+ <VvAction
47
+ v-bind="{
48
+ disabled: navItem.disabled,
49
+ to: navItem.to,
50
+ href: navItem.href,
51
+ tabindex: 0,
52
+ }"
53
+ :class="{
54
+ current: activeItem == navItem.id,
55
+ disabled: navItem.disabled,
56
+ }"
57
+ class="vv-nav__item-label"
58
+ v-on="navItem.on || {}"
59
+ @click="onClick(navItem)"
60
+ >
61
+ {{ navItem.title }}
62
+ </VvAction>
63
+ </li>
64
+ </ul>
65
+ </nav>
66
+ </template>
@@ -0,0 +1,21 @@
1
+ import { ModifiersProps } from '@/props'
2
+
3
+ export type NavItem = {
4
+ id?: string
5
+ title: string
6
+ to?: string | { [key: string]: unknown }
7
+ href?: string
8
+ disabled?: boolean
9
+ on: () => void
10
+ }
11
+
12
+ export const VvNavProps = {
13
+ ...ModifiersProps,
14
+ items: {
15
+ type: Array<NavItem>,
16
+ required: true,
17
+ default: () => [],
18
+ },
19
+ }
20
+
21
+ export const VvNavEvents = ['click']
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ defineProps({
3
+ title: String,
4
+ })
5
+ </script>
6
+
7
+ <template>
8
+ <span class="vv-nav__heading-label" aria-hidden="true">
9
+ {{ title }}
10
+ </span>
11
+ </template>
@@ -0,0 +1,8 @@
1
+ export default defineComponent({
2
+ render() {
3
+ return h('li', {
4
+ class: 'vv-nav__divider',
5
+ role: 'separator',
6
+ })
7
+ },
8
+ })
@@ -0,0 +1,53 @@
1
+ <script setup lang="ts">
2
+ import { VvTabProps, VvTabEvents } from '@/components/VvTab'
3
+ import type { NavItem } from '@/components/VvNav'
4
+ import VvNav from '@/components/VvNav/VvNav.vue'
5
+
6
+ const props = defineProps(VvTabProps)
7
+ const emit = defineEmits(VvTabEvents)
8
+ const { modifiers, items } = toRefs(props)
9
+ const activeTab: Ref<string | null> = ref(null)
10
+
11
+ // bem css classes
12
+ const bemCssClasses = useModifiers('vv-tab', modifiers)
13
+
14
+ const localItems = computed(() => {
15
+ return items.value.map((item, index) => {
16
+ return {
17
+ ...item,
18
+ id: item.id || `tab-item_${index}`,
19
+ }
20
+ })
21
+ })
22
+
23
+ /**
24
+ * Triggers when the item is clicked.
25
+ * @private
26
+ * @event click
27
+ * @param [NavItem, string] item - the clicked item
28
+ */
29
+ function onClick(item: NavItem) {
30
+ if (!item.disabled) {
31
+ emit('click', item)
32
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
33
+ activeTab.value = item.id!
34
+ }
35
+ }
36
+ </script>
37
+
38
+ <template>
39
+ <div :class="bemCssClasses">
40
+ <VvNav :items="localItems" modifiers="tabs full" @click="onClick" />
41
+ <!-- #region tab content -->
42
+ <article
43
+ v-for="item in localItems"
44
+ :id="item.id"
45
+ :key="item.id"
46
+ :class="{ target: activeTab === item.id }"
47
+ class="vv-tab__panel"
48
+ >
49
+ <slot :name="`${item.id}`" />
50
+ </article>
51
+ <!-- #endregion tab content -->
52
+ </div>
53
+ </template>
@@ -0,0 +1,13 @@
1
+ import { ModifiersProps } from '@/props'
2
+ import type { NavItem } from '../VvNav'
3
+
4
+ export const VvTabProps = {
5
+ ...ModifiersProps,
6
+ items: {
7
+ type: Array<NavItem>,
8
+ required: true,
9
+ default: () => [],
10
+ },
11
+ }
12
+
13
+ export const VvTabEvents = ['click']
@@ -0,0 +1,34 @@
1
+ import { VvNavProps } from '@/components/VvNav'
2
+ import { ModifiersArgTypes } from '@/stories/argTypes'
3
+
4
+ export const defaultArgs = {
5
+ ...propsToObject(VvNavProps),
6
+ items: [
7
+ {
8
+ title: 'Item 1',
9
+ href: 'javascript:void(0)',
10
+ },
11
+ {
12
+ title: 'Item 2',
13
+ to: 'about',
14
+ },
15
+ {
16
+ title: 'Item 3',
17
+ to: 'contacts',
18
+ on: {
19
+ click: () => {
20
+ // eslint-disable-next-line no-console
21
+ console.log('clicked Item 3')
22
+ },
23
+ },
24
+ },
25
+ ],
26
+ modifiers: 'sidebar',
27
+ }
28
+
29
+ export const argTypes = {
30
+ modifiers: {
31
+ ...ModifiersArgTypes.modifiers,
32
+ options: ['sidebar', 'aside', 'tabs', 'full'],
33
+ },
34
+ }
@@ -0,0 +1,28 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvNav from '@/components/VvNav/VvNav.vue'
3
+ import { defaultArgs, argTypes } from './Nav.settings'
4
+ import { defaultTest } from './Nav.test'
5
+
6
+ <Meta
7
+ title="Components/Nav"
8
+ component={VvNav}
9
+ args={defaultArgs}
10
+ argTypes={argTypes}
11
+ />
12
+
13
+ export const Template = (args) => ({
14
+ components: { VvNav },
15
+ setup() {
16
+ return { args }
17
+ },
18
+ template: /* html */ `
19
+ <div class="m-md w-1/5">
20
+ <vv-nav v-bind="args" data-testId="element" />
21
+ </div>`,
22
+ })
23
+
24
+ <Canvas>
25
+ <Story name="Default" play={defaultTest}>
26
+ {Template.bind()}
27
+ </Story>
28
+ </Canvas>
@@ -0,0 +1,32 @@
1
+ import type { PlayAttributes } from '@/test/types'
2
+ import { expect } from '@/test/expect'
3
+ import { within, userEvent } from '@storybook/testing-library'
4
+
5
+ export async function defaultTest({ canvasElement, args }: PlayAttributes) {
6
+ const element = (await within(canvasElement).findByTestId(
7
+ 'element',
8
+ )) as HTMLElement
9
+
10
+ if (!args.items || !args.items?.length) {
11
+ throw new Error('No items passed')
12
+ }
13
+
14
+ // check children number the same of items prop
15
+ expect(element.children?.[0].children.length).toEqual(args.items?.length)
16
+
17
+ // take firse and second elements
18
+ const firstEl = await element.getElementsByTagName('a')?.[0]
19
+ const secondEl = await element.getElementsByTagName('router-link')?.[1]
20
+ // check they have not current class
21
+ const secondElHasCurrentClass = secondEl.classList.contains('current')
22
+ const firstElHasCurrentClass = firstEl.classList.contains('current')
23
+
24
+ await expect(firstElHasCurrentClass).toBe(false)
25
+ await expect(secondElHasCurrentClass).toBe(false)
26
+ // click first item and check "current" css class
27
+ await userEvent.click(firstEl)
28
+ await expect(firstEl).toHaveClass('current')
29
+
30
+ // check accessibility
31
+ await expect(element).toHaveNoViolations()
32
+ }
@@ -0,0 +1,48 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvNav from '@/components/VvNav/VvNav.vue'
3
+ import { defaultArgs, argTypes } from './Nav.settings'
4
+ import { defaultTest } from './Nav.test'
5
+ import { Template } from './Nav.stories.mdx'
6
+
7
+ <Meta
8
+ title="Components/Nav/Modifiers"
9
+ component={VvNav}
10
+ args={defaultArgs}
11
+ argTypes={argTypes}
12
+ />
13
+
14
+ <Canvas>
15
+ <Story name="Sidebar" play={defaultTest} args={{ modifiers: 'sidebar' }}>
16
+ {Template.bind({})}
17
+ </Story>
18
+ </Canvas>
19
+
20
+ <Canvas>
21
+ <Story name="Aside" play={defaultTest} args={{ modifiers: 'aside' }}>
22
+ {Template.bind({})}
23
+ </Story>
24
+ </Canvas>
25
+
26
+ <Canvas>
27
+ <Story
28
+ name="Tabs"
29
+ play={defaultTest}
30
+ args={{
31
+ modifiers: 'tabs',
32
+ }}
33
+ >
34
+ {Template.bind({})}
35
+ </Story>
36
+ </Canvas>
37
+
38
+ <Canvas>
39
+ <Story
40
+ name="TabsFull"
41
+ play={defaultTest}
42
+ args={{
43
+ modifiers: 'tabs full',
44
+ }}
45
+ >
46
+ {Template.bind({})}
47
+ </Story>
48
+ </Canvas>
@@ -0,0 +1,41 @@
1
+ import { VvTabProps } from '@/components/VvTab'
2
+
3
+ export const defaultArgs = {
4
+ ...propsToObject(VvTabProps),
5
+ items: [
6
+ {
7
+ title: 'Item 1',
8
+ href: 'javascript:void(0)',
9
+ },
10
+ {
11
+ title: 'Item 2',
12
+ to: 'about',
13
+ },
14
+ {
15
+ id: 'tab-3',
16
+ title: 'Item 3',
17
+ to: 'contacts',
18
+ on: {
19
+ click: () => {
20
+ // eslint-disable-next-line no-console
21
+ console.log('clicked Item 3')
22
+ },
23
+ },
24
+ },
25
+ ],
26
+ }
27
+
28
+ export const defaultArgTypes = {
29
+ tabId: {
30
+ description: 'Slot by tab-id',
31
+ control: {
32
+ type: 'text',
33
+ },
34
+ table: {
35
+ category: 'Slots',
36
+ type: {
37
+ summary: 'html',
38
+ },
39
+ },
40
+ },
41
+ }
@@ -0,0 +1,65 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvTab from '@/components/VvTab/VvTab.vue'
3
+ import { defaultArgs, defaultArgTypes } from './Tab.settings'
4
+ import { defaultTest } from './Tab.test'
5
+
6
+ <Meta
7
+ title="Components/Tab"
8
+ component={VvTab}
9
+ args={defaultArgs}
10
+ argTypes={defaultArgTypes}
11
+ />
12
+
13
+ export const Template = (args) => ({
14
+ components: { VvTab },
15
+ setup() {
16
+ return { args }
17
+ },
18
+ template: /* html */ `
19
+ <div class="m-md w-1/2">
20
+ <vv-tab v-bind="args" data-testId="element">
21
+ <template #tab-item_0>
22
+ <span class="text-20 font-semibold">Tab 1</span>
23
+ <p>
24
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
25
+ vitae elit libero, a pharetra augue. Nullam id dolor id nibh
26
+ ultricies vehicula ut id elit. Morbi leo risus, porta ac
27
+ consectetur ac, vestibulum at eros. Praesent commodo cursus
28
+ magna, vel scelerisque nisl consectetur et. Donec sed odio dui.
29
+ Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae
30
+ elit libero, a pharetra augue.
31
+ </p>
32
+ </template>
33
+ <template #tab-item_1>
34
+ <span class="text-20 font-semibold">Tab 2</span>
35
+ <p>
36
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
37
+ vitae elit libero, a pharetra augue. Nullam id dolor id nibh
38
+ ultricies vehicula ut id elit. Morbi leo risus, porta ac
39
+ consectetur ac, vestibulum at eros. Praesent commodo cursus
40
+ magna, vel scelerisque nisl consectetur et. Donec sed odio dui.
41
+ Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae
42
+ elit libero, a pharetra augue.
43
+ </p>
44
+ </template>
45
+ <template #tab-3>
46
+ <span class="text-20 font-semibold">Tab 3</span>
47
+ <p>
48
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
49
+ vitae elit libero, a pharetra augue. Nullam id dolor id nibh
50
+ ultricies vehicula ut id elit. Morbi leo risus, porta ac
51
+ consectetur ac, vestibulum at eros. Praesent commodo cursus
52
+ magna, vel scelerisque nisl consectetur et. Donec sed odio dui.
53
+ Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae
54
+ elit libero, a pharetra augue.
55
+ </p>
56
+ </template>
57
+ </vv-tab>
58
+ </div>`,
59
+ })
60
+
61
+ <Canvas>
62
+ <Story name="Default" play={defaultTest}>
63
+ {Template.bind()}
64
+ </Story>
65
+ </Canvas>
@@ -0,0 +1,37 @@
1
+ import type { PlayAttributes } from '@/test/types'
2
+ import { expect } from '@/test/expect'
3
+ import { within, userEvent } from '@storybook/testing-library'
4
+
5
+ export async function defaultTest({ canvasElement, args }: PlayAttributes) {
6
+ const element = (await within(canvasElement).findByTestId(
7
+ 'element',
8
+ )) as HTMLElement
9
+
10
+ if (!args.items || !args.items?.length) {
11
+ throw new Error('No items passed')
12
+ }
13
+
14
+ // check children number the same of items prop
15
+ const childrenEls = element.getElementsByClassName('vv-nav')[0]
16
+ expect(childrenEls?.children[0].children.length).toEqual(args.items?.length)
17
+
18
+ // take firse and second elements
19
+ const firstEl = await element.getElementsByTagName('a')?.[0]
20
+ const secondEl = await element.getElementsByTagName('router-link')?.[1]
21
+ // check they have not current class
22
+ const secondElHasCurrentClass = secondEl.classList.contains('current')
23
+ const firstElHasCurrentClass = firstEl.classList.contains('current')
24
+
25
+ await expect(firstElHasCurrentClass).toBe(false)
26
+ await expect(secondElHasCurrentClass).toBe(false)
27
+ // click first item and check "current" css class
28
+ await userEvent.click(firstEl)
29
+ await expect(firstEl).toHaveClass('current')
30
+
31
+ // check tab content to include "Tab 1"
32
+ const tabPanelEl = element.getElementsByClassName('vv-tab__panel')?.[0]
33
+ await expect(tabPanelEl.innerHTML.includes('Tab 1')).toBe(true)
34
+
35
+ // check accessibility
36
+ await expect(element).toHaveNoViolations()
37
+ }