Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 97adef9

Browse files
committed
feat(vue-utils): add createContext helper
1 parent cfc0df4 commit 97adef9

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

packages/utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './tabbable'
99
export * from './assertion'
1010
export * from 'css-box-model'
1111
export * from './responsive'
12+
export * from './vue-helpers'
1213

1314
export function orient(options: {
1415
orientation?: 'vertical' | 'horizontal'

packages/utils/src/vue-helpers.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { inject, InjectionKey, provide } from 'vue'
2+
3+
export interface CreateContextOptions {
4+
/**
5+
* If `true`, Vue will throw if context is `null` or `undefined`
6+
* In some cases, you might want to support nested context, so you can set it to `false`
7+
*/
8+
strict?: boolean
9+
/**
10+
* Error message to throw if the context is `undefined`
11+
*/
12+
errorMessage?: string
13+
/**
14+
* The display name of the context
15+
*/
16+
name?: string
17+
}
18+
19+
type CreateContextReturn<T> = [(opts: T) => void, () => T]
20+
21+
/**
22+
* Creates a named context, provider, and hook.
23+
*
24+
* @param options create context options
25+
*/
26+
export function createContext<ContextType>(options: CreateContextOptions = {}) {
27+
const {
28+
strict = true,
29+
errorMessage = 'useContext: `context` is undefined. Seems you forgot to wrap component within the Provider',
30+
name,
31+
} = options
32+
33+
let contextSymbol = Symbol(`${name}Symbol`) as InjectionKey<ContextType>
34+
35+
function Provider(payload: ContextType) {
36+
provide<ContextType>(contextSymbol, payload)
37+
}
38+
39+
function useContext() {
40+
const context = inject(contextSymbol, null)
41+
42+
if (!context && strict) {
43+
throw new Error(errorMessage)
44+
}
45+
46+
return context
47+
}
48+
49+
return [Provider, useContext] as CreateContextReturn<ContextType>
50+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { createContext } from '@chakra-ui/vue-utils/src'
2+
import { defineComponent, h } from 'vue'
3+
import { render } from '../../test-utils/src'
4+
5+
interface ExampleContext {
6+
example: string
7+
}
8+
9+
const [ExampleProvider, useExampleContext] = createContext<ExampleContext>({
10+
name: 'ExampleContext',
11+
errorMessage:
12+
'useContext: `context` is undefined. Seems you forgot to wrap component within the Provider',
13+
})
14+
15+
const ExampleComponent = defineComponent({
16+
setup() {
17+
const { example } = useExampleContext()
18+
return () => h('div', `injected: ${example}`)
19+
},
20+
})
21+
22+
beforeEach(() => {
23+
// disable console.warn
24+
jest.spyOn(console, 'warn').mockImplementation(() => {})
25+
})
26+
27+
it('should provide and inject a context', () => {
28+
const { getByText } = render({
29+
components: { ExampleComponent },
30+
template: `
31+
<div>
32+
<ExampleComponent />
33+
</div>
34+
`,
35+
setup() {
36+
ExampleProvider({ example: 'works' })
37+
},
38+
})
39+
getByText(/works/)
40+
})
41+
42+
it('should throw an error when there is no provider', () => {
43+
expect(() => {
44+
render({
45+
components: { ExampleComponent },
46+
template: `<ExampleComponent />`,
47+
setup() {},
48+
})
49+
}).toThrowError(
50+
'useContext: `context` is undefined. Seems you forgot to wrap component within the Provider'
51+
)
52+
})

0 commit comments

Comments
 (0)