@tolgee/web 4.9.3-rc.d287ae9.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.
Files changed (206) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/dist/tolgee-backend-fetch.cjs.min.js +2 -0
  4. package/dist/tolgee-backend-fetch.cjs.min.js.map +1 -0
  5. package/dist/tolgee-backend-fetch.esm.min.mjs +2 -0
  6. package/dist/tolgee-backend-fetch.esm.min.mjs.map +1 -0
  7. package/dist/tolgee-backend-fetch.umd.min.js +2 -0
  8. package/dist/tolgee-backend-fetch.umd.min.js.map +1 -0
  9. package/dist/tolgee-context-ui.cjs.min.js +143 -0
  10. package/dist/tolgee-context-ui.cjs.min.js.map +1 -0
  11. package/dist/tolgee-context-ui.esm.min.mjs +143 -0
  12. package/dist/tolgee-context-ui.esm.min.mjs.map +1 -0
  13. package/dist/tolgee-context-ui.umd.min.js +143 -0
  14. package/dist/tolgee-context-ui.umd.min.js.map +1 -0
  15. package/dist/tolgee-in-context-tools.cjs.min.js +143 -0
  16. package/dist/tolgee-in-context-tools.cjs.min.js.map +1 -0
  17. package/dist/tolgee-in-context-tools.esm.min.mjs +143 -0
  18. package/dist/tolgee-in-context-tools.esm.min.mjs.map +1 -0
  19. package/dist/tolgee-in-context-tools.umd.min.js +143 -0
  20. package/dist/tolgee-in-context-tools.umd.min.js.map +1 -0
  21. package/dist/tolgee-invisible-observer.cjs.min.js +2 -0
  22. package/dist/tolgee-invisible-observer.cjs.min.js.map +1 -0
  23. package/dist/tolgee-invisible-observer.esm.min.mjs +2 -0
  24. package/dist/tolgee-invisible-observer.esm.min.mjs.map +1 -0
  25. package/dist/tolgee-invisible-observer.umd.min.js +2 -0
  26. package/dist/tolgee-invisible-observer.umd.min.js.map +1 -0
  27. package/dist/tolgee-language-detector.cjs.min.js +2 -0
  28. package/dist/tolgee-language-detector.cjs.min.js.map +1 -0
  29. package/dist/tolgee-language-detector.esm.min.mjs +2 -0
  30. package/dist/tolgee-language-detector.esm.min.mjs.map +1 -0
  31. package/dist/tolgee-language-detector.umd.min.js +2 -0
  32. package/dist/tolgee-language-detector.umd.min.js.map +1 -0
  33. package/dist/tolgee-language-storage.cjs.min.js +2 -0
  34. package/dist/tolgee-language-storage.cjs.min.js.map +1 -0
  35. package/dist/tolgee-language-storage.esm.min.mjs +2 -0
  36. package/dist/tolgee-language-storage.esm.min.mjs.map +1 -0
  37. package/dist/tolgee-language-storage.umd.min.js +2 -0
  38. package/dist/tolgee-language-storage.umd.min.js.map +1 -0
  39. package/dist/tolgee-text-observer.cjs.min.js +2 -0
  40. package/dist/tolgee-text-observer.cjs.min.js.map +1 -0
  41. package/dist/tolgee-text-observer.esm.min.mjs +2 -0
  42. package/dist/tolgee-text-observer.esm.min.mjs.map +1 -0
  43. package/dist/tolgee-text-observer.umd.min.js +2 -0
  44. package/dist/tolgee-text-observer.umd.min.js.map +1 -0
  45. package/dist/tolgee-web-tolgee.cjs.min.js +2 -0
  46. package/dist/tolgee-web-tolgee.cjs.min.js.map +1 -0
  47. package/dist/tolgee-web-tolgee.esm.min.mjs +2 -0
  48. package/dist/tolgee-web-tolgee.esm.min.mjs.map +1 -0
  49. package/dist/tolgee-web-tolgee.umd.min.js +2 -0
  50. package/dist/tolgee-web-tolgee.umd.min.js.map +1 -0
  51. package/dist/tolgee-web.cjs.js +26921 -0
  52. package/dist/tolgee-web.cjs.js.map +1 -0
  53. package/dist/tolgee-web.cjs.min.js +143 -0
  54. package/dist/tolgee-web.cjs.min.js.map +1 -0
  55. package/dist/tolgee-web.esm.min.mjs +143 -0
  56. package/dist/tolgee-web.esm.min.mjs.map +1 -0
  57. package/dist/tolgee-web.esm.mjs +26899 -0
  58. package/dist/tolgee-web.esm.mjs.map +1 -0
  59. package/dist/tolgee-web.umd.js +26927 -0
  60. package/dist/tolgee-web.umd.js.map +1 -0
  61. package/dist/tolgee-web.umd.min.js +143 -0
  62. package/dist/tolgee-web.umd.min.js.map +1 -0
  63. package/lib/BackendFetch.d.ts +4 -0
  64. package/lib/BrowserExtensionPlugin/BrowserExtensionPlugin.d.ts +8 -0
  65. package/lib/BrowserExtensionPlugin/constants.d.ts +3 -0
  66. package/lib/BrowserExtensionPlugin/loadInContextLib.d.ts +1 -0
  67. package/lib/ContextUi.d.ts +2 -0
  68. package/lib/DevBackend.d.ts +2 -0
  69. package/lib/DevTools.d.ts +3 -0
  70. package/lib/InContextTools.d.ts +2 -0
  71. package/lib/InvisibleObserver.d.ts +2 -0
  72. package/lib/LanguageDetector.d.ts +3 -0
  73. package/lib/LanguageStorage.d.ts +3 -0
  74. package/lib/TextObserver.d.ts +2 -0
  75. package/lib/WebTolgee.d.ts +3 -0
  76. package/lib/index.d.ts +4 -0
  77. package/lib/observers/general/DomHelper.d.ts +4 -0
  78. package/lib/observers/general/ElementHighlighter.d.ts +10 -0
  79. package/lib/observers/general/ElementMeta.d.ts +3 -0
  80. package/lib/observers/general/ElementRegistry.d.ts +11 -0
  81. package/lib/observers/general/ElementStore.d.ts +10 -0
  82. package/lib/observers/general/GeneralObserver.d.ts +12 -0
  83. package/lib/observers/general/MouseEventHandler.d.ts +13 -0
  84. package/lib/observers/general/NodeHandler.d.ts +6 -0
  85. package/lib/observers/general/helpers.d.ts +7 -0
  86. package/lib/observers/invisible/InvisibleWrapper.d.ts +2 -0
  87. package/lib/observers/invisible/ValueMemory.d.ts +5 -0
  88. package/lib/observers/invisible/encoderPolyfill.d.ts +8 -0
  89. package/lib/observers/invisible/secret.d.ts +6 -0
  90. package/lib/observers/text/TextWrapper.d.ts +8 -0
  91. package/lib/observers/text/helpers.d.ts +3 -0
  92. package/lib/tools/decodeApiKey.d.ts +1 -0
  93. package/lib/tools/extension.d.ts +28 -0
  94. package/lib/typedIndex.d.ts +11 -0
  95. package/lib/types.d.ts +28 -0
  96. package/lib/ui/KeyContextMenu/KeyContextMenu.d.ts +19 -0
  97. package/lib/ui/KeyDialog/KeyDialog.d.ts +23 -0
  98. package/lib/ui/KeyDialog/KeyForm.d.ts +2 -0
  99. package/lib/ui/KeyDialog/LanguageSelect.d.ts +2 -0
  100. package/lib/ui/KeyDialog/NewWindow.d.ts +2 -0
  101. package/lib/ui/KeyDialog/NsSelect.d.ts +9 -0
  102. package/lib/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.d.ts +6 -0
  103. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.d.ts +8 -0
  104. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.d.ts +6 -0
  105. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.d.ts +2 -0
  106. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.d.ts +8 -0
  107. package/lib/ui/KeyDialog/ScreenshotGallery/utils.d.ts +3 -0
  108. package/lib/ui/KeyDialog/TranslationDialog.d.ts +2 -0
  109. package/lib/ui/KeyDialog/TranslationDialogContextProvider.d.ts +128 -0
  110. package/lib/ui/KeyDialog/TranslationDialogWrapper.d.ts +2 -0
  111. package/lib/ui/KeyDialog/TranslationFields.d.ts +2 -0
  112. package/lib/ui/KeyDialog/languageHelpers.d.ts +12 -0
  113. package/lib/ui/KeyDialog/tools.d.ts +3 -0
  114. package/lib/ui/ThemeProvider.d.ts +2 -0
  115. package/lib/ui/client/QueryProvider.d.ts +12 -0
  116. package/lib/ui/client/apiSchema.generated.d.ts +3283 -0
  117. package/lib/ui/client/client.d.ts +5 -0
  118. package/lib/ui/client/types.d.ts +58 -0
  119. package/lib/ui/client/useQueryApi.d.ts +84 -0
  120. package/lib/ui/common/BodyEnd.d.ts +13 -0
  121. package/lib/ui/common/FieldTitle.d.ts +2 -0
  122. package/lib/ui/common/LoadingButton.d.ts +7 -0
  123. package/lib/ui/constants.d.ts +5 -0
  124. package/lib/ui/index.d.ts +13 -0
  125. package/lib/ui/tools/createProvider.d.ts +5 -0
  126. package/lib/ui/tools/isLanguagePermitted.d.ts +1 -0
  127. package/lib/ui/tools/sleep.d.ts +1 -0
  128. package/package.json +91 -0
  129. package/src/BackendFetch.ts +64 -0
  130. package/src/BrowserExtensionPlugin/BrowserExtensionPlugin.ts +98 -0
  131. package/src/BrowserExtensionPlugin/constants.ts +3 -0
  132. package/src/BrowserExtensionPlugin/loadInContextLib.ts +32 -0
  133. package/src/ContextUi.ts +7 -0
  134. package/src/DevBackend.ts +30 -0
  135. package/src/DevTools.ts +9 -0
  136. package/src/InContextTools.ts +20 -0
  137. package/src/InvisibleObserver.ts +16 -0
  138. package/src/LanguageDetector.test.ts +19 -0
  139. package/src/LanguageDetector.ts +32 -0
  140. package/src/LanguageStorage.ts +23 -0
  141. package/src/TextObserver.ts +45 -0
  142. package/src/WebTolgee.ts +8 -0
  143. package/src/__test__/browser.extension.test.ts +69 -0
  144. package/src/__test__/observer.test.ts +13 -0
  145. package/src/__test__/testObserver.ts +106 -0
  146. package/src/__test__/testRetranslate.ts +47 -0
  147. package/src/index.ts +4 -0
  148. package/src/observers/general/DomHelper.ts +46 -0
  149. package/src/observers/general/ElementHighlighter.ts +72 -0
  150. package/src/observers/general/ElementMeta.ts +17 -0
  151. package/src/observers/general/ElementRegistry.ts +159 -0
  152. package/src/observers/general/ElementStore.ts +34 -0
  153. package/src/observers/general/GeneralObserver.ts +133 -0
  154. package/src/observers/general/MouseEventHandler.ts +198 -0
  155. package/src/observers/general/NodeHandler.ts +39 -0
  156. package/src/observers/general/helpers.ts +65 -0
  157. package/src/observers/invisible/InvisibleWrapper.test.ts +17 -0
  158. package/src/observers/invisible/InvisibleWrapper.ts +96 -0
  159. package/src/observers/invisible/ValueMemory.test.ts +25 -0
  160. package/src/observers/invisible/ValueMemory.ts +20 -0
  161. package/src/observers/invisible/encoderPolyfill.ts +96 -0
  162. package/src/observers/invisible/secret.test.ts +61 -0
  163. package/src/observers/invisible/secret.ts +68 -0
  164. package/src/observers/text/TextWrapper.ts +258 -0
  165. package/src/observers/text/helpers.ts +56 -0
  166. package/src/tools/decodeApiKey.test.ts +14 -0
  167. package/src/tools/decodeApiKey.ts +74 -0
  168. package/src/tools/extension.test.ts +159 -0
  169. package/src/tools/extension.ts +117 -0
  170. package/src/typedIndex.ts +13 -0
  171. package/src/types.ts +33 -0
  172. package/src/ui/KeyContextMenu/KeyContextMenu.tsx +106 -0
  173. package/src/ui/KeyDialog/KeyDialog.tsx +67 -0
  174. package/src/ui/KeyDialog/KeyForm.tsx +208 -0
  175. package/src/ui/KeyDialog/LanguageSelect.tsx +78 -0
  176. package/src/ui/KeyDialog/NewWindow.tsx +106 -0
  177. package/src/ui/KeyDialog/NsSelect.tsx +67 -0
  178. package/src/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.tsx +97 -0
  179. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.tsx +33 -0
  180. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.tsx +138 -0
  181. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.tsx +240 -0
  182. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.tsx +113 -0
  183. package/src/ui/KeyDialog/ScreenshotGallery/utils.ts +17 -0
  184. package/src/ui/KeyDialog/TranslationDialog.tsx +14 -0
  185. package/src/ui/KeyDialog/TranslationDialogContextProvider.tsx +464 -0
  186. package/src/ui/KeyDialog/TranslationDialogWrapper.tsx +44 -0
  187. package/src/ui/KeyDialog/TranslationFields.tsx +113 -0
  188. package/src/ui/KeyDialog/languageHelpers.ts +18 -0
  189. package/src/ui/KeyDialog/tools.ts +30 -0
  190. package/src/ui/ThemeProvider.tsx +71 -0
  191. package/src/ui/__test__/keyContextMenu.test.ts +56 -0
  192. package/src/ui/client/QueryProvider.tsx +38 -0
  193. package/src/ui/client/apiSchema.generated.ts +3281 -0
  194. package/src/ui/client/client.ts +155 -0
  195. package/src/ui/client/types.ts +113 -0
  196. package/src/ui/client/useQueryApi.ts +121 -0
  197. package/src/ui/common/BodyEnd.tsx +44 -0
  198. package/src/ui/common/FieldTitle.tsx +9 -0
  199. package/src/ui/common/LoadingButton.tsx +45 -0
  200. package/src/ui/constants.ts +12 -0
  201. package/src/ui/index.ts +88 -0
  202. package/src/ui/screenshots/ScreenshotPreview.tsx +18 -0
  203. package/src/ui/tools/createProvider.tsx +54 -0
  204. package/src/ui/tools/isLanguagePermitted.ts +14 -0
  205. package/src/ui/tools/sleep.ts +2 -0
  206. package/types/index.d.ts +9 -0
@@ -0,0 +1,45 @@
1
+ import type { ObserverInterface, TolgeePlugin } from '@tolgee/core';
2
+ import { GeneralObserver } from './observers/general/GeneralObserver';
3
+ import { setNodeText } from './observers/general/helpers';
4
+ import { TextWrapper } from './observers/text/TextWrapper';
5
+
6
+ const TextObserverCreator =
7
+ (): ObserverInterface =>
8
+ ({ translate, onClick, options }) => {
9
+ const wrapper = TextWrapper({
10
+ inputPrefix: options.inputPrefix,
11
+ inputSuffix: options.inputSuffix,
12
+ translate,
13
+ });
14
+ const { wrap, unwrap, stop, forEachElement, highlight, run } =
15
+ GeneralObserver(wrapper, options, onClick);
16
+
17
+ const retranslate = () => {
18
+ forEachElement((_, elMeta) => {
19
+ for (const [node, nodeMeta] of elMeta.nodes.entries()) {
20
+ if (nodeMeta.keyAttributeOnly) {
21
+ return;
22
+ }
23
+ const result = wrapper.unwrap(nodeMeta.oldTextContent);
24
+ if (result) {
25
+ setNodeText(node, result.text);
26
+ }
27
+ }
28
+ });
29
+ };
30
+
31
+ return {
32
+ wrap,
33
+ unwrap,
34
+ stop,
35
+ run,
36
+ retranslate,
37
+ highlight,
38
+ outputNotFormattable: true,
39
+ };
40
+ };
41
+
42
+ export const TextObserver = (): TolgeePlugin => (tolgee, tools) => {
43
+ tools.setObserver(TextObserverCreator());
44
+ return tolgee;
45
+ };
@@ -0,0 +1,8 @@
1
+ import { Options, Tolgee as TolgeeCore, TolgeeInstance } from '@tolgee/core';
2
+ import { BrowserExtensionPlugin } from './BrowserExtensionPlugin/BrowserExtensionPlugin';
3
+
4
+ export const Tolgee = (options?: Partial<Options>): TolgeeInstance => {
5
+ return TolgeeCore(options).use(BrowserExtensionPlugin());
6
+ };
7
+
8
+ export { TolgeeCore };
@@ -0,0 +1,69 @@
1
+ const handshakeWithExtension = jest.fn(() => Promise.resolve());
2
+ const loadInContextLib = jest.fn(() => Promise.resolve(() => {}));
3
+
4
+ jest.mock('../tools/plugin', () => ({
5
+ handshakeWithExtension,
6
+ }));
7
+
8
+ import {
9
+ IN_CONTEXT_EXPORT_NAME,
10
+ IN_CONTEXT_FILE,
11
+ IN_CONTEXT_UMD_NAME,
12
+ } from '../BrowserExtensionPlugin/constants';
13
+
14
+ jest.mock('../BrowserExtensionPlugin/loadInContextLib', () => ({
15
+ loadInContextLib,
16
+ }));
17
+
18
+ import { Tolgee } from '@tolgee/core';
19
+ import { BrowserExtensionPlugin } from '../typedIndex';
20
+ import {
21
+ API_KEY_LOCAL_STORAGE,
22
+ API_URL_LOCAL_STORAGE,
23
+ } from '../BrowserExtensionPlugin/BrowserExtensionPlugin';
24
+ import fs from 'fs/promises';
25
+ import path from 'path';
26
+
27
+ describe('compatibility with browser extension', () => {
28
+ afterEach(() => {
29
+ sessionStorage.clear();
30
+ });
31
+
32
+ it('sends correct data to extension', async () => {
33
+ const tolgee = Tolgee({ language: 'en', apiUrl: 'test' });
34
+ tolgee.use(BrowserExtensionPlugin());
35
+ await tolgee.run();
36
+ expect(handshakeWithExtension).toBeCalledTimes(1);
37
+ expect(handshakeWithExtension).toBeCalledWith({
38
+ config: {
39
+ apiKey: '',
40
+ apiUrl: 'test',
41
+ },
42
+ mode: 'production',
43
+ uiPresent: true,
44
+ uiVersion: undefined,
45
+ });
46
+ });
47
+
48
+ it('loads in-context lib if session storage is set', async () => {
49
+ sessionStorage.setItem(API_KEY_LOCAL_STORAGE, 'test');
50
+ sessionStorage.setItem(API_URL_LOCAL_STORAGE, 'test');
51
+
52
+ const tolgee = Tolgee({ language: 'en' });
53
+ tolgee.use(BrowserExtensionPlugin());
54
+ await tolgee.run();
55
+
56
+ expect(loadInContextLib).toBeCalledTimes(1);
57
+ });
58
+
59
+ it('builded module is valid', async () => {
60
+ // this test works only after build
61
+ const fileContent = await fs.readFile(
62
+ path.join(__dirname, `../../dist/${IN_CONTEXT_FILE}`)
63
+ );
64
+ expect(fileContent.toString().includes(IN_CONTEXT_UMD_NAME)).toBeTruthy();
65
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
66
+ const module = await import(`../../dist/${IN_CONTEXT_FILE}`);
67
+ expect(typeof module[IN_CONTEXT_EXPORT_NAME]).toEqual('function');
68
+ });
69
+ });
@@ -0,0 +1,13 @@
1
+ import { InvisibleObserver } from '../InvisibleObserver';
2
+ import { TextObserver } from '../TextObserver';
3
+ import { testObserver } from './testObserver';
4
+ import { testRetranslate } from './testRetranslate';
5
+
6
+ describe('invisble observer', () => {
7
+ testObserver(InvisibleObserver());
8
+ });
9
+
10
+ describe('text observer', () => {
11
+ testObserver(TextObserver());
12
+ testRetranslate(TextObserver());
13
+ });
@@ -0,0 +1,106 @@
1
+ import {
2
+ Tolgee,
3
+ TolgeeInstance,
4
+ TolgeePlugin,
5
+ TOLGEE_ATTRIBUTE_NAME,
6
+ TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE,
7
+ } from '@tolgee/core';
8
+ import { screen, waitFor } from '@testing-library/dom';
9
+
10
+ export const testObserver = (plugin: TolgeePlugin) => {
11
+ describe('observer', () => {
12
+ let tolgee: TolgeeInstance;
13
+
14
+ beforeEach(() => {
15
+ tolgee = Tolgee()
16
+ .use(plugin)
17
+ .init({
18
+ language: 'en',
19
+ staticData: { en: { hello: 'world' } },
20
+ });
21
+ tolgee.run();
22
+ });
23
+
24
+ afterEach(() => {
25
+ tolgee.stop();
26
+ });
27
+
28
+ it('runs observer', async () => {
29
+ document.body.innerHTML = `
30
+ <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
31
+ `;
32
+
33
+ await waitFor(() => {
34
+ expect(screen.queryByTestId('translation')?.textContent).toEqual(
35
+ 'world'
36
+ );
37
+ });
38
+ });
39
+
40
+ it('runs observer', async () => {
41
+ document.body.innerHTML = `
42
+ <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
43
+ `;
44
+
45
+ await waitFor(() => {
46
+ expect(screen.queryByTestId('translation')?.textContent).toEqual(
47
+ 'world'
48
+ );
49
+ });
50
+ });
51
+
52
+ it('ignoring works', async () => {
53
+ document.body.innerHTML = `
54
+ <span data-testid="ignoredTranslation" data-tolgee-restricted="true">
55
+ ${tolgee.t({ key: 'hello' })}
56
+ <span data-testid="ignoredInside">${tolgee.t({ key: 'hello' })}</span>
57
+ </span>
58
+ <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
59
+ `;
60
+
61
+ await waitFor(() => {
62
+ expect(
63
+ screen
64
+ .queryByTestId('translation')
65
+ ?.getAttribute(TOLGEE_ATTRIBUTE_NAME)
66
+ ).not.toBeFalsy();
67
+ expect(
68
+ screen
69
+ .queryByTestId('ignoredTranslation')
70
+ ?.getAttribute(TOLGEE_ATTRIBUTE_NAME)
71
+ ).toBeFalsy();
72
+ expect(
73
+ screen
74
+ .queryByTestId('ignoredInside')
75
+ ?.getAttribute(TOLGEE_ATTRIBUTE_NAME)
76
+ ).toBeFalsy();
77
+ });
78
+ });
79
+
80
+ it('key only attribute', async () => {
81
+ document.body.innerHTML = `
82
+ <span data-testid="translation" ${TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE}="hello">Hello</span>
83
+ `;
84
+
85
+ await waitFor(() => {
86
+ expect(
87
+ screen
88
+ .queryByTestId('translation')
89
+ ?.getAttribute(TOLGEE_ATTRIBUTE_NAME)
90
+ ).not.toBeFalsy();
91
+ });
92
+
93
+ screen
94
+ .queryByTestId('translation')!
95
+ .removeAttribute(TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE);
96
+
97
+ await waitFor(() => {
98
+ expect(
99
+ screen
100
+ .queryByTestId('translation')
101
+ ?.getAttribute(TOLGEE_ATTRIBUTE_NAME)
102
+ ).toBeFalsy();
103
+ });
104
+ });
105
+ });
106
+ };
@@ -0,0 +1,47 @@
1
+ import { screen, waitFor } from '@testing-library/dom';
2
+ import {
3
+ Tolgee,
4
+ TolgeeInstance,
5
+ TolgeePlugin,
6
+ TOLGEE_ATTRIBUTE_NAME,
7
+ } from '@tolgee/core';
8
+
9
+ export const testRetranslate = (plugin: TolgeePlugin) => {
10
+ describe('retranslate', () => {
11
+ let tolgee: TolgeeInstance;
12
+
13
+ beforeEach(async () => {
14
+ tolgee = Tolgee()
15
+ .use(plugin)
16
+ .init({
17
+ language: 'en',
18
+ staticData: { en: { hello: 'world' }, es: { hello: 'mundo' } },
19
+ });
20
+ await tolgee.run();
21
+ });
22
+
23
+ afterEach(() => {
24
+ tolgee.stop();
25
+ });
26
+
27
+ it('change translation on language change', async () => {
28
+ document.body.innerHTML = `
29
+ <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
30
+ `;
31
+
32
+ await waitFor(() => {
33
+ const element = screen.queryByTestId('translation');
34
+ expect(element?.textContent).toEqual('world');
35
+ expect(element?.getAttribute(TOLGEE_ATTRIBUTE_NAME)).not.toBeFalsy();
36
+ });
37
+
38
+ await tolgee.changeLanguage('es');
39
+
40
+ await waitFor(() => {
41
+ expect(screen.queryByTestId('translation')?.textContent).toEqual(
42
+ 'mundo'
43
+ );
44
+ });
45
+ });
46
+ });
47
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './ContextUi';
2
+ export * from './DevTools';
3
+ export * from './InContextTools';
4
+ export * from './typedIndex';
@@ -0,0 +1,46 @@
1
+ import { ObserverOptions } from '@tolgee/core';
2
+
3
+ export const DomHelper = (options: ObserverOptions) => {
4
+ function getParentElement(node: Node): Element | undefined {
5
+ if (node.parentElement) {
6
+ return node.parentElement;
7
+ }
8
+ if ((node as Attr).ownerElement) {
9
+ return (node as Attr).ownerElement || undefined;
10
+ }
11
+ }
12
+
13
+ function getSuitableParent(node: Node): Element {
14
+ const domParent = getParentElement(node);
15
+
16
+ if (domParent === undefined) {
17
+ // eslint-disable-next-line no-console
18
+ console.error(node);
19
+ throw new Error('No suitable parent found for node above.');
20
+ }
21
+
22
+ if (!options.passToParent) {
23
+ return domParent;
24
+ }
25
+
26
+ if (Array.isArray(options.passToParent)) {
27
+ const tagNameEquals = (elementTagName: string) =>
28
+ domParent.tagName.toLowerCase() === elementTagName.toLowerCase();
29
+ if (options.passToParent.findIndex(tagNameEquals) === -1) {
30
+ return domParent;
31
+ }
32
+ }
33
+
34
+ if (typeof options.passToParent === 'function') {
35
+ if (!options.passToParent(domParent)) {
36
+ return domParent;
37
+ }
38
+ }
39
+
40
+ return getSuitableParent(domParent);
41
+ }
42
+
43
+ return Object.freeze({
44
+ getSuitableParent,
45
+ });
46
+ };
@@ -0,0 +1,72 @@
1
+ import { ElementMeta, TOLGEE_HIGHLIGHTER_CLASS } from '@tolgee/core';
2
+ import { TolgeeElement } from '../../types';
3
+
4
+ const HIGHLIGHTER_BASE_STYLE: Partial<CSSStyleDeclaration> = {
5
+ pointerEvents: 'none',
6
+ position: 'fixed',
7
+ boxSizing: 'content-box',
8
+ zIndex: String(Number.MAX_SAFE_INTEGER),
9
+ contain: 'layout',
10
+ display: 'block',
11
+ borderStyle: 'solid',
12
+ borderRadius: '4px',
13
+ };
14
+
15
+ type Props = {
16
+ highlightColor: string;
17
+ highlightWidth: number;
18
+ };
19
+
20
+ export const ElementHighlighter = ({
21
+ highlightColor,
22
+ highlightWidth,
23
+ }: Props) => {
24
+ function initHighlightFunction(
25
+ element: TolgeeElement,
26
+ elementMeta: ElementMeta
27
+ ) {
28
+ elementMeta.highlight = () => {
29
+ if (!element.isConnected) {
30
+ return;
31
+ }
32
+ let highlightEl = elementMeta.highlightEl;
33
+ if (!highlightEl) {
34
+ highlightEl = document.createElement('div');
35
+ highlightEl.classList.add(TOLGEE_HIGHLIGHTER_CLASS);
36
+ Object.entries(HIGHLIGHTER_BASE_STYLE).forEach(([key, value]) => {
37
+ // @ts-ignore
38
+ highlightEl!.style[key] = value;
39
+ });
40
+ highlightEl.style.borderColor = highlightColor;
41
+
42
+ elementMeta.highlightEl = highlightEl;
43
+ document.body.appendChild(highlightEl);
44
+ }
45
+
46
+ const shape = element.getBoundingClientRect();
47
+
48
+ highlightEl.style.borderWidth = highlightWidth + 'px';
49
+ highlightEl.style.top = shape.top - highlightWidth + 'px';
50
+ highlightEl.style.left = shape.left - highlightWidth + 'px';
51
+ highlightEl.style.width = shape.width + 'px';
52
+ highlightEl.style.height = shape.height + 'px';
53
+ };
54
+ }
55
+
56
+ function initUnhighlightFunction(
57
+ element: TolgeeElement,
58
+ elementMeta: ElementMeta
59
+ ) {
60
+ elementMeta.unhighlight = () => {
61
+ elementMeta.highlightEl?.remove();
62
+ elementMeta.highlightEl = undefined;
63
+ };
64
+ }
65
+
66
+ function initHighlighter(element: TolgeeElement, elementMeta: ElementMeta) {
67
+ initHighlightFunction(element, elementMeta);
68
+ initUnhighlightFunction(element, elementMeta);
69
+ }
70
+
71
+ return Object.freeze({ initHighlighter });
72
+ };
@@ -0,0 +1,17 @@
1
+ import type { ElementMeta, KeyAndParams, NodeMeta } from '@tolgee/core';
2
+
3
+ export function initElementMeta(): ElementMeta {
4
+ return {
5
+ nodes: new Map(),
6
+ };
7
+ }
8
+
9
+ export function initNodeMeta(
10
+ oldTextContent: string,
11
+ keys: KeyAndParams[]
12
+ ): NodeMeta {
13
+ return {
14
+ oldTextContent,
15
+ keys,
16
+ };
17
+ }
@@ -0,0 +1,159 @@
1
+ import {
2
+ FallbackNSTranslation,
3
+ getFallback,
4
+ RESTRICTED_ASCENDANT_ATTRIBUTE,
5
+ TOLGEE_ATTRIBUTE_NAME,
6
+ } from '@tolgee/core';
7
+ import {
8
+ ElementMeta,
9
+ KeyWithDefault,
10
+ NodeMeta,
11
+ TranslationOnClick,
12
+ ObserverOptions,
13
+ } from '@tolgee/core';
14
+ import { TolgeeElement } from '../../types';
15
+
16
+ import { ElementHighlighter } from './ElementHighlighter';
17
+ import { initElementMeta } from './ElementMeta';
18
+ import { ElementStore } from './ElementStore';
19
+ import { compareDescriptors, nodeContains } from './helpers';
20
+ import { MouseEventHandler } from './MouseEventHandler';
21
+
22
+ export const ElementRegistry = (
23
+ options: ObserverOptions,
24
+ onClick: TranslationOnClick
25
+ ) => {
26
+ const elementStore = ElementStore();
27
+ const elementHighlighter = ElementHighlighter({
28
+ highlightColor: options.highlightColor,
29
+ highlightWidth: options.highlightWidth,
30
+ });
31
+ const eventHandler = MouseEventHandler({
32
+ highlightKeys: options.highlightKeys,
33
+ elementStore,
34
+ onClick: (event, el) => {
35
+ const meta = elementStore.get(el)!;
36
+ onClick(event, {
37
+ el,
38
+ meta,
39
+ keysAndDefaults: getKeysAndDefaults(meta),
40
+ });
41
+ },
42
+ });
43
+
44
+ function register(element: Element, node: Node, nodeMeta: NodeMeta) {
45
+ if (isRestricted(element)) {
46
+ return;
47
+ }
48
+ const tolgeeElement = element as TolgeeElement;
49
+ let elementMeta = elementStore.get(tolgeeElement);
50
+ if (!elementMeta) {
51
+ elementMeta = initElementMeta();
52
+ elementStore.set(tolgeeElement, elementMeta);
53
+ tolgeeElement.setAttribute(TOLGEE_ATTRIBUTE_NAME, 'true');
54
+ }
55
+ elementMeta.nodes.set(node, nodeMeta);
56
+ elementHighlighter.initHighlighter(tolgeeElement, elementMeta);
57
+ }
58
+
59
+ function run(mouseHighlight: boolean) {
60
+ if (mouseHighlight) {
61
+ eventHandler.run();
62
+ }
63
+ }
64
+
65
+ function stop() {
66
+ eventHandler.stop();
67
+ }
68
+
69
+ function isRestricted(element: Element) {
70
+ const restrictedElements = options.restrictedElements;
71
+ return (
72
+ restrictedElements.indexOf(element.tagName.toLowerCase()) !== -1 ||
73
+ element.closest(`[${RESTRICTED_ASCENDANT_ATTRIBUTE}]`) !== null
74
+ );
75
+ }
76
+
77
+ function refreshAll() {
78
+ elementStore.forEachElement((element, meta) => {
79
+ if (meta.preventClean) {
80
+ return;
81
+ }
82
+ cleanElementInactiveNodes(meta);
83
+ if (meta.nodes.size === 0) {
84
+ cleanElement(element, meta);
85
+ }
86
+ });
87
+ }
88
+
89
+ function findAll(key?: string, ns?: FallbackNSTranslation) {
90
+ const result: ElementMeta[] = [];
91
+ elementStore.forEachElement((_, meta) => {
92
+ for (const nodeMeta of meta.nodes.values()) {
93
+ const fits = nodeMeta.keys.find((val) =>
94
+ compareDescriptors(
95
+ { key, ns: getFallback(ns) },
96
+ { key: val.key, ns: getFallback(val.ns) }
97
+ )
98
+ );
99
+ if (fits) {
100
+ result.push(meta);
101
+ break;
102
+ }
103
+ }
104
+ });
105
+ return result;
106
+ }
107
+
108
+ function cleanElementInactiveNodes(meta: ElementMeta) {
109
+ meta.nodes = new Map(getActiveNodes(meta));
110
+ }
111
+
112
+ function getTargetElement() {
113
+ return options.targetElement || document.body;
114
+ }
115
+
116
+ function* getActiveNodes(meta: ElementMeta) {
117
+ for (const [node, nodeMeta] of meta.nodes.entries()) {
118
+ if (nodeContains(getTargetElement(), node)) {
119
+ yield [node, nodeMeta] as const;
120
+ }
121
+ }
122
+ }
123
+
124
+ function cleanElement(element: TolgeeElement, meta: ElementMeta) {
125
+ if (meta.highlightEl) {
126
+ meta.unhighlight?.();
127
+ }
128
+ element.removeAttribute(TOLGEE_ATTRIBUTE_NAME);
129
+ elementStore.remove(element);
130
+ }
131
+
132
+ function getKeyOptions(meta: ElementMeta): KeyWithDefault[] {
133
+ const nodes = Array.from(meta.nodes.values());
134
+ return nodes.reduce(
135
+ (acc, curr) => [
136
+ ...acc,
137
+ ...curr.keys.map((k) => ({
138
+ key: k.key,
139
+ defaultValue: k.defaultValue,
140
+ ns: k.ns,
141
+ })),
142
+ ],
143
+ [] as KeyWithDefault[]
144
+ );
145
+ }
146
+
147
+ function getKeysAndDefaults(meta: ElementMeta): KeyWithDefault[] {
148
+ return getKeyOptions(meta);
149
+ }
150
+
151
+ return Object.freeze({
152
+ register,
153
+ forEachElement: elementStore.forEachElement,
154
+ findAll,
155
+ refreshAll,
156
+ run,
157
+ stop,
158
+ });
159
+ };
@@ -0,0 +1,34 @@
1
+ import type { ElementMeta } from '@tolgee/core';
2
+ import { TolgeeElement } from '../../types';
3
+
4
+ export type RegistredElementsMap = Map<TolgeeElement, ElementMeta>;
5
+
6
+ export const ElementStore = () => {
7
+ const registredElements: RegistredElementsMap = new Map();
8
+
9
+ function set(el: TolgeeElement, meta: ElementMeta) {
10
+ registredElements.set(el, meta);
11
+ }
12
+
13
+ function get(el: TolgeeElement | undefined) {
14
+ return el && registredElements.get(el);
15
+ }
16
+
17
+ function remove(el: TolgeeElement) {
18
+ return registredElements.delete(el);
19
+ }
20
+
21
+ function forEachElement(
22
+ callback: (el: TolgeeElement, meta: ElementMeta) => void
23
+ ) {
24
+ registredElements.forEach((value, key) => callback(key, value));
25
+ }
26
+ return Object.freeze({
27
+ set,
28
+ get,
29
+ remove,
30
+ forEachElement,
31
+ });
32
+ };
33
+
34
+ export type ElementStoreType = ReturnType<typeof ElementStore>;