@youversion/platform-react-hooks 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/useLanguage.d.ts +9 -0
- package/dist/useLanguage.d.ts.map +1 -0
- package/dist/useLanguage.js +17 -0
- package/dist/useLanguage.js.map +1 -0
- package/dist/useLanguageClient.d.ts +3 -0
- package/dist/useLanguageClient.d.ts.map +1 -0
- package/dist/useLanguageClient.js +18 -0
- package/dist/useLanguageClient.js.map +1 -0
- package/dist/useLanguages.d.ts.map +1 -1
- package/dist/useLanguages.js +2 -15
- package/dist/useLanguages.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/useLanguage.test.tsx +116 -0
- package/src/useLanguage.ts +32 -0
- package/src/useLanguageClient.test.tsx +112 -0
- package/src/useLanguageClient.ts +25 -0
- package/src/useLanguages.test.tsx +11 -103
- package/src/useLanguages.ts +2 -20
- package/vitest.config.ts +2 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @youversion/platform-react-hooks@1.
|
|
2
|
+
> @youversion/platform-react-hooks@1.9.0 build /home/runner/work/platform-sdk-react/platform-sdk-react/packages/hooks
|
|
3
3
|
> tsc -p tsconfig.build.json
|
|
4
4
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @youversion/platform-react-hooks
|
|
2
2
|
|
|
3
|
+
## 1.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d4b0071: feat(hooks): Add useLanguage hook to retrieve a language from api
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [d4b0071]
|
|
12
|
+
- @youversion/platform-core@1.9.0
|
|
13
|
+
|
|
3
14
|
## 1.8.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAG3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAG3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAE3B,aAAa;AACb,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAE3B,aAAa;AACb,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type UseApiDataOptions } from './useApiData';
|
|
2
|
+
import { type Language } from '@youversion/platform-core';
|
|
3
|
+
export declare function useLanguage(languageId: string, apiOptions?: UseApiDataOptions): {
|
|
4
|
+
language: Language | null;
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
refetch: () => void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=useLanguage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLanguage.d.ts","sourceRoot":"","sources":["../src/useLanguage.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAG1D,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,iBAAiB,GAC7B;IACD,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAiBA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useApiData } from './useApiData';
|
|
3
|
+
import {} from '@youversion/platform-core';
|
|
4
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
5
|
+
export function useLanguage(languageId, apiOptions) {
|
|
6
|
+
const languagesClient = useLanguagesClient();
|
|
7
|
+
const { data, loading, error, refetch } = useApiData(() => languagesClient.getLanguage(languageId), [languagesClient, languageId], {
|
|
8
|
+
enabled: apiOptions?.enabled !== false,
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
language: data,
|
|
12
|
+
loading,
|
|
13
|
+
error,
|
|
14
|
+
refetch,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=useLanguage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLanguage.js","sourceRoot":"","sources":["../src/useLanguage.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAClE,OAAO,EAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,UAA8B;IAO9B,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,EAC7C,CAAC,eAAe,EAAE,UAAU,CAAC,EAC7B;QACE,OAAO,EAAE,UAAU,EAAE,OAAO,KAAK,KAAK;KACvC,CACF,CAAC;IAEF,OAAO;QACL,QAAQ,EAAE,IAAI;QACd,OAAO;QACP,KAAK;QACL,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLanguageClient.d.ts","sourceRoot":"","sources":["../src/useLanguageClient.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEvE,wBAAgB,kBAAkB,IAAI,eAAe,CAkBpD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useMemo } from 'react';
|
|
3
|
+
import { YouVersionContext } from './context';
|
|
4
|
+
import { ApiClient, LanguagesClient } from '@youversion/platform-core';
|
|
5
|
+
export function useLanguagesClient() {
|
|
6
|
+
const context = useContext(YouVersionContext);
|
|
7
|
+
return useMemo(() => {
|
|
8
|
+
if (!context?.appKey) {
|
|
9
|
+
throw new Error('YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.');
|
|
10
|
+
}
|
|
11
|
+
return new LanguagesClient(new ApiClient({
|
|
12
|
+
appKey: context.appKey,
|
|
13
|
+
apiHost: context.apiHost,
|
|
14
|
+
installationId: context.installationId,
|
|
15
|
+
}));
|
|
16
|
+
}, [context?.apiHost, context?.appKey, context?.installationId]);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=useLanguageClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLanguageClient.js","sourceRoot":"","sources":["../src/useLanguageClient.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE9C,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,uHAAuH,CACxH,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,eAAe,CACxB,IAAI,SAAS,CAAC;YACZ,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,cAAc,EAAE,OAAO,CAAC,cAAc;SACvC,CAAC,CACH,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLanguages.d.ts","sourceRoot":"","sources":["../src/useLanguages.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLanguages.d.ts","sourceRoot":"","sources":["../src/useLanguages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,2BAA2B,CAAC;AAGnC,wBAAgB,YAAY,CAC1B,OAAO,GAAE,mBAAwB,EACjC,UAAU,CAAC,EAAE,iBAAiB,GAC7B;IACD,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAiBA"}
|
package/dist/useLanguages.js
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import { useContext } from 'react';
|
|
4
|
-
import { YouVersionContext } from './context';
|
|
5
|
-
import { LanguagesClient, ApiClient } from '@youversion/platform-core';
|
|
6
2
|
import { useApiData } from './useApiData';
|
|
7
3
|
import {} from '@youversion/platform-core';
|
|
4
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
8
5
|
export function useLanguages(options = {}, apiOptions) {
|
|
9
|
-
const
|
|
10
|
-
const languagesClient = useMemo(() => {
|
|
11
|
-
if (!context?.appKey) {
|
|
12
|
-
throw new Error('YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.');
|
|
13
|
-
}
|
|
14
|
-
return new LanguagesClient(new ApiClient({
|
|
15
|
-
appKey: context.appKey,
|
|
16
|
-
apiHost: context.apiHost,
|
|
17
|
-
installationId: context.installationId,
|
|
18
|
-
}));
|
|
19
|
-
}, [context?.apiHost, context?.appKey, context?.installationId]);
|
|
6
|
+
const languagesClient = useLanguagesClient();
|
|
20
7
|
const { data, loading, error, refetch } = useApiData(() => languagesClient.getLanguages(options), [languagesClient, options?.country, options?.page_size, options?.page_token], {
|
|
21
8
|
enabled: apiOptions?.enabled !== false,
|
|
22
9
|
});
|
package/dist/useLanguages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLanguages.js","sourceRoot":"","sources":["../src/useLanguages.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"useLanguages.js","sourceRoot":"","sources":["../src/useLanguages.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAClE,OAAO,EAIN,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,UAAU,YAAY,CAC1B,UAA+B,EAAE,EACjC,UAA8B;IAO9B,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC,OAAO,CAAC,EAC3C,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,EAC5E;QACE,OAAO,EAAE,UAAU,EAAE,OAAO,KAAK,KAAK;KACvC,CACF,CAAC;IAEF,OAAO;QACL,SAAS,EAAE,IAAI;QACf,OAAO;QACP,KAAK;QACL,OAAO;KACR,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youversion/platform-react-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@youversion/platform-core": "1.
|
|
25
|
+
"@youversion/platform-core": "1.9.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=19.1.0 <20.0.0"
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"jsdom": "27.0.1",
|
|
38
38
|
"typescript": "5.9.3",
|
|
39
39
|
"vitest": "4.0.4",
|
|
40
|
-
"@internal/
|
|
41
|
-
"@internal/
|
|
40
|
+
"@internal/tsconfig": "0.0.0",
|
|
41
|
+
"@internal/eslint-config": "0.0.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"dev": "tsc --watch",
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { useLanguage } from './useLanguage';
|
|
4
|
+
import { type LanguagesClient } from '@youversion/platform-core';
|
|
5
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
6
|
+
|
|
7
|
+
vi.mock('./useLanguageClient');
|
|
8
|
+
|
|
9
|
+
describe('useLanguage', () => {
|
|
10
|
+
const mockGetLanguage = vi.fn();
|
|
11
|
+
|
|
12
|
+
const mockLanguage = {
|
|
13
|
+
id: 'en',
|
|
14
|
+
language: 'en',
|
|
15
|
+
script: 'Latn',
|
|
16
|
+
script_name: 'Latin',
|
|
17
|
+
aliases: ['eng'],
|
|
18
|
+
display_names: { en: 'English' },
|
|
19
|
+
scripts: ['Latn'],
|
|
20
|
+
variants: [],
|
|
21
|
+
countries: ['US', 'GB', 'CA', 'AU'],
|
|
22
|
+
text_direction: 'ltr',
|
|
23
|
+
writing_population: 1500000000,
|
|
24
|
+
speaking_population: 1500000000,
|
|
25
|
+
default_bible_id: 111,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
vi.resetAllMocks();
|
|
30
|
+
|
|
31
|
+
mockGetLanguage.mockResolvedValue(mockLanguage);
|
|
32
|
+
|
|
33
|
+
const mockClient: Partial<LanguagesClient> = { getLanguage: mockGetLanguage };
|
|
34
|
+
vi.mocked(useLanguagesClient).mockReturnValue(mockClient as LanguagesClient);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('fetching language', () => {
|
|
38
|
+
it('should fetch a language by id', async () => {
|
|
39
|
+
const { result } = renderHook(() => useLanguage('en'));
|
|
40
|
+
|
|
41
|
+
expect(result.current.loading).toBe(true);
|
|
42
|
+
expect(result.current.language).toBe(null);
|
|
43
|
+
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(result.current.loading).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(mockGetLanguage).toHaveBeenCalledWith('en');
|
|
49
|
+
expect(result.current.language).toEqual(mockLanguage);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should refetch when languageId changes', async () => {
|
|
53
|
+
const { result, rerender } = renderHook(({ languageId }) => useLanguage(languageId), {
|
|
54
|
+
initialProps: { languageId: 'en' },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await waitFor(() => {
|
|
58
|
+
expect(result.current.loading).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(mockGetLanguage).toHaveBeenCalledTimes(1);
|
|
62
|
+
expect(mockGetLanguage).toHaveBeenCalledWith('en');
|
|
63
|
+
|
|
64
|
+
rerender({ languageId: 'es' });
|
|
65
|
+
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
expect(mockGetLanguage).toHaveBeenCalledTimes(2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(mockGetLanguage).toHaveBeenLastCalledWith('es');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should not fetch when enabled is false', async () => {
|
|
74
|
+
const { result } = renderHook(() => useLanguage('en', { enabled: false }));
|
|
75
|
+
|
|
76
|
+
await waitFor(() => {
|
|
77
|
+
expect(result.current.loading).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(mockGetLanguage).not.toHaveBeenCalled();
|
|
81
|
+
expect(result.current.language).toBe(null);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle fetch errors', async () => {
|
|
85
|
+
const error = new Error('Failed to fetch language');
|
|
86
|
+
mockGetLanguage.mockRejectedValueOnce(error);
|
|
87
|
+
|
|
88
|
+
const { result } = renderHook(() => useLanguage('en'));
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
expect(result.current.loading).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(result.current.error).toEqual(error);
|
|
95
|
+
expect(result.current.language).toBe(null);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should support manual refetch', async () => {
|
|
99
|
+
const { result } = renderHook(() => useLanguage('en'));
|
|
100
|
+
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
expect(result.current.loading).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(mockGetLanguage).toHaveBeenCalledTimes(1);
|
|
106
|
+
|
|
107
|
+
act(() => {
|
|
108
|
+
result.current.refetch();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await waitFor(() => {
|
|
112
|
+
expect(mockGetLanguage).toHaveBeenCalledTimes(2);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useApiData, type UseApiDataOptions } from './useApiData';
|
|
4
|
+
import { type Language } from '@youversion/platform-core';
|
|
5
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
6
|
+
|
|
7
|
+
export function useLanguage(
|
|
8
|
+
languageId: string,
|
|
9
|
+
apiOptions?: UseApiDataOptions,
|
|
10
|
+
): {
|
|
11
|
+
language: Language | null;
|
|
12
|
+
loading: boolean;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
refetch: () => void;
|
|
15
|
+
} {
|
|
16
|
+
const languagesClient = useLanguagesClient();
|
|
17
|
+
|
|
18
|
+
const { data, loading, error, refetch } = useApiData<Language>(
|
|
19
|
+
() => languagesClient.getLanguage(languageId),
|
|
20
|
+
[languagesClient, languageId],
|
|
21
|
+
{
|
|
22
|
+
enabled: apiOptions?.enabled !== false,
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
language: data,
|
|
28
|
+
loading,
|
|
29
|
+
error,
|
|
30
|
+
refetch,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
5
|
+
import { YouVersionContext } from './context';
|
|
6
|
+
import { LanguagesClient, ApiClient } from '@youversion/platform-core';
|
|
7
|
+
|
|
8
|
+
// Mock the core package
|
|
9
|
+
vi.mock('@youversion/platform-core', async () => {
|
|
10
|
+
const actual = await vi.importActual('@youversion/platform-core');
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
LanguagesClient: vi.fn(function () {
|
|
14
|
+
return {};
|
|
15
|
+
}),
|
|
16
|
+
ApiClient: vi.fn(function () {
|
|
17
|
+
return { isApiClient: true };
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('useLanguagesClient', () => {
|
|
23
|
+
const mockAppKey = 'test-app-key';
|
|
24
|
+
|
|
25
|
+
const createWrapper = (contextValue: { appKey: string }) => {
|
|
26
|
+
return ({ children }: { children: ReactNode }) => (
|
|
27
|
+
<YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.resetAllMocks();
|
|
33
|
+
|
|
34
|
+
vi.mocked(LanguagesClient).mockImplementation(function () {
|
|
35
|
+
const mockClient: Partial<LanguagesClient> = { getLanguages: vi.fn() };
|
|
36
|
+
return mockClient;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
vi.mocked(ApiClient).mockImplementation(function () {
|
|
40
|
+
const mockApiClient = { isApiClient: true };
|
|
41
|
+
return mockApiClient;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('context validation', () => {
|
|
46
|
+
it('should throw error when context is not provided', () => {
|
|
47
|
+
expect(() => renderHook(() => useLanguagesClient())).toThrow(
|
|
48
|
+
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should throw error when appKey is missing', () => {
|
|
53
|
+
const wrapper = createWrapper({
|
|
54
|
+
appKey: '',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(() => renderHook(() => useLanguagesClient(), { wrapper })).toThrow(
|
|
58
|
+
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('client creation', () => {
|
|
64
|
+
it('should create LanguagesClient with correct ApiClient config', () => {
|
|
65
|
+
const wrapper = createWrapper({
|
|
66
|
+
appKey: mockAppKey,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
renderHook(() => useLanguagesClient(), { wrapper });
|
|
70
|
+
|
|
71
|
+
expect(ApiClient).toHaveBeenCalledWith({
|
|
72
|
+
appKey: mockAppKey,
|
|
73
|
+
});
|
|
74
|
+
expect(LanguagesClient).toHaveBeenCalledWith(expect.objectContaining({ isApiClient: true }));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should memoize LanguagesClient instance', () => {
|
|
78
|
+
const wrapper = createWrapper({
|
|
79
|
+
appKey: mockAppKey,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const { rerender } = renderHook(() => useLanguagesClient(), { wrapper });
|
|
83
|
+
|
|
84
|
+
rerender();
|
|
85
|
+
|
|
86
|
+
expect(LanguagesClient).toHaveBeenCalledTimes(1);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should create new LanguagesClient when context values change', () => {
|
|
90
|
+
let currentAppKey = mockAppKey;
|
|
91
|
+
|
|
92
|
+
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
93
|
+
<YouVersionContext.Provider
|
|
94
|
+
value={{
|
|
95
|
+
appKey: currentAppKey,
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
{children}
|
|
99
|
+
</YouVersionContext.Provider>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const { rerender } = renderHook(() => useLanguagesClient(), { wrapper });
|
|
103
|
+
|
|
104
|
+
expect(LanguagesClient).toHaveBeenCalledTimes(1);
|
|
105
|
+
|
|
106
|
+
currentAppKey = 'new-app-key';
|
|
107
|
+
rerender();
|
|
108
|
+
|
|
109
|
+
expect(LanguagesClient).toHaveBeenCalledTimes(2);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useContext, useMemo } from 'react';
|
|
4
|
+
import { YouVersionContext } from './context';
|
|
5
|
+
import { ApiClient, LanguagesClient } from '@youversion/platform-core';
|
|
6
|
+
|
|
7
|
+
export function useLanguagesClient(): LanguagesClient {
|
|
8
|
+
const context = useContext(YouVersionContext);
|
|
9
|
+
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
if (!context?.appKey) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return new LanguagesClient(
|
|
18
|
+
new ApiClient({
|
|
19
|
+
appKey: context.appKey,
|
|
20
|
+
apiHost: context.apiHost,
|
|
21
|
+
installationId: context.installationId,
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
}, [context?.apiHost, context?.appKey, context?.installationId]);
|
|
25
|
+
}
|
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
2
|
-
import { describe, it, expect, vi, beforeEach
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
3
|
import type { ReactNode } from 'react';
|
|
4
4
|
import { useLanguages } from './useLanguages';
|
|
5
5
|
import { YouVersionContext } from './context';
|
|
6
6
|
import {
|
|
7
|
-
LanguagesClient,
|
|
8
|
-
ApiClient,
|
|
7
|
+
type LanguagesClient,
|
|
9
8
|
type Collection,
|
|
10
9
|
type Language,
|
|
11
10
|
type GetLanguagesOptions,
|
|
12
11
|
} from '@youversion/platform-core';
|
|
12
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
vi.mock('@youversion/platform-core', async () => {
|
|
16
|
-
const actual = await vi.importActual('@youversion/platform-core');
|
|
17
|
-
return {
|
|
18
|
-
...actual,
|
|
19
|
-
LanguagesClient: vi.fn(function () {
|
|
20
|
-
return {};
|
|
21
|
-
}),
|
|
22
|
-
ApiClient: vi.fn(function () {
|
|
23
|
-
return { isApiClient: true };
|
|
24
|
-
}),
|
|
25
|
-
};
|
|
26
|
-
});
|
|
14
|
+
vi.mock('./useLanguageClient');
|
|
27
15
|
|
|
28
16
|
describe('useLanguages', () => {
|
|
29
17
|
const mockAppKey = 'test-app-key';
|
|
18
|
+
const mockGetLanguages = vi.fn();
|
|
30
19
|
|
|
31
20
|
const mockLanguages: Collection<Language> = {
|
|
32
21
|
data: [
|
|
@@ -46,7 +35,7 @@ describe('useLanguages', () => {
|
|
|
46
35
|
text_direction: 'ltr',
|
|
47
36
|
writing_population: 370000000,
|
|
48
37
|
speaking_population: 1500000000,
|
|
49
|
-
|
|
38
|
+
default_bible_id: 1,
|
|
50
39
|
},
|
|
51
40
|
{
|
|
52
41
|
id: 'es',
|
|
@@ -64,14 +53,12 @@ describe('useLanguages', () => {
|
|
|
64
53
|
text_direction: 'ltr',
|
|
65
54
|
writing_population: 470000000,
|
|
66
55
|
speaking_population: 580000000,
|
|
67
|
-
|
|
56
|
+
default_bible_id: 128,
|
|
68
57
|
},
|
|
69
58
|
],
|
|
70
59
|
next_page_token: null,
|
|
71
60
|
};
|
|
72
61
|
|
|
73
|
-
let mockGetLanguages: Mock;
|
|
74
|
-
|
|
75
62
|
const createWrapper = (contextValue: { appKey: string }) => {
|
|
76
63
|
return ({ children }: { children: ReactNode }) => (
|
|
77
64
|
<YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
|
|
@@ -79,91 +66,12 @@ describe('useLanguages', () => {
|
|
|
79
66
|
};
|
|
80
67
|
|
|
81
68
|
beforeEach(() => {
|
|
82
|
-
vi.
|
|
69
|
+
vi.resetAllMocks();
|
|
83
70
|
|
|
84
|
-
mockGetLanguages
|
|
85
|
-
|
|
86
|
-
(LanguagesClient as unknown as ReturnType<typeof vi.fn>).mockImplementation(function () {
|
|
87
|
-
return {
|
|
88
|
-
getLanguages: mockGetLanguages,
|
|
89
|
-
};
|
|
90
|
-
});
|
|
71
|
+
mockGetLanguages.mockResolvedValue(mockLanguages);
|
|
91
72
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
isApiClient: true,
|
|
95
|
-
};
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('context validation', () => {
|
|
100
|
-
it('should throw error when context is not provided', () => {
|
|
101
|
-
expect(() => renderHook(() => useLanguages({ country: 'US' }))).toThrow(
|
|
102
|
-
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should throw error when appKey is missing', () => {
|
|
107
|
-
const wrapper = createWrapper({
|
|
108
|
-
appKey: '',
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
expect(() => renderHook(() => useLanguages({ country: 'US' }), { wrapper })).toThrow(
|
|
112
|
-
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('client creation', () => {
|
|
118
|
-
it('should create LanguagesClient with correct ApiClient config', () => {
|
|
119
|
-
const wrapper = createWrapper({
|
|
120
|
-
appKey: mockAppKey,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
renderHook(() => useLanguages({ country: 'US' }), { wrapper });
|
|
124
|
-
|
|
125
|
-
expect(ApiClient).toHaveBeenCalledWith({
|
|
126
|
-
appKey: mockAppKey,
|
|
127
|
-
});
|
|
128
|
-
expect(LanguagesClient).toHaveBeenCalledWith(expect.objectContaining({ isApiClient: true }));
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should memoize LanguagesClient instance', () => {
|
|
132
|
-
const wrapper = createWrapper({
|
|
133
|
-
appKey: mockAppKey,
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const { result, rerender } = renderHook(() => useLanguages({ country: 'US' }), { wrapper });
|
|
137
|
-
const _firstRefetch = result.current.refetch;
|
|
138
|
-
|
|
139
|
-
rerender();
|
|
140
|
-
const _secondRefetch = result.current.refetch;
|
|
141
|
-
|
|
142
|
-
expect(LanguagesClient).toHaveBeenCalledTimes(1);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('should create new LanguagesClient when context values change', () => {
|
|
146
|
-
let currentAppKey = mockAppKey;
|
|
147
|
-
|
|
148
|
-
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
149
|
-
<YouVersionContext.Provider
|
|
150
|
-
value={{
|
|
151
|
-
appKey: currentAppKey,
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
{children}
|
|
155
|
-
</YouVersionContext.Provider>
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
const { rerender } = renderHook(() => useLanguages({ country: 'US' }), { wrapper });
|
|
159
|
-
|
|
160
|
-
expect(LanguagesClient).toHaveBeenCalledTimes(1);
|
|
161
|
-
|
|
162
|
-
currentAppKey = 'new-app-key';
|
|
163
|
-
rerender();
|
|
164
|
-
|
|
165
|
-
expect(LanguagesClient).toHaveBeenCalledTimes(2);
|
|
166
|
-
});
|
|
73
|
+
const mockClient: Partial<LanguagesClient> = { getLanguages: mockGetLanguages };
|
|
74
|
+
vi.mocked(useLanguagesClient).mockReturnValue(mockClient as LanguagesClient);
|
|
167
75
|
});
|
|
168
76
|
|
|
169
77
|
describe('fetching languages', () => {
|
package/src/useLanguages.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useMemo } from 'react';
|
|
4
|
-
import { useContext } from 'react';
|
|
5
|
-
import { YouVersionContext } from './context';
|
|
6
|
-
import { LanguagesClient, ApiClient } from '@youversion/platform-core';
|
|
7
3
|
import { useApiData, type UseApiDataOptions } from './useApiData';
|
|
8
4
|
import {
|
|
9
5
|
type GetLanguagesOptions,
|
|
10
6
|
type Collection,
|
|
11
7
|
type Language,
|
|
12
8
|
} from '@youversion/platform-core';
|
|
9
|
+
import { useLanguagesClient } from './useLanguageClient';
|
|
13
10
|
|
|
14
11
|
export function useLanguages(
|
|
15
12
|
options: GetLanguagesOptions = {},
|
|
@@ -20,22 +17,7 @@ export function useLanguages(
|
|
|
20
17
|
error: Error | null;
|
|
21
18
|
refetch: () => void;
|
|
22
19
|
} {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const languagesClient = useMemo(() => {
|
|
26
|
-
if (!context?.appKey) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
return new LanguagesClient(
|
|
32
|
-
new ApiClient({
|
|
33
|
-
appKey: context.appKey,
|
|
34
|
-
apiHost: context.apiHost,
|
|
35
|
-
installationId: context.installationId,
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
}, [context?.apiHost, context?.appKey, context?.installationId]);
|
|
20
|
+
const languagesClient = useLanguagesClient();
|
|
39
21
|
|
|
40
22
|
const { data, loading, error, refetch } = useApiData<Collection<Language>>(
|
|
41
23
|
() => languagesClient.getLanguages(options),
|