Skip to content

Commit

Permalink
feat(js sdk): Allow element wrapping & test TG-280 (#435)
Browse files Browse the repository at this point in the history
  • Loading branch information
JanCizmar authored Sep 15, 2021
1 parent a5c402a commit f47c3ed
Show file tree
Hide file tree
Showing 45 changed files with 44,472 additions and 35,800 deletions.
1 change: 0 additions & 1 deletion packages/core/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ module.exports = {
'<rootDir>/src/__testFixtures/*',
'/node_modules/*',
],
modulePathIgnorePatterns: ['cypress'],
moduleNameMapper: {
'@testFixtures/(.*)': '<rootDir>/src/__testFixtures/$1',
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/Constants/Global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export const RESTRICTED_ASCENDANT_ATTRIBUTE = 'data-tolgee-restricted';

export const TOLGEE_ATTRIBUTE_NAME = '_tolgee';
export const TOLGEE_TARGET_ATTRIBUTE = '_tolgee-target';
export const TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE = 'data-tolgee-key-only';
7 changes: 4 additions & 3 deletions packages/core/src/Tolgee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { EventEmitterImpl } from './services/EventEmitter';
import { DependencyStore } from './services/DependencyStore';

export class Tolgee {
export;
default;
Tolgee;
private dependencyStore: DependencyStore;

constructor(config: TolgeeConfig) {
Expand Down Expand Up @@ -46,6 +43,10 @@ export class Tolgee {
return this.dependencyStore.eventService.LANGUAGE_CHANGED;
}

public get onTranslationChange() {
return this.dependencyStore.eventService.TRANSLATION_CHANGED;
}

public get onLangLoaded() {
return this.dependencyStore.eventService.LANGUAGE_LOADED;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/handlers/AbstractHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export abstract class AbstractHandler {
protected translationHighlighter: TranslationHighlighter
) {}

private static initParentElement(element: Element): ElementWithMeta {
protected static initParentElement(element: Element): ElementWithMeta {
if (element[TOLGEE_ATTRIBUTE_NAME] === undefined) {
element[TOLGEE_ATTRIBUTE_NAME] = {
nodes: new Set(),
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/handlers/CoreHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Properties } from '../Properties';
import { AttributeHandler } from './AttributeHandler';
import { ElementWithMeta } from '../types';
import { TextService } from '../services/TextService';
import { WrappedHandler } from './WrappedHandler';

export class CoreHandler {
constructor(
Expand All @@ -14,7 +15,8 @@ export class CoreHandler {
private eventService: EventService,
private properties: Properties,
private attributeHandler: AttributeHandler,
private textService: TextService
private textService: TextService,
private wrappedHandler: WrappedHandler
) {
eventService.LANGUAGE_CHANGED.subscribe(this.refresh.bind(this));
eventService.TRANSLATION_CHANGED.subscribe(this.refresh.bind(this));
Expand All @@ -23,6 +25,7 @@ export class CoreHandler {
public async handleSubtree(target: Element) {
await this.attributeHandler.handle(target);
await this.textHandler.handle(target);
await this.wrappedHandler.handle(target);
}

private async refresh() {
Expand Down
31 changes: 31 additions & 0 deletions packages/core/src/handlers/WrappedHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NodeHelper } from '../helpers/NodeHelper';
import { Properties } from '../Properties';
import { TranslationHighlighter } from '../highlighter/TranslationHighlighter';
import { TextService } from '../services/TextService';
import { AbstractHandler } from './AbstractHandler';
import { ElementRegistrar } from '../services/ElementRegistrar';
import { TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE } from '../Constants/Global';

export class WrappedHandler extends AbstractHandler {
constructor(
protected properties: Properties,
protected translationHighlighter: TranslationHighlighter,
protected textService: TextService,
protected elementRegistrar: ElementRegistrar
) {
super(properties, textService, elementRegistrar, translationHighlighter);
}

async handle(node: Node): Promise<void> {
const xPath = `./descendant-or-self::*[@${TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE}]`;
const nodes = NodeHelper.evaluate(xPath, node);
const filtered: Element[] = this.filterRestricted(nodes as Element[]);
filtered.forEach((element) => {
const elementWithMeta = AbstractHandler.initParentElement(element);
elementWithMeta._tolgee.wrappedWithElementOnlyKey = element.getAttribute(
TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE
);
this.elementRegistrar.register(elementWithMeta);
});
}
}
53 changes: 28 additions & 25 deletions packages/core/src/highlighter/TranslationHighlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { TranslationService } from '../services/TranslationService';
import { MouseEventHandler } from './MouseEventHandler';

export class TranslationHighlighter {
private _renderer: any;

constructor(
private service: CoreService,
private properties: Properties,
Expand All @@ -16,6 +14,31 @@ export class TranslationHighlighter {
private mouseEventHandler: MouseEventHandler
) {}

private _renderer: any;

private get renderer() {
if (this._renderer === undefined) {
if (typeof this.properties.config.ui === 'function') {
this._renderer = new this.properties.config.ui({
coreService: this.service,
properties: this.properties,
eventService: this.eventService,
translationService: this.translationService,
});
}
}
return this._renderer;
}

private static getKeyOptions(node: ElementWithMeta): Set<string> {
const nodes = Array.from(node._tolgee.nodes);
const keys = nodes.reduce(
(acc, curr) => [...acc, ...curr._tolgee.keys.map((k) => k.key)],
[]
);
return new Set(keys);
}

listen(element: ElementWithMeta & ElementCSSInlineStyle) {
this.mouseEventHandler.handle(
element,
Expand All @@ -27,6 +50,9 @@ export class TranslationHighlighter {
mouseEvent: MouseEvent,
element: ElementWithMeta
): Promise<string> {
if (element._tolgee.wrappedWithElementOnlyKey) {
return element._tolgee.wrappedWithElementOnlyKey;
}
const keys = TranslationHighlighter.getKeyOptions(element);
if (keys.size > 1) {
return await this.renderer.getKey({ keys: keys, openEvent: mouseEvent });
Expand All @@ -38,15 +64,6 @@ export class TranslationHighlighter {
console.error('No key to translate. This seems like a bug in tolgee.');
}

private static getKeyOptions(node: ElementWithMeta): Set<string> {
const nodes = Array.from(node._tolgee.nodes);
const keys = nodes.reduce(
(acc, curr) => [...acc, ...curr._tolgee.keys.map((k) => k.key)],
[]
);
return new Set(keys);
}

private translationEdit = async (e: MouseEvent, element: ElementWithMeta) => {
if (typeof this.renderer === 'object') {
const key = await this.getKey(e, element);
Expand All @@ -62,18 +79,4 @@ export class TranslationHighlighter {
'To disable highlighting use production mode.'
);
};

private get renderer() {
if (this._renderer === undefined) {
if (typeof this.properties.config.ui === 'function') {
this._renderer = new this.properties.config.ui({
coreService: this.service,
properties: this.properties,
eventService: this.eventService,
translationService: this.translationService,
});
}
}
return this._renderer;
}
}
10 changes: 9 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Tolgee } from './Tolgee';
import { TolgeeConfig } from './TolgeeConfig';
import { ModifierKey } from './Constants/ModifierKey';
import { TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE } from './Constants/Global';
import { TranslationData } from './DTOs/TranslationData';

export { Tolgee, TolgeeConfig, ModifierKey };
export {
Tolgee,
TolgeeConfig,
ModifierKey,
TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE,
TranslationData,
};
12 changes: 11 additions & 1 deletion packages/core/src/services/DependencyStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { CoreHandler } from '../handlers/CoreHandler';
import { Observer } from '../Observer';
import { CoreService } from './CoreService';
import { TolgeeConfig } from '../TolgeeConfig';
import { WrappedHandler } from '../handlers/WrappedHandler';

export class DependencyStore {
public properties: Properties = new Properties();
Expand Down Expand Up @@ -55,13 +56,22 @@ export class DependencyStore {
this.elementRegistrar,
this.translationHighlighter
);

public wrappedHandler = new WrappedHandler(
this.properties,
this.translationHighlighter,
this.textService,
this.elementRegistrar
);

public coreHandler: CoreHandler = new CoreHandler(
this.coreService,
this.textHandler,
this.eventService,
this.properties,
this.attributeHandler,
this.textService
this.textService,
this.wrappedHandler
);
public observer: Observer = new Observer(
this.properties,
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/services/ElementRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export class ElementRegistrar {

register(element: ElementWithMeta) {
//ignore element with no active nodes
if (this.getActiveNodes(element).next().value === undefined) {
if (
this.getActiveNodes(element).next().value === undefined &&
!element._tolgee.wrappedWithElementOnlyKey
) {
return;
}
if (
Expand All @@ -29,7 +32,10 @@ export class ElementRegistrar {
refreshAll() {
for (const element of this.registeredElements) {
this.cleanElementInactiveNodes(element);
if (element._tolgee.nodes.size === 0) {
if (
element._tolgee.nodes.size === 0 &&
!element._tolgee.wrappedWithElementOnlyKey
) {
this.cleanElement(element);
}
}
Expand Down
54 changes: 29 additions & 25 deletions packages/core/src/services/TextService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@ export class TextService {
private translationService: TranslationService
) {}

private get rawUnWrapRegex(): string {
const escapedPrefix = this.escapeForRegExp(
this.properties.config.inputPrefix
);
const escapedSuffix = this.escapeForRegExp(
this.properties.config.inputSuffix
);
return `(\\\\?)(${escapedPrefix}(.*?)${escapedSuffix})`;
}

private static parseUnwrapped(unWrappedString: string): KeyAndParams {
const strings = unWrappedString.match(/(?:[^\\,:\n]|\\.)+/g);
const result = {
key: TextHelper.removeEscapes(strings.shift()),
params: {},
};

while (strings.length) {
const [name, value] = strings.splice(0, 2);
result.params[name] = value;
}
return result;
}

async translate(
key: string,
params: TranslationParams,
Expand Down Expand Up @@ -102,28 +126,18 @@ export class TextService {
return { translated, key: key, params };
}

private static parseUnwrapped(unWrappedString: string): KeyAndParams {
const strings = unWrappedString.match(/(?:[^\\,:\n]|\\.)+/g);
const result = {
key: TextHelper.removeEscapes(strings.shift()),
params: {},
};

while (strings.length) {
const [name, value] = strings.splice(0, 2);
result.params[name] = value;
}
return result;
}

private readonly format = (
translation: string,
params: TranslationParams
): string => {
try {
return new IntlMessageFormat(
translation,
this.properties.currentLanguage
this.properties.currentLanguage,
undefined,
{
ignoreTag: true,
}
).format(params) as string;
} catch (e) {
if (e.code === 'MISSING_VALUE') {
Expand All @@ -138,16 +152,6 @@ export class TextService {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};

private get rawUnWrapRegex(): string {
const escapedPrefix = this.escapeForRegExp(
this.properties.config.inputPrefix
);
const escapedSuffix = this.escapeForRegExp(
this.properties.config.inputSuffix
);
return `(\\\\?)(${escapedPrefix}(.*?)${escapedSuffix})`;
}

private readonly escapeParam = (param: any) => {
if (typeof param === 'string') {
return param.replace(/[,:\\]/gs, '\\$&');
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type ElementWithMeta = Element &
};

export type ElementMeta = {
wrappedWithElementOnlyKey?: string;
nodes: Set<NodeWithMeta>;
listeningForHighlighting?: boolean;
removeAllEventListeners?: () => void;
Expand Down
Loading

0 comments on commit f47c3ed

Please sign in to comment.