diff --git a/src/helpers/helpers.config.ts b/src/helpers/helpers.config.ts index db0b125e9e3..926c96b66d7 100644 --- a/src/helpers/helpers.config.ts +++ b/src/helpers/helpers.config.ts @@ -46,6 +46,7 @@ export function _createResolver< _fallback: fallback, _getTarget: getTarget, override: (scope: AnyObject) => _createResolver([scope, ...scopes], prefixes, finalRootScopes, fallback), + overridePrefixes: (pref: string[]) => _createResolver(scopes, pref, finalRootScopes, fallback) }; return new Proxy(cache, { /** @@ -107,6 +108,16 @@ export function _createResolver< }) as ResolverProxy; } +function applyPrefixes< + T extends AnyObject[] = AnyObject[], + R extends AnyObject[] = T +>( + proxy: ResolverProxy, + prefixes: string[] +) : ResolverProxy { + return (prefixes && prefixes.length ? proxy.overridePrefixes(prefixes) : proxy) as ResolverProxy; +} + /** * Returns an Proxy for resolving option values with context. * @param proxy - The Proxy returned by `_createResolver` @@ -131,8 +142,8 @@ export function _attachContext< _subProxy: subProxy, _stack: new Set(), _descriptors: _descriptors(proxy, descriptorDefaults), - setContext: (ctx: AnyObject) => _attachContext(proxy, ctx, subProxy, descriptorDefaults), - override: (scope: AnyObject) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults) + setContext: (ctx: AnyObject, prefixes: string[]) => _attachContext(applyPrefixes(proxy, prefixes), ctx, subProxy, descriptorDefaults), + override: (scope: AnyObject, prefixes: string[]) => _attachContext(applyPrefixes(proxy.override(scope), prefixes), context, subProxy, descriptorDefaults) }; return new Proxy(cache, { /** @@ -336,7 +347,8 @@ function createSubResolver( parentScopes: AnyObject[], resolver: ResolverCache, prop: ResolverObjectKey, - value: unknown + value: unknown, + prefixes: string[] = [''] ) { const rootScopes = resolver._rootScopes; const fallback = resolveFallback(resolver._fallback, prop, value); @@ -353,7 +365,7 @@ function createSubResolver( return false; } } - return _createResolver(Array.from(set), [''], rootScopes, fallback, + return _createResolver(Array.from(set), prefixes, rootScopes, fallback, () => subGetTarget(resolver, prop as string, value)); } @@ -398,7 +410,7 @@ function _resolveWithPrefixes( value = _resolve(readKey(prefix, prop), scopes); if (typeof value !== 'undefined') { return needsSubResolver(prop, value) - ? createSubResolver(scopes, proxy, prop, value) + ? createSubResolver(scopes, proxy, prop, value, prefixes) : value; } } diff --git a/src/helpers/helpers.config.types.ts b/src/helpers/helpers.config.types.ts index 64f0e329b54..966b90a8221 100644 --- a/src/helpers/helpers.config.types.ts +++ b/src/helpers/helpers.config.types.ts @@ -19,6 +19,7 @@ export interface ResolverCache< _storage?: T[number]; _getTarget(): T[number]; override(scope: S): ResolverProxy<(T[number] | S)[], T | R> + overridePrefixes(pref: string[]): ResolverProxy<(T[number] | S)[], T | R> } export type ResolverProxy< @@ -50,8 +51,8 @@ export interface ContextCache< _subProxy: ResolverProxy; _stack: Set; _descriptors: Descriptor - setContext(ctx: AnyObject): ContextProxy - override(scope: S): ContextProxy<(T[number] | S)[], T | R> + setContext(ctx: AnyObject, prefixes: string[]): ContextProxy + override(scope: S, prefixes: string[]): ContextProxy<(T[number] | S)[], T | R> } export type ContextProxy< diff --git a/test/specs/helpers.config.tests.js b/test/specs/helpers.config.tests.js index b4d08b9602a..bac621938c6 100644 --- a/test/specs/helpers.config.tests.js +++ b/test/specs/helpers.config.tests.js @@ -719,6 +719,20 @@ describe('Chart.helpers.config', function() { expect(opts.fn).toEqual(1); }); + it('should support changing prefixes', function() { + const res = _createResolver([{test: 1, hoverTest: 2}]); + const opts = _attachContext(res, {ctx: 1}); + expect(opts.test).toEqual(1); + expect(opts.setContext({ctx: 2}, ['hover', '']).test).toEqual(2); + expect(opts.test).toEqual(1); + expect(opts.override('', ['hover', '']).test).toEqual(2); + }); + + it('should support prefixes to subnodes', function() { + const res = _createResolver([{node: {test: 1, hoverTest: 2}}], ['hover', '']); + expect(res.node.test).toEqual(2); + }); + it('should support common object methods', function() { const defaults = { option1: 'defaults'