@volverjs/ui-vue 0.0.6-beta.1 → 0.0.6-beta.3

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 -2
  2. package/dist/components/VvAvatarGroup/VvAvatarGroup.es.js +363 -0
  3. package/dist/components/VvAvatarGroup/VvAvatarGroup.umd.js +1 -0
  4. package/dist/components/VvAvatarGroup/VvAvatarGroup.vue.d.ts +68 -0
  5. package/dist/components/VvAvatarGroup/index.d.ts +38 -0
  6. package/dist/components/VvNav/VvNav.es.js +442 -0
  7. package/dist/components/VvNav/VvNav.umd.js +1 -0
  8. package/dist/components/VvNav/VvNav.vue.d.ts +47 -0
  9. package/dist/components/VvNav/index.d.ts +31 -0
  10. package/dist/components/VvNavItemTitle/VvNavItemTitle.es.js +19 -0
  11. package/dist/components/VvNavItemTitle/VvNavItemTitle.umd.js +1 -0
  12. package/dist/components/VvNavItemTitle/VvNavItemTitle.vue.d.ts +6 -0
  13. package/dist/components/VvNavSeparator/VvNavSeparator.d.ts +2 -0
  14. package/dist/icons.es.js +3 -3
  15. package/dist/icons.umd.js +1 -1
  16. package/dist/stories/AvatarGroup/AvatarGroup.settings.d.ts +56 -0
  17. package/dist/stories/AvatarGroup/AvatarGroup.test.d.ts +2 -0
  18. package/dist/stories/Nav/Nav.settings.d.ts +33 -0
  19. package/dist/stories/Nav/Nav.test.d.ts +2 -0
  20. package/package.json +25 -1
  21. package/src/assets/icons/detailed.json +1 -1
  22. package/src/assets/icons/normal.json +1 -1
  23. package/src/assets/icons/simple.json +1 -1
  24. package/src/components/VvAvatarGroup/VvAvatarGroup.vue +65 -0
  25. package/src/components/VvAvatarGroup/index.ts +28 -0
  26. package/src/components/VvNav/VvNav.vue +56 -0
  27. package/src/components/VvNav/index.ts +20 -0
  28. package/src/components/VvNavItemTitle/VvNavItemTitle.vue +11 -0
  29. package/src/components/VvNavSeparator/VvNavSeparator.ts +8 -0
  30. package/src/stories/Avatar/Avatar.settings.ts +1 -9
  31. package/src/stories/AvatarGroup/AvatarGroup.settings.ts +60 -0
  32. package/src/stories/AvatarGroup/AvatarGroup.stories.mdx +30 -0
  33. package/src/stories/AvatarGroup/AvatarGroup.test.ts +36 -0
  34. package/src/stories/AvatarGroup/AvatarGroupModifiers.stories.mdx +24 -0
  35. package/src/stories/AvatarGroup/AvatarGroupSlotDefault.stories.mdx +45 -0
  36. package/src/stories/Nav/Nav.settings.ts +34 -0
  37. package/src/stories/Nav/Nav.stories.mdx +28 -0
  38. package/src/stories/Nav/Nav.test.ts +32 -0
  39. package/src/stories/Nav/NavModifiers.stories.mdx +48 -0
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import { VvAvatarGroupProps } from '@/components/VvAvatarGroup'
3
+ import VvAvatar from '@/components/VvAvatar/VvAvatar.vue'
4
+
5
+ const props = defineProps(VvAvatarGroupProps)
6
+ const { modifiers, items, toShow, totalItems, avatarModifiers } =
7
+ toRefs(props)
8
+
9
+ // bem css classes
10
+ const bemCssClasses = useModifiers('vv-avatar-group', modifiers)
11
+
12
+ const stringModifiers = computed(() => {
13
+ if (avatarModifiers?.value && Array.isArray(avatarModifiers?.value)) {
14
+ return avatarModifiers.value.join(' ')
15
+ }
16
+ return avatarModifiers?.value || ''
17
+ })
18
+
19
+ const avatarItems = computed(() => {
20
+ return items.value.slice(0, toShow.value).map((item) => {
21
+ let modifiers: string[] = []
22
+ let itemModifiers: string[] = []
23
+
24
+ if (avatarModifiers?.value) {
25
+ modifiers = Array.isArray(avatarModifiers?.value)
26
+ ? avatarModifiers?.value
27
+ : [avatarModifiers?.value]
28
+ }
29
+
30
+ if (item.modifiers) {
31
+ itemModifiers = Array.isArray(item.modifiers)
32
+ ? item.modifiers
33
+ : [item.modifiers]
34
+ }
35
+
36
+ return {
37
+ ...item,
38
+ key: item.key || useUniqueId().value,
39
+ modifiers: [...modifiers, ...itemModifiers],
40
+ }
41
+ })
42
+ })
43
+ </script>
44
+
45
+ <template>
46
+ <span :class="bemCssClasses">
47
+ <slot name="default">
48
+ <VvAvatar
49
+ v-for="avatarItem in avatarItems"
50
+ :key="avatarItem.key"
51
+ v-bind="{
52
+ modifiers: avatarItem.modifiers,
53
+ imgSrc: avatarItem.imgSrc,
54
+ }"
55
+ >
56
+ <span v-if="avatarItem.text">{{ avatarItem.text }}</span>
57
+ </VvAvatar>
58
+ <VvAvatar
59
+ v-if="(totalItems || items.length) > toShow"
60
+ :modifiers="`${stringModifiers} surface bordered`"
61
+ >{{ `+${(totalItems || items.length) - toShow}` }}</VvAvatar
62
+ >
63
+ </slot>
64
+ </span>
65
+ </template>
@@ -0,0 +1,28 @@
1
+ import { ModifiersProps } from '@/props'
2
+
3
+ export type AvatarItem = {
4
+ key?: string
5
+ text?: string
6
+ imgSrc?: string
7
+ modifiers?: string | string[]
8
+ }
9
+
10
+ export const VvAvatarGroupProps = {
11
+ ...ModifiersProps,
12
+ /**
13
+ * avatar items
14
+ */
15
+ items: {
16
+ type: Array<AvatarItem>,
17
+ default: () => [],
18
+ required: true,
19
+ },
20
+ toShow: {
21
+ type: Number,
22
+ default: 3,
23
+ },
24
+ totalItems: {
25
+ type: Number,
26
+ },
27
+ avatarModifiers: [String, Array] as PropType<string | Array<string>>,
28
+ }
@@ -0,0 +1,56 @@
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 } = toRefs(props)
8
+ const activeItem: Ref<string | null> = ref(null)
9
+
10
+ // bem css classes
11
+ const bemCssClasses = useModifiers('vv-nav', modifiers)
12
+
13
+ /**
14
+ * Triggers when the item is clicked.
15
+ * @private
16
+ * @event click
17
+ * @param [NavItem, number] item, id - the clicked item
18
+ */
19
+ function onClick(item: NavItem, id: string) {
20
+ if (!item.disabled) {
21
+ emit('click', item)
22
+ activeItem.value = id
23
+ }
24
+ }
25
+ </script>
26
+
27
+ <template>
28
+ <nav :class="bemCssClasses">
29
+ <ul class="vv-nav__menu" role="menu" aria-busy="true">
30
+ <li
31
+ v-for="(navItem, index) in items"
32
+ :key="`nav-item_${index}`"
33
+ class="vv-nav__item"
34
+ role="presentation"
35
+ >
36
+ <VvAction
37
+ v-bind="{
38
+ disabled: navItem.disabled,
39
+ to: navItem.to,
40
+ href: navItem.href,
41
+ tabindex: 0,
42
+ }"
43
+ :class="{
44
+ current: activeItem == `nav-item_${index}`,
45
+ disabled: navItem.disabled,
46
+ }"
47
+ class="vv-nav__item-label"
48
+ v-on="navItem.on || {}"
49
+ @click="onClick(navItem, `nav-item_${index}`)"
50
+ >
51
+ {{ navItem.title }}
52
+ </VvAction>
53
+ </li>
54
+ </ul>
55
+ </nav>
56
+ </template>
@@ -0,0 +1,20 @@
1
+ import { ModifiersProps } from '@/props'
2
+
3
+ export type NavItem = {
4
+ title: string
5
+ to?: string | { [key: string]: unknown }
6
+ href?: string
7
+ disabled?: boolean
8
+ on: () => void
9
+ }
10
+
11
+ export const VvNavProps = {
12
+ ...ModifiersProps,
13
+ items: {
14
+ type: Array<NavItem>,
15
+ required: true,
16
+ default: () => [],
17
+ },
18
+ }
19
+
20
+ 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
+ })
@@ -22,15 +22,7 @@ export const argTypes = {
22
22
  },
23
23
  modifiers: {
24
24
  ...ModifiersArgTypes.modifiers,
25
- options: [
26
- 'rounded',
27
- 'square',
28
- 'bordered',
29
- 'ring',
30
- 'circle',
31
- 'lg',
32
- 'md',
33
- ],
25
+ options: ['rounded', 'square', 'bordered', 'ring', 'lg', 'md'],
34
26
  },
35
27
  imgSrc: {
36
28
  type: {
@@ -0,0 +1,60 @@
1
+ import { VvAvatarGroupProps } from '@/components/VvAvatarGroup'
2
+ import { DefaultSlotArgTypes, ModifiersArgTypes } from '@/stories/argTypes'
3
+
4
+ export const defaultArgs = {
5
+ ...propsToObject(VvAvatarGroupProps),
6
+ items: [
7
+ {
8
+ text: 'MR',
9
+ imgSrc: 'https://i.pravatar.cc/300',
10
+ modifiers: 'rounded',
11
+ },
12
+ {
13
+ imgSrc: 'https://i.pravatar.cc/300',
14
+ modifiers: 'rounded',
15
+ },
16
+ {
17
+ imgSrc: 'https://i.pravatar.cc/300',
18
+ modifiers: 'rounded',
19
+ },
20
+ {
21
+ imgSrc: 'https://i.pravatar.cc/300',
22
+ modifiers: 'rounded',
23
+ },
24
+ ],
25
+ avatarModifiers: 'rounded',
26
+ }
27
+
28
+ export const argTypes = {
29
+ ...DefaultSlotArgTypes,
30
+ default: {
31
+ description: 'Default slot',
32
+ control: {
33
+ type: 'text',
34
+ },
35
+ table: {
36
+ category: 'Slots',
37
+ type: {
38
+ summary: 'html',
39
+ },
40
+ },
41
+ },
42
+ modifiers: {
43
+ ...ModifiersArgTypes.modifiers,
44
+ options: ['tight', 'relaxed'],
45
+ },
46
+ avatarModifiers: {
47
+ ...ModifiersArgTypes.modifiers,
48
+ options: ['rounded', 'square', 'bordered', 'ring', 'lg', 'md'],
49
+ },
50
+ toShow: {
51
+ control: {
52
+ type: 'number',
53
+ },
54
+ },
55
+ totalItems: {
56
+ control: {
57
+ type: 'number',
58
+ },
59
+ },
60
+ }
@@ -0,0 +1,30 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvAvatarGroup from '@/components/VvAvatarGroup/VvAvatarGroup.vue'
3
+ import { defaultArgs, argTypes } from './AvatarGroup.settings'
4
+ import { defaultTest } from './AvatarGroup.test'
5
+
6
+ <Meta
7
+ title="Components/AvatarGroup"
8
+ component={VvAvatarGroup}
9
+ args={defaultArgs}
10
+ argTypes={argTypes}
11
+ />
12
+
13
+ export const Template = (args) => ({
14
+ components: { VvAvatarGroup },
15
+ setup() {
16
+ return { args }
17
+ },
18
+ template: /* html */ `
19
+ <div class="m-md">
20
+ <vv-avatar-group v-bind="args" data-testId="element">
21
+ <template v-if="args.default">{{ args.default }}</template>
22
+ </vv-avatar-group>
23
+ </div>`,
24
+ })
25
+
26
+ <Canvas>
27
+ <Story name="Default" play={defaultTest}>
28
+ {Template.bind()}
29
+ </Story>
30
+ </Canvas>
@@ -0,0 +1,36 @@
1
+ import type { PlayAttributes } from '@/test/types'
2
+ import { expect } from '@/test/expect'
3
+ import { within } 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
+ // slot default
11
+ if (!args.default && !args.items && !args.items?.length) {
12
+ throw new Error('Default slot or items is required!')
13
+ }
14
+
15
+ const modifiers =
16
+ !args.modifiers || Array.isArray(args.modifiers)
17
+ ? args.modifiers
18
+ : [args.modifiers]
19
+
20
+ // modifiers
21
+ if (modifiers) {
22
+ for (const modifier of modifiers) {
23
+ expect(element).toHaveClass(`vv-avatar-group--${modifier}`)
24
+ }
25
+ }
26
+
27
+ // check children numbers
28
+ if (args.items && args.items.length && !args.default) {
29
+ if ((args.totalItems || args.items.length) > args.toShow) {
30
+ expect(element.children.length).toEqual(args.toShow + 1)
31
+ }
32
+ }
33
+
34
+ // check accessibility
35
+ await expect(element).toHaveNoViolations()
36
+ }
@@ -0,0 +1,24 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvAvatarGroup from '@/components/VvAvatarGroup/VvAvatarGroup.vue'
3
+ import { defaultArgs, argTypes } from './AvatarGroup.settings'
4
+ import { defaultTest } from './AvatarGroup.test'
5
+ import { Template } from './AvatarGroup.stories.mdx'
6
+
7
+ <Meta
8
+ title="Components/AvatarGroup/Modifiers"
9
+ component={VvAvatarGroup}
10
+ args={defaultArgs}
11
+ argTypes={argTypes}
12
+ />
13
+
14
+ <Canvas>
15
+ <Story name="Tight" play={defaultTest} args={{ modifiers: 'tight' }}>
16
+ {Template.bind({})}
17
+ </Story>
18
+ </Canvas>
19
+
20
+ <Canvas>
21
+ <Story name="Relaxed" play={defaultTest} args={{ modifiers: 'relaxed' }}>
22
+ {Template.bind({})}
23
+ </Story>
24
+ </Canvas>
@@ -0,0 +1,45 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs'
2
+ import VvAvatarGroup from '@/components/VvAvatarGroup/VvAvatarGroup.vue'
3
+ import VvAvatar from '@/components/VvAvatar/VvAvatar.vue'
4
+ import { defaultArgs, argTypes } from './AvatarGroup.settings'
5
+ import { defaultTest } from './AvatarGroup.test'
6
+ import { Template } from './AvatarGroup.stories.mdx'
7
+
8
+ <Meta
9
+ title="Components/AvatarGroup/Slots"
10
+ component={VvAvatarGroup}
11
+ args={defaultArgs}
12
+ argTypes={argTypes}
13
+ />
14
+
15
+ export const TemplateSlot = (args) => ({
16
+ components: { VvAvatarGroup, VvAvatar },
17
+ setup() {
18
+ return { args }
19
+ },
20
+ template: /* html */ `
21
+ <div class="m-md">
22
+ <vv-avatar-group v-bind="args" data-testId="element" modifiers="relaxed">
23
+ <vv-avatar modifiers="rounded">MR</vv-avatar>
24
+ <vv-avatar modifiers="rounded accent">PB</vv-avatar>
25
+ <vv-avatar imgSrc="https://i.pravatar.cc/300" modifiers="rounded" />
26
+ <vv-avatar modifiers="rounded surface">+3</vv-avatar>
27
+ </vv-avatar-group>
28
+ </div>`,
29
+ })
30
+
31
+ <Canvas>
32
+ <Story
33
+ name="Default"
34
+ play={defaultTest}
35
+ argTypes={{
36
+ default: {
37
+ control: {
38
+ disable: true,
39
+ },
40
+ },
41
+ }}
42
+ >
43
+ {TemplateSlot.bind({})}
44
+ </Story>
45
+ </Canvas>
@@ -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>