Merge branch 'link' into 'main'
Link See merge request cellule-financiere-pmo/design-system/visua-vue!3
This commit is contained in:
commit
475240dcf1
24
package-lock.json
generated
24
package-lock.json
generated
|
@ -13,7 +13,8 @@
|
|||
"jsdom": "^26.1.0",
|
||||
"primevue": "^4.3.6",
|
||||
"vite-plugin-inspect": "^11.3.0",
|
||||
"vue": "^3.5.17"
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
|
@ -2424,6 +2425,12 @@
|
|||
"he": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/devtools-core": {
|
||||
"version": "7.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.7.tgz",
|
||||
|
@ -6305,6 +6312,21 @@
|
|||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
|
||||
"integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.6.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-tsc": {
|
||||
"version": "2.2.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz",
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"jsdom": "^26.1.0",
|
||||
"primevue": "^4.3.6",
|
||||
"vite-plugin-inspect": "^11.3.0",
|
||||
"vue": "^3.5.17"
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
// import VButtonView from '../template/VButtonView.vue'
|
||||
import VButtonGroupView from '../template/VButtonGroupView.vue';
|
||||
// import VButtonGroupView from '../template/VButtonGroupView.vue';
|
||||
import VLinkView from '../template/VLinkView.vue';
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<!-- <VButtonView/> -->
|
||||
<VButtonGroupView/>
|
||||
<!-- <VButtonGroupView/> -->
|
||||
<VLinkView/>
|
||||
</template>
|
||||
|
|
|
@ -64,4 +64,8 @@
|
|||
--p-button-primary-focus-ring-color: var(--focus-color);
|
||||
--p-button-secondary-focus-ring-color: var(--focus-color);
|
||||
--p-button-danger-focus-ring-color: var(--focus-color);
|
||||
/* link */
|
||||
--p-button-link-color: var(--text-action-high-blue-france);
|
||||
--p-button-link-hover-color: var(--text-action-high-blue-france);
|
||||
--p-button-link-active-color: var(--text-action-high-blue-france);
|
||||
}
|
||||
|
|
46
src/components/button/IVLink.type.ts
Normal file
46
src/components/button/IVLink.type.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import type { RouteLocationRaw } from "vue-router";
|
||||
|
||||
/**
|
||||
* Interface representing a navigational link component.
|
||||
* This can be used for both internal routing (via `to`) and external links (via `href`).
|
||||
*/
|
||||
export default interface IVLink {
|
||||
/**
|
||||
* The text label displayed for the link.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Internal route destination using Vue Router's RouteLocationRaw.
|
||||
* Optional – used for client-side navigation.
|
||||
*/
|
||||
to?: RouteLocationRaw;
|
||||
|
||||
/**
|
||||
* External URL for the link.
|
||||
* Optional – used for standard anchor navigation.
|
||||
*/
|
||||
href?: string;
|
||||
|
||||
/**
|
||||
* Optional icon name or path to be displayed alongside the label.
|
||||
*/
|
||||
icon?: string;
|
||||
|
||||
/**
|
||||
* Specifies where to open the linked document (e.g., "_blank", "_self").
|
||||
* Optional – applies to external links.
|
||||
*/
|
||||
target?: string;
|
||||
|
||||
/**
|
||||
* If true, the link is disabled and not clickable.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* If true, the icon is displayed on the right side of the label.
|
||||
* Defaults to false (icon on the left).
|
||||
*/
|
||||
iconRight?: boolean;
|
||||
}
|
60
src/components/button/VLink.vue
Normal file
60
src/components/button/VLink.vue
Normal file
|
@ -0,0 +1,60 @@
|
|||
<script setup lang="ts">
|
||||
import Button from 'primevue/button';
|
||||
import type IVLink from '@/components/button/IVLink.type';
|
||||
import { computed } from 'vue';
|
||||
import styles from '@visua/typography.module.css';
|
||||
|
||||
const props = defineProps<IVLink>();
|
||||
|
||||
// Icon position computed
|
||||
const iconPos = computed<string>(() => {
|
||||
return props.iconRight ? 'right': 'left'
|
||||
})
|
||||
|
||||
// Change the html tag of root element
|
||||
const htmlTag = computed(() => { return props.to ? 'RouterLink' : props.href ? 'a' : 'button'})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
variant="link"
|
||||
role="link"
|
||||
:label="props.label"
|
||||
:aria-label="props.label"
|
||||
:icon="props.icon"
|
||||
:disabled="props.disabled"
|
||||
:aria-disabled="props.disabled"
|
||||
:icon-pos="iconPos"
|
||||
:as="htmlTag"
|
||||
:href="props.href"
|
||||
:target="props.target"
|
||||
:to="props.to"
|
||||
:tabindex="props.disabled ? -1 : 0"
|
||||
@click="props.disabled && $event.preventDefault()"
|
||||
class="p-button"
|
||||
:class="[styles['text-body-MD-standard-text-Regular'], {'disabled': props.disabled}]"
|
||||
:style="props.disabled ? { pointerEvents: 'none' } : {}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
*{
|
||||
text-decoration: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.p-button{
|
||||
padding: 0px;
|
||||
gap: calc(var(--p-button-gap)/2);
|
||||
}
|
||||
|
||||
.p-button.disabled{
|
||||
--p-button-link-color: var(--text-disabled-grey);
|
||||
--p-button-link-hover-color: var(--text-disabled-grey);
|
||||
--p-button-link-active-color: var(--text-disabled-grey);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
</style>
|
58
test/VLink.spec.ts
Normal file
58
test/VLink.spec.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { mount } from '@vue/test-utils'
|
||||
import VLink from '../src/components/button/VLink.vue'
|
||||
import {test, expect, describe, vi} from 'vitest'
|
||||
|
||||
describe('VLink', () => {
|
||||
test('renders the label correctly', () => {
|
||||
const wrapper = mount(VLink, {
|
||||
props: {
|
||||
label: 'Link'
|
||||
}
|
||||
})
|
||||
expect(wrapper.text()).toContain('Link');
|
||||
})
|
||||
|
||||
test('renders as an anchor tag when `href` is provided', () => {
|
||||
const wrapper = mount(VLink, {
|
||||
props: {label: 'External', href: 'https://example.com' }
|
||||
});
|
||||
|
||||
const a = wrapper.find('a')
|
||||
expect(a.exists()).toBe(true);
|
||||
expect(a.attributes('href')).toBe('https://example.com');
|
||||
})
|
||||
|
||||
test('disables the link when `disabled` is true', () => {
|
||||
const wrapper = mount(VLink, {
|
||||
props: {label: 'Disabled', disabled: true }
|
||||
})
|
||||
|
||||
const button = wrapper.find('.p-button');
|
||||
expect(button.classes()).toContain('disabled');
|
||||
expect(button.attributes('aria-disabled')).toBe('true');
|
||||
});
|
||||
|
||||
test('prevents click when disabled', async () => {
|
||||
const clickHandler = vi.fn();
|
||||
const wrapper = mount(VLink, {
|
||||
props: {label: 'Disabled', disabled: true, onClick: clickHandler}
|
||||
})
|
||||
|
||||
await wrapper.trigger('click')
|
||||
expect(clickHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('places icon on the right when `iconRight` is true', () => {
|
||||
const wrapper = mount(VLink, {
|
||||
props: {
|
||||
label: 'link',
|
||||
icon: "ri-external-link-line",
|
||||
iconRight: true,
|
||||
}
|
||||
})
|
||||
const icon = wrapper.find('.p-button-icon-right');
|
||||
expect(icon.exists()).toBe(true);
|
||||
expect(icon.classes()).toContain('ri-external-link-line');
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user