From 6f6cf1f4217500ec520c154e33278b7b78ae6a1f Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Wed, 29 Jan 2020 13:30:45 +0000 Subject: [PATCH] Return the injected theme as in the properties --- src/core/middleware/theme.ts | 16 +- tests/core/unit/middleware/theme.ts | 258 --------------------------- tests/core/unit/middleware/theme.tsx | 160 +++++++++++++++++ 3 files changed, 169 insertions(+), 265 deletions(-) delete mode 100644 tests/core/unit/middleware/theme.ts create mode 100644 tests/core/unit/middleware/theme.tsx diff --git a/src/core/middleware/theme.ts b/src/core/middleware/theme.ts index 79ed5b3f1..42b9b89c0 100644 --- a/src/core/middleware/theme.ts +++ b/src/core/middleware/theme.ts @@ -4,7 +4,7 @@ import icache from './icache'; import injector from './injector'; import Injector from '../Injector'; import Set from '../../shim/Set'; -import { shallow } from '../diff'; +import { shallow, auto } from '../diff'; import Registry from '../Registry'; export { Theme, Classes, ClassNames } from './../interfaces'; @@ -32,11 +32,17 @@ const factory = create({ invalidator, icache, diffProperty, injector, getRegistr export const theme = factory( ({ middleware: { invalidator, icache, diffProperty, injector, getRegistry }, properties }) => { let themeKeys = new Set(); - diffProperty('theme', (current: ThemeProperties, next: ThemeProperties) => { - if (current.theme !== next.theme) { + + diffProperty('theme', properties, (current, next) => { + const themeInjector = injector.get>(INJECTED_THEME_KEY); + const diffResult = auto(current.theme, next.theme); + if (diffResult.changed) { icache.clear(); invalidator(); } + if (!next.theme && themeInjector) { + return themeInjector.get(); + } }); diffProperty('classes', (current: ThemeProperties, next: ThemeProperties) => { let result = false; @@ -79,10 +85,6 @@ export const theme = factory( themeKeys.add(key); theme = classes as T; let { theme: currentTheme, classes: currentClasses } = properties(); - if (!currentTheme) { - const injectedTheme = injector.get>(INJECTED_THEME_KEY); - currentTheme = injectedTheme ? injectedTheme.get() : undefined; - } if (currentTheme && currentTheme[key]) { theme = { ...theme, ...currentTheme[key] }; } diff --git a/tests/core/unit/middleware/theme.ts b/tests/core/unit/middleware/theme.ts deleted file mode 100644 index 3d7908819..000000000 --- a/tests/core/unit/middleware/theme.ts +++ /dev/null @@ -1,258 +0,0 @@ -const { it, describe, afterEach } = intern.getInterface('bdd'); -const { assert } = intern.getPlugin('chai'); -import { sandbox } from 'sinon'; - -import themeMiddleware from '../../../../src/core/middleware/theme'; -import icacheMiddleware from '../../../../src/core/middleware/icache'; -import Injector from '../../../../src/core/Injector'; - -const sb = sandbox.create(); -const invalidator = sb.stub(); -const diffProperty = sb.stub(); -const injector = { - subscribe: sb.stub(), - get: sb.stub() -}; -const defineInjector = sb.stub(); -const getRegistry = sb.stub(); -const registryHandler = { - base: { - defineInjector - } -}; -getRegistry.returns(registryHandler); - -describe('theme middleware', () => { - afterEach(() => { - sb.resetHistory(); - }); - - it('Should register injector and allow theme to be set', () => { - const icache = icacheMiddleware().callback({ - middleware: { destroy: sb.stub(), invalidator: sb.stub() }, - properties: () => ({}), - children: () => [], - id: 'blah' - }); - const { callback } = themeMiddleware(); - defineInjector.callsFake((...args: any[]) => { - injector.get.withArgs(args[0]).returns(new Injector('blah')); - }); - const theme = callback({ - id: 'test', - middleware: { - invalidator, - icache, - diffProperty, - injector, - getRegistry - }, - properties: () => ({}), - children: () => [] - }); - - assert.isTrue(diffProperty.calledTwice); - assert.isTrue(injector.subscribe.calledOnce); - const testTheme = { - foo: { - bar: 'themed-root' - } - }; - theme.set(testTheme); - const css = { - ' _key': 'foo', - bar: 'root' - }; - const result = theme.classes(css); - assert.deepEqual(result, { bar: 'themed-root' }); - const resultTwo = theme.classes(css); - assert.strictEqual(resultTwo, result); - assert.strictEqual(theme.get(), testTheme); - theme.set({ - foo: { - bar: 'other-themed-root' - } - }); - injector.subscribe.getCall(0).callArg(1); - assert.isTrue(invalidator.calledOnce); - const resultThree = theme.classes(css); - assert.notStrictEqual(resultThree, resultTwo); - assert.deepEqual(resultThree, { bar: 'other-themed-root' }); - }); - - it('Should give precedence to theme from properties over an injected theme', () => { - const icache = icacheMiddleware().callback({ - middleware: { destroy: sb.stub(), invalidator: sb.stub() }, - properties: () => ({}), - children: () => [], - id: 'blah' - }); - const { callback } = themeMiddleware(); - defineInjector.callsFake((...args: any[]) => { - injector.get.withArgs(args[0]).returns(new Injector('blah')); - }); - const propertyTheme = { - foo: { - bar: 'other-themed-root' - } - }; - const theme = callback({ - id: 'test', - middleware: { - invalidator, - icache, - diffProperty, - injector, - getRegistry - }, - properties: () => ({ - theme: propertyTheme - }), - children: () => [] - }); - - assert.isTrue(diffProperty.calledTwice); - assert.isTrue(injector.subscribe.calledOnce); - assert.isTrue(invalidator.notCalled); - - theme.set({ - foo: { - bar: 'themed-root' - } - }); - const css = { - ' _key': 'foo', - bar: 'root' - }; - const result = theme.classes(css); - assert.deepEqual(result, { bar: 'other-themed-root' }); - const currentTheme = { ...propertyTheme }; - propertyTheme.foo.bar = 'updated-themed-root'; - diffProperty.getCall(0).callArgWith(1, { theme: currentTheme }, { theme: propertyTheme }); - assert.isTrue(invalidator.calledOnce); - const resultTwo = theme.classes(css); - assert.deepEqual(resultTwo, { bar: 'updated-themed-root' }); - diffProperty.getCall(0).callArgWith(1, { theme: propertyTheme }, { theme: propertyTheme }); - assert.isTrue(invalidator.calledOnce); - const resultThree = theme.classes(css); - assert.deepEqual(resultThree, { bar: 'updated-themed-root' }); - }); - - it('Should support classes property for adding additional classes', () => { - const icache = icacheMiddleware().callback({ - middleware: { destroy: sb.stub(), invalidator: sb.stub() }, - properties: () => ({}), - children: () => [], - id: 'blah' - }); - const { callback } = themeMiddleware(); - defineInjector.callsFake((...args: any[]) => { - injector.get.withArgs(args[0]).returns(new Injector('blah')); - }); - const theme = callback({ - id: 'test', - middleware: { - invalidator, - icache, - diffProperty, - injector, - getRegistry - }, - properties: () => ({ - classes: { - other: { - bar: ['extra-extra-extra'] - }, - foo: { - bar: ['extra-extra'], - other: ['extra-extra-extra'] - } - } - }), - children: () => [] - }); - - assert.isTrue(diffProperty.calledTwice); - assert.isTrue(injector.subscribe.calledOnce); - assert.isTrue(invalidator.notCalled); - - theme.set({ - foo: { - bar: 'themed-root' - } - }); - const other = { - ' _key': 'other', - bar: 'root' - }; - const resultOne = theme.classes(other); - assert.deepEqual(resultOne, { bar: 'root extra-extra-extra' }); - const css = { - ' _key': 'foo', - bar: 'root' - }; - const resultTwo = theme.classes(css); - assert.deepEqual(resultTwo, { bar: 'themed-root extra-extra' }); - assert.isTrue(invalidator.notCalled); - diffProperty.getCall(1).callArgWith( - 1, - { - classes: { - other: { - bar: ['extra-extra-extra'] - }, - foo: { - bar: ['extra-extra'], - other: ['extra-extra-extra'] - } - } - }, - { - classes: { - other: { - bar: ['extra-extra-extra'] - }, - foo: { - bar: ['extra-extra'], - other: ['extra-extra'] - } - } - } - ); - assert.isTrue(invalidator.calledOnce); - diffProperty.getCall(1).callArgWith( - 1, - { - classes: { - other: { - bar: ['extra-extra-extra'] - }, - foo: { - bar: ['extra-extra'], - other: ['extra-extra-extra'] - } - } - }, - {} - ); - assert.isTrue(invalidator.calledTwice); - diffProperty.getCall(1).callArgWith( - 1, - {}, - { - classes: { - other: { - bar: ['extra-extra-extra'] - }, - foo: { - bar: ['extra-extra'], - other: ['extra-extra'] - } - } - } - ); - assert.isTrue(invalidator.calledThrice); - diffProperty.getCall(1).callArgWith(1, {}, {}); - assert.isTrue(invalidator.calledThrice); - }); -}); diff --git a/tests/core/unit/middleware/theme.tsx b/tests/core/unit/middleware/theme.tsx new file mode 100644 index 000000000..31051f494 --- /dev/null +++ b/tests/core/unit/middleware/theme.tsx @@ -0,0 +1,160 @@ +const { it, afterEach, beforeEach } = intern.getInterface('bdd'); +const { describe: jsdomDescribe } = intern.getPlugin('jsdom'); +const { assert } = intern.getPlugin('chai'); + +import { renderer, tsx, create, invalidator } from '../../../../src/core/vdom'; +import theme from '../../../../src/core/middleware/theme'; +import { createResolvers } from './../../support/util'; + +const resolvers = createResolvers(); + +jsdomDescribe('theme middleware', () => { + beforeEach(() => { + resolvers.stub(); + }); + afterEach(() => { + resolvers.restore(); + }); + + it('Should register injector and allow theme to be set', () => { + const factory = create({ theme }); + const css = { + ' _key': 'test-key', + root: 'root' + }; + const widgetTheme = { + 'test-key': { + root: 'themed-root' + } + }; + const App = factory(function App({ middleware: { theme } }) { + const themedCss = theme.classes(css); + return ( +
+
+
+ ); + }); + const root = document.createElement('div'); + const r = renderer(() => ); + r.mount({ domNode: root }); + assert.strictEqual( + root.outerHTML, + '
' + ); + (root.children[0].children[1] as HTMLButtonElement).click(); + resolvers.resolve(); + assert.strictEqual( + root.outerHTML, + '
{"test-key":{"root":"themed-root"}}
' + ); + }); + + it('Should use theme property over injected theme', () => { + const factory = create({ theme, invalidator }); + const css = { + ' _key': 'test-key', + root: 'root', + other: 'other' + }; + const overrideWidgetTheme = { + 'test-key': { + root: 'override-themed-root' + } + }; + let currentTheme: any; + const ThemedWidget = factory(function App({ middleware: { theme } }) { + const themedCss = theme.classes(css); + return
; + }); + const App = factory(function App({ middleware: { theme, invalidator } }) { + const themedCss = theme.classes(css); + return ( +
+ +
+ ); + }); + const root = document.createElement('div'); + const r = renderer(() => ); + r.mount({ domNode: root }); + assert.strictEqual( + root.outerHTML, + '
' + ); + currentTheme = overrideWidgetTheme; + (root.children[0].children[1] as HTMLButtonElement).click(); + resolvers.resolve(); + assert.strictEqual( + root.outerHTML, + '
' + ); + currentTheme = undefined; + (root.children[0].children[1] as HTMLButtonElement).click(); + resolvers.resolve(); + assert.strictEqual( + root.outerHTML, + '
' + ); + (root.children[0].children[1] as HTMLButtonElement).click(); + resolvers.resolve(); + assert.strictEqual( + root.outerHTML, + '
' + ); + }); + + it('classes should be decorated onto the theme', () => { + const factory = create({ theme }); + const css = { + ' _key': 'test-key', + root: 'root' + }; + const widgetTheme = { + 'test-key': { + root: 'themed-root' + } + }; + const App = factory(function App({ middleware: { theme } }) { + const themedCss = theme.classes(css); + return ( +
+
+
+ ); + }); + const root = document.createElement('div'); + const r = renderer(() => ( + + )); + r.mount({ domNode: root }); + assert.strictEqual( + root.outerHTML, + '
' + ); + (root.children[0].children[1] as HTMLButtonElement).click(); + resolvers.resolve(); + assert.strictEqual( + root.outerHTML, + '
{"test-key":{"root":"themed-root"}}
' + ); + }); +});