diff --git a/packages/solid/src/reactive/signal.ts b/packages/solid/src/reactive/signal.ts index 67c70f265..7e063f535 100644 --- a/packages/solid/src/reactive/signal.ts +++ b/packages/solid/src/reactive/signal.ts @@ -1130,10 +1130,10 @@ export function registerGraph(value: SourceMapValue): void { export type ContextProviderComponent = FlowComponent<{ value: T }>; // Context API -export interface Context { +export interface Context { id: symbol; Provider: ContextProviderComponent; - defaultValue: T; + defaultValue: D; } /** @@ -1158,7 +1158,7 @@ export interface Context { export function createContext( defaultValue?: undefined, options?: EffectOptions -): Context; +): Context; export function createContext(defaultValue: T, options?: EffectOptions): Context; export function createContext( defaultValue?: T, @@ -1176,7 +1176,7 @@ export function createContext( * * @description https://www.solidjs.com/docs/latest/api#usecontext */ -export function useContext(context: Context): T { +export function useContext(context: Context): D extends undefined ? T | undefined : T { return Owner && Owner.context && Owner.context[context.id] !== undefined ? Owner.context[context.id] : context.defaultValue; @@ -1215,7 +1215,7 @@ export type SuspenseContextType = { resolved?: boolean; }; -type SuspenseContext = Context & { +type SuspenseContext = Context & { active?(): boolean; increment?(): void; decrement?(): void; @@ -1224,7 +1224,7 @@ type SuspenseContext = Context & { let SuspenseContext: SuspenseContext; export function getSuspenseContext() { - return SuspenseContext || (SuspenseContext = createContext()); + return SuspenseContext || (SuspenseContext = createContext()); } // Interop diff --git a/packages/solid/web/test/context.spec.tsx b/packages/solid/web/test/context.spec.tsx index ca0778be8..31b449f1e 100644 --- a/packages/solid/web/test/context.spec.tsx +++ b/packages/solid/web/test/context.spec.tsx @@ -20,13 +20,14 @@ describe("Testing Context", () => { ); }; - const div = document.createElement("div"); + it("should create context properly", () => { expect(ThemeContext.id).toBeDefined(); expect(ThemeContext.defaultValue).toBe("light"); }); it("should work with single provider child", () => { + const div = document.createElement("div"); render( () => ( @@ -35,11 +36,11 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[0].innerHTML).toBe("dark"); }); it("should work with single conditional provider child", () => { + const div = document.createElement("div"); render( () => ( @@ -48,11 +49,11 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[0].innerHTML).toBe("dark"); }); it("should work with multi provider child", () => { + const div = document.createElement("div"); render( () => ( @@ -62,11 +63,11 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[1].innerHTML).toBe("dark"); }); it("should work with multi conditional provider child", () => { + const div = document.createElement("div"); render( () => ( @@ -76,11 +77,11 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[1].innerHTML).toBe("dark"); }); it("should work with dynamic multi provider child", () => { + const div = document.createElement("div"); const child = () => ; render( () => ( @@ -91,11 +92,11 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[1].innerHTML).toBe("dark"); }); it("should work with dynamic multi conditional provider child", () => { + const div = document.createElement("div"); const child = () => ; render( () => ( @@ -106,7 +107,65 @@ describe("Testing Context", () => { ), div ); - expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark"); - div.innerHTML = ""; + expect(div.children[1].innerHTML).toBe("dark"); + }); + + const ThemeContextWithUndefined = createContext("light"); + const ComponentWithUndefined = () => { + const theme = useContext(ThemeContextWithUndefined); + // ?? 'undefined' will never get reached + return
{theme ?? "undefined"}
; + }; + + it("should override when nesting", () => { + const div = document.createElement("div"); + render( + () => ( + <> + + + + + + + + + + + + ), + div + ); + expect(div.children[0].innerHTML!).toBe("light"); + expect(div.children[1].innerHTML!).toBe("dark"); + expect(div.children[2].innerHTML!).toBe("darker"); + expect(div.children[3].innerHTML!).toBe("light"); + }); + + const ThemeContextWithoutDefault = createContext(); + const ComponentWithoutDefault = () => { + const theme = useContext(ThemeContextWithoutDefault); + return
{theme ?? "no-default"}
; + }; + + it("should work with no default provided", () => { + const div = document.createElement("div"); + render( + () => ( + <> + + + + + + + + + ), + div + ); + expect(div.children[0].innerHTML!).toBe("no-default"); + expect(div.children[1].innerHTML!).toBe("dark"); + expect(div.children[2].innerHTML!).toBe("no-default"); }); });