diff --git a/packages/vue/src/composables/useContextFactory/index.test.ts b/packages/vue/src/composables/useContextFactory/index.test.ts new file mode 100644 index 0000000..0dc5033 --- /dev/null +++ b/packages/vue/src/composables/useContextFactory/index.test.ts @@ -0,0 +1,69 @@ +import { describe, it, expect } from 'vitest'; +import { defineComponent } from 'vue'; +import { useContextFactory } from './index'; +import { mount } from '@vue/test-utils'; +import { VueToolsError } from '../../utils'; + +function testFactory( + data: Data, + options?: { contextName?: string, fallback?: Data }, +) { + const contextName = options?.contextName ?? 'TestContext'; + + const [inject, provide] = useContextFactory(contextName); + + const Child = defineComponent({ + setup() { + const value = inject(options?.fallback); + return { value }; + }, + template: `{{ value }}`, + }); + + const Parent = defineComponent({ + components: { Child }, + setup() { + provide(data); + }, + template: ``, + }); + + return { + Parent, + Child, + }; +} + +// TODO: maybe replace template with passing mock functions to setup + +describe('useContextFactory', () => { + it('should provide and inject context correctly', () => { + const { Parent } = testFactory('test'); + + const component = mount(Parent); + + expect(component.text()).toBe('test'); + }); + + it('should throw an error when context is not provided', () => { + const { Child } = testFactory('test'); + + expect(() => mount(Child)).toThrow(VueToolsError); + }); + + it('should inject a fallback value when context is not provided', () => { + const { Child } = testFactory('test', { fallback: 'fallback' }); + + const component = mount(Child); + + expect(component.text()).toBe('fallback'); + }); + + it('should correctly handle null values', () => { + const { Parent } = testFactory(null); + + const component = mount(Parent); + + expect(component.text()).toBe(''); + }); +}); \ No newline at end of file diff --git a/packages/vue/src/composables/useContextFactory/index.ts b/packages/vue/src/composables/useContextFactory/index.ts new file mode 100644 index 0000000..773162e --- /dev/null +++ b/packages/vue/src/composables/useContextFactory/index.ts @@ -0,0 +1,34 @@ +import { inject, provide, type InjectionKey } from 'vue'; +import { VueToolsError } from '../../utils'; + +/** + * @name useContextFactory + * @category Utilities + * @description A composable that provides a factory for creating context with unique key + * + * @param {string} name The name of the context + * @returns {readonly [injectContext, provideContext]} The context factory + * @throws {VueToolsError} when the context is not provided + * + * @example + * const [injectContext, provideContext] = useContextFactory('MyContext'); + */ +export function useContextFactory(name: string) { + const injectionKey: InjectionKey = Symbol(name); + + const injectContext = (fallback?: Fallback) => { + const context = inject(injectionKey, fallback); + + if (context !== undefined) + return context; + + throw new VueToolsError(`useContextFactory: '${name}' context is not provided`); + }; + + const provideContext = (context: ContextValue) => { + provide(injectionKey, context); + return context; + }; + + return [injectContext, provideContext] as const; + } \ No newline at end of file