Skip to content

Commit

Permalink
chore: improve primitive tests (#166)
Browse files Browse the repository at this point in the history
* chore: improve primitive tests

* chore: add more tests

* [autofix.ci] apply automated fixes

* chore: add more primitive tests

* [autofix.ci] apply automated fixes
  • Loading branch information
dammy001 authored Jul 9, 2023
1 parent 7ba2151 commit ae24c95
Show file tree
Hide file tree
Showing 8 changed files with 1,812 additions and 2,472 deletions.
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# See http://EditorConfig.org for more information

# top-most EditorConfig file
root = true

# Every File
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
shamefully-hoist=true
strict-peer-dependencies=false
ignore-workspace-root-check=true
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

**An open-source UI component library for building high-quality, accessible design systems and web apps.**

Oku Primitives is a low-level UI component library with a focus on accessibility, customization and developer experience. You can use these components either as the base layer of your design system, or adopt them incrementally.
Oku Primitives is a low-level UI component library with a focus on accessibility, customization and developer experience. You can use these components either as the base layer of your design system or adopt them incrementally.

Website: [Oku Website](https://oku-ui.com)

Expand All @@ -20,8 +20,8 @@ Please read our [contributing guide](https://github.com/oku-ui/primitives/blob/m

# TODO Managements - 1/3

- [ ] Finding at least 1 sponsor - Contact us on twitter [@oku_ui](https://twitter.com/oku_ui)
- [ ] Finding at least 1 contributor - Contact us on twitter [@oku_ui](https://twitter.com/oku_ui)
- [ ] Finding at least 1 sponsor - Contact us on Twitter [@oku_ui](https://twitter.com/oku_ui)](https://twitter.com/oku_ui)
- [ ] Finding at least 1 contributor - Contact us on Twitter [@oku_ui](https://twitter.com/oku_ui)
- [x] Website opening and documentation writing [docs](https://github.com/oku-ui/docs)

# TODO Components - 7/28
Expand Down Expand Up @@ -80,11 +80,11 @@ Enter the component you want most in the components, leave the emojis and follow

Thanks to [@radix_ui](https://github.com/radix-ui/primitives) for the inspiration and the great work they've done with [Radix Primitives](https://radix-ui.com). We proceed through the initial stages of many codes by looking at them.

Thanks to Johnson Chu [@johnsoncodehk](https://github.com/johnsoncodehk). Supported me in many issues that I was stuck in Typescript.
Thanks to Johnson Chu [@johnsoncodehk](https://github.com/johnsoncodehk). Supported me with many issues that I was stuck in Typescript.

Thanks to Daniel Roe [@danielroe](https://github.com/danielroe). Nuxt has helped me in many areas so far.

Thanks to Kevin Deng [@sxzz](https://github.com/sxzz). Helped me a lot in the issues I was stuck in Vue. and [Vue Macros](https://vue-macros.sxzz.moe) is a great project.
Thanks to Kevin Deng [@sxzz](https://github.com/sxzz). Helped me a lot with the issues I was stuck in Vue. and [Vue Macros](https://vue-macros.sxzz.moe) is a great project.


---
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"play:vue": "pnpm clean:dts && turbo run dev --filter='./playground/vue3/**'",
"play:nuxt": "pnpm clean:dts && turbo run dev --filter='./playground/nuxt3/**'",
"play": "pnpm clean:dts && turbo run dev --filter='./playground/**'",
"test": "vitest",
"test": "vitest run",
"test:watch": "vitest --watch",
"coverage": "vitest run --coverage",
"build:storybook": "pnpm storybook build",
Expand Down
242 changes: 241 additions & 1 deletion packages/core/primitive/src/primitive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,249 @@ import { mount } from '@vue/test-utils'
import { Primitive } from './index'

describe('Primitive', () => {
it('should render correctly', () => {
it('should render div element correctly', () => {
const wrapper = mount(Primitive.div)
expect(wrapper.exists()).toBe(true)
expect(wrapper.find('div').exists()).toBe(true)
})

it('renders div element with custom class', () => {
const wrapper = mount(Primitive.div, {
props: {
class: 'custom-class',
},
})

const element = wrapper.find('div')

expect(element.exists()).toBe(true)
expect(element.classes()).toContain('custom-class')
})

it('renders div element with default slot content', () => {
const defaultSlot = 'default slot content'

const wrapper = mount(Primitive.div, {
slots: {
default: defaultSlot,
},
})

const element = wrapper.find('div')

expect(element.exists()).toBe(true)
expect(element.text()).toBe(defaultSlot)
})

it('renders button element with custom text', () => {
const buttonText = 'Login'

const wrapper = mount(Primitive.button, {
slots: {
default: buttonText,
},
})

const element = wrapper.find('button')

expect(element.exists()).toBe(true)
expect(element.text()).toBe(buttonText)
})

it('emits a click event when button is clicked', async () => {
const wrapper = mount(Primitive.button)

const button = wrapper.find('button')

await button.trigger('click')
expect(wrapper.emitted().click).toBeTruthy()
})

it('renders button element with custom attribute', () => {
const attributeName = 'id'
const attributeValue = 'button'

const wrapper = mount(Primitive.button, {
attrs: {
[attributeName]: attributeValue,
},
})

const element = wrapper.find('button')

expect(element.exists()).toBe(true)
expect(element.attributes(attributeName)).toBe(attributeValue)
})

it('renders button element with custom class', () => {
const customClass = 'custom-class'
const wrapper = mount(Primitive.button, {
props: {
class: customClass,
},
})

const element = wrapper.find('button')

expect(element.exists()).toBe(true)
expect(element.classes()).toContain(customClass)
})

it('renders button element with disabled attribute', () => {
const wrapper = mount(Primitive.button, {
props: {
disabled: true,
},
})

const element = wrapper.find('button')

expect(element.exists()).toBe(true)
expect(element.element.disabled).toBe(true)
})

it('renders button element with aria-label attribute', () => {
const ariaLabel = 'Close'
const wrapper = mount(Primitive.button, {
attrs: {
'aria-label': ariaLabel,
},
})

const element = wrapper.find('button')

expect(element.exists()).toBe(true)
expect(element.attributes('aria-label')).toBe(ariaLabel)
})

it('renders input element with type "text"', () => {
const wrapper = mount(Primitive.input, {
props: {
type: 'text',
},
})

const element = wrapper.find('input')
expect(element.exists()).toBe(true)
expect(element.attributes('type')).toBe('text')
})

it('renders input element with value prop', () => {
const value = 'Oku Primitives'

const wrapper = mount(Primitive.input, {
props: {
value,
},
})

const element = wrapper.find('input')

expect(element.exists()).toBe(true)
expect(element.element.value).toBe(value)
})

it('emits focus and blur events on input element', async () => {
const wrapper = mount(Primitive.input)
const element = wrapper.find('input')

await element.trigger('focus')
expect(wrapper.emitted('focus')).toBeTruthy()

await element.trigger('blur')
expect(wrapper.emitted('blur')).toBeTruthy()
})

it('renders input element with type "email"', () => {
const wrapper = mount(Primitive.input, {
props: {
type: 'email',
},
})

const element = wrapper.find('input')
expect(element.exists()).toBe(true)
expect(element.attributes('type')).toBe('email')
})

it('renders input element with required attribute', () => {
const wrapper = mount(Primitive.input, {
props: {
required: true,
},
})

const element = wrapper.find('input')
expect(element.exists()).toBe(true)
expect(element.element.required).toBe(true)
})

it('renders anchor element with href attribute', () => {
const href = 'https://example.com'
const wrapper = mount(Primitive.a, {
props: {
href,
},
})

const element = wrapper.find('a')
expect(element.exists()).toBe(true)
expect(element.attributes('href')).toBe(href)
})

it('renders anchor element with target="_blank" attribute', () => {
const wrapper = mount(Primitive.a, {
props: {
href: 'https://example.com',
target: '_blank',
},
})

const element = wrapper.find('a')
expect(element.exists()).toBe(true)
expect(element.attributes('target')).toBe('_blank')
})

it('renders anchor element with rel attribute', () => {
const rel = 'noopener noreferrer'
const wrapper = mount(Primitive.a, {
props: {
href: 'https://example.com',
rel,
},
})

const element = wrapper.find('a')
expect(element.exists()).toBe(true)
expect(element.attributes('rel')).not.toBe('opener')
expect(element.attributes('rel')).toBe(rel)
})

it('renders anchor element with text content', () => {
const textContent = 'Click here'
const wrapper = mount(Primitive.a, {
props: {
href: 'https://example.com',
},
slots: {
default: textContent,
},
})

const element = wrapper.find('a')
expect(element.exists()).toBe(true)
expect(element.text()).toBe(textContent)
})

it('emits click event when anchor element is clicked', async () => {
const wrapper = mount(Primitive.a, {
props: {
href: 'https://example.com',
},
})

const element = wrapper.find('a')
await element.trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
})
49 changes: 33 additions & 16 deletions packages/core/primitive/src/primitive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// TODO: IntrinsicElementAttributes vue 3.3 add
import type { ComponentPublicInstance, DefineComponent, FunctionalComponent, IntrinsicElementAttributes } from 'vue'
import type {
ComponentPublicInstance,
DefineComponent,
FunctionalComponent,
IntrinsicElementAttributes,
} from 'vue'
import { defineComponent, h, onMounted } from 'vue'

/* -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -29,26 +34,34 @@ type ElementConstructor<P> =
| ((props: P, ...args: any) => FunctionalComponent<any, any>)

// extends keyof JSX.IntrinsicElements | ElementConstructor<any>
type ComponentProps<T extends keyof JSX.IntrinsicElements | ElementConstructor<any>> =
T extends ElementConstructor<infer P>
? P
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: {}
type ComponentProps<
T extends keyof JSX.IntrinsicElements | ElementConstructor<any>,
> = T extends ElementConstructor<infer P>
? P
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: {}

type RefElement<T extends abstract new (...args: any) => any> = Omit<InstanceType<T>, keyof ComponentPublicInstance | 'class' | 'style'>
type RefElement<T extends abstract new (...args: any) => any> = Omit<
InstanceType<T>,
keyof ComponentPublicInstance | 'class' | 'style'
>

type MergeProps<T, U> = U & T

interface PrimitiveProps {
asChild?: boolean
}

type Primitives = { [E in typeof NODES[number]]: DefineComponent<{
asChild?: boolean
}> }
type Primitives = {
[E in (typeof NODES)[number]]: DefineComponent<{
asChild?: boolean
}>;
}

type ElementType<T extends keyof IntrinsicElementAttributes> = Partial<IntrinsicElementAttributes[T]>
type ElementType<T extends keyof IntrinsicElementAttributes> = Partial<
IntrinsicElementAttributes[T]
>

const Primitive = NODES.reduce((primitive, node) => {
const Node = defineComponent({
Expand All @@ -69,13 +82,17 @@ const Primitive = NODES.reduce((primitive, node) => {
}
},
})

return { ...primitive, [node]: Node }
}, {} as Primitives)

const OkuPrimitive = Primitive

export {
OkuPrimitive,
Primitive,
export { OkuPrimitive, Primitive }
export type {
ComponentProps,
MergeProps,
PrimitiveProps,
RefElement,
ElementType,
}
export type { ComponentProps, MergeProps, PrimitiveProps, RefElement, ElementType }
Loading

0 comments on commit ae24c95

Please sign in to comment.