mirror of
https://github.com/lights0123/n-link.git
synced 2026-08-07 18:23:28 +00:00
feat(ui): add AppButton component
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1e2a8c5b1e
commit
ebf7435221
35
n-link-core/components/AppButton.spec.ts
Normal file
35
n-link-core/components/AppButton.spec.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import AppButton from './AppButton.vue';
|
||||||
|
|
||||||
|
describe('AppButton', () => {
|
||||||
|
it('renders slot content', () => {
|
||||||
|
const wrapper = mount(AppButton, { slots: { default: 'Download' } });
|
||||||
|
expect(wrapper.text()).toBe('Download');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies the primary accent style by default', () => {
|
||||||
|
const wrapper = mount(AppButton, { slots: { default: 'Go' } });
|
||||||
|
expect(wrapper.get('button').classes()).toContain('bg-accent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies the danger variant', () => {
|
||||||
|
const wrapper = mount(AppButton, {
|
||||||
|
props: { variant: 'danger' }, slots: { default: 'Delete' },
|
||||||
|
});
|
||||||
|
expect(wrapper.get('button').classes()).toContain('bg-danger');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits click when enabled', async () => {
|
||||||
|
const wrapper = mount(AppButton, { slots: { default: 'Go' } });
|
||||||
|
await wrapper.get('button').trigger('click');
|
||||||
|
expect(wrapper.emitted('click')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reflects the disabled prop', () => {
|
||||||
|
const wrapper = mount(AppButton, {
|
||||||
|
props: { disabled: true }, slots: { default: 'Go' },
|
||||||
|
});
|
||||||
|
expect(wrapper.get('button').attributes('disabled')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
31
n-link-core/components/AppButton.vue
Normal file
31
n-link-core/components/AppButton.vue
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<button class="rounded font-bold focus:outline-none focus-visible:ring-2
|
||||||
|
focus-visible:ring-accent transition-colors disabled:opacity-75
|
||||||
|
disabled:cursor-not-allowed"
|
||||||
|
:class="[variantClass, sizeClass]" :disabled="disabled">
|
||||||
|
<slot/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
variant?: 'primary' | 'secondary' | 'danger' | 'success';
|
||||||
|
size?: 'md' | 'sm';
|
||||||
|
disabled?: boolean;
|
||||||
|
}>(), {
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'md',
|
||||||
|
disabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const variantClass = computed(() => ({
|
||||||
|
primary: 'bg-accent text-accent-fg',
|
||||||
|
secondary: 'bg-raised text-fg border border-edge',
|
||||||
|
danger: 'bg-danger text-white',
|
||||||
|
success: 'bg-success text-white',
|
||||||
|
}[props.variant]));
|
||||||
|
|
||||||
|
const sizeClass = computed(() => (props.size === 'sm' ? 'px-3 py-2 text-sm' : 'px-6 py-2.5'));
|
||||||
|
</script>
|
||||||
Loading…
Reference in a new issue