diff --git a/jest.config.json b/jest.config.json index f32fc8f..02d4c2a 100644 --- a/jest.config.json +++ b/jest.config.json @@ -3,5 +3,5 @@ "transform": { "^.+\\.tsx?$": "ts-jest" }, - "testRegex": "__tests__/.*spec\\.ts$" + "testRegex": "__tests__/.*spec\\.tsx?$" } diff --git a/package-lock.json b/package-lock.json index 35549f8..fd38c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@types/jest": "^29.5.11", "@types/react": "^18.2.45", "@types/react-native": "^0.72.8", + "@types/react-test-renderer": "^17.0.0", "@types/tailwindcss": "^3.1.0", "@typescript-eslint/eslint-plugin": "^6.15.0", "@typescript-eslint/parser": "^6.15.0", @@ -28,6 +29,7 @@ "prettier": "^3.1.1", "react": "^17.0.2", "react-native": "^0.73.1", + "react-test-renderer": "^17.0.0", "ts-jest": "^29.1.1", "typescript": "^5.3.3" }, @@ -3102,6 +3104,26 @@ "@types/react": "*" } }, + "node_modules/@types/react-test-renderer": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.9.tgz", + "integrity": "sha512-bOfxcu5oZ+KxvACScbkTwZ4eGCtZFTz4VZCOVAIfGbThxqiXSIGipKVG8ubaYBXquUSQROzNIUzviWdSnnAlzg==", + "dev": true, + "dependencies": { + "@types/react": "^17" + } + }, + "node_modules/@types/react-test-renderer/node_modules/@types/react": { + "version": "17.0.75", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.75.tgz", + "integrity": "sha512-MSA+NzEzXnQKrqpO63CYqNstFjsESgvJAdAyyJ1n6ZQq/GLgf6nOfIKwk+Twuz0L1N6xPe+qz5xRCJrbhMaLsw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -9525,6 +9547,31 @@ "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-test-renderer": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", + "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "react-is": "^17.0.2", + "react-shallow-renderer": "^16.13.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-test-renderer/node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -13660,6 +13707,28 @@ "@types/react": "*" } }, + "@types/react-test-renderer": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.9.tgz", + "integrity": "sha512-bOfxcu5oZ+KxvACScbkTwZ4eGCtZFTz4VZCOVAIfGbThxqiXSIGipKVG8ubaYBXquUSQROzNIUzviWdSnnAlzg==", + "dev": true, + "requires": { + "@types/react": "^17" + }, + "dependencies": { + "@types/react": { + "version": "17.0.75", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.75.tgz", + "integrity": "sha512-MSA+NzEzXnQKrqpO63CYqNstFjsESgvJAdAyyJ1n6ZQq/GLgf6nOfIKwk+Twuz0L1N6xPe+qz5xRCJrbhMaLsw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + } + } + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -18447,6 +18516,30 @@ "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" } }, + "react-test-renderer": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", + "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "react-is": "^17.0.2", + "react-shallow-renderer": "^16.13.1", + "scheduler": "^0.20.2" + }, + "dependencies": { + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 54a174a..064102f 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@types/jest": "^29.5.11", "@types/react": "^18.2.45", "@types/react-native": "^0.72.8", + "@types/react-test-renderer": "^17.0.0", "@types/tailwindcss": "^3.1.0", "@typescript-eslint/eslint-plugin": "^6.15.0", "@typescript-eslint/parser": "^6.15.0", @@ -65,6 +66,7 @@ "prettier": "^3.1.1", "react": "^17.0.2", "react-native": "^0.73.1", + "react-test-renderer": "^17.0.0", "ts-jest": "^29.1.1", "typescript": "^5.3.3" }, diff --git a/src/__tests__/color-scheme.spec.tsx b/src/__tests__/color-scheme.spec.tsx new file mode 100644 index 0000000..3990939 --- /dev/null +++ b/src/__tests__/color-scheme.spec.tsx @@ -0,0 +1,56 @@ +import renderer from 'react-test-renderer'; +import rn from 'react-native'; +import { describe, it, expect } from '@jest/globals'; +import React from 'react'; +import type { RnColorScheme, TailwindFn } from '../'; +import { create, useDeviceContext, useAppColorScheme } from '../'; + +jest.mock(`react-native`, () => ({ + Platform: { OS: `ios` }, + Appearance: { getColorScheme: () => `light` }, + useColorScheme: () => `light`, + useWindowDimensions: () => ({ width: 320, height: 640, fontScale: 1, scale: 2 }), +})); + +const Test: React.FC<{ tw: TailwindFn; initial?: RnColorScheme }> = ({ tw, initial }) => { + useDeviceContext(tw, { withDeviceColorScheme: true }); + const [colorScheme] = useAppColorScheme(tw, initial); + return ( + <> + {String(colorScheme)} + {tw.prefixMatch(`dark`) ? `match:dark` : `no-match:dark`} + + ); +}; + +describe(`useAppColorScheme()`, () => { + it(`should initialize to ambient color scheme, if no initializer`, () => { + rn.Appearance.getColorScheme = () => `dark`; + + let component = renderer.create(); + expect(component.toJSON()).toEqual([`dark`, `match:dark`]); + + rn.Appearance.getColorScheme = () => `light`; + component = renderer.create(); + expect(component.toJSON()).toEqual([`light`, `no-match:dark`]); + + rn.Appearance.getColorScheme = () => null; + component = renderer.create(); + expect(component.toJSON()).toEqual([`null`, `no-match:dark`]); + + rn.Appearance.getColorScheme = () => undefined; + component = renderer.create(); + expect(component.toJSON()).toEqual([`undefined`, `no-match:dark`]); + }); + + it(`should initialize to explicitly passed color scheme when initializer provided`, () => { + rn.Appearance.getColorScheme = () => `dark`; + + let component = renderer.create(); + expect(component.toJSON()).toEqual([`light`, `no-match:dark`]); + + rn.Appearance.getColorScheme = () => `light`; + component = renderer.create(); + expect(component.toJSON()).toEqual([`dark`, `match:dark`]); + }); +}); diff --git a/src/hooks.ts b/src/hooks.ts index dc4b4fd..44eb30b 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -28,9 +28,12 @@ export function useAppColorScheme( toggleColorScheme: () => void, setColorScheme: (colorScheme: RnColorScheme) => void, ] { - const [colorScheme, setColorScheme] = useState( - initialValue ?? Appearance.getColorScheme(), - ); + const [colorScheme, setColorScheme] = useState(() => { + const init = initialValue ?? Appearance.getColorScheme(); + tw.setColorScheme(init); + return init; + }); + return [ colorScheme, () => { diff --git a/tsconfig.json b/tsconfig.json index 30fe818..748bb80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "lib": ["ES2019"], "target": "ES2019", "declaration": true, - "moduleResolution": "node" + "moduleResolution": "node", + "jsx": "react" }, "exclude": ["./dist", "**/*.spec.ts"] }