Skip to content

Commit

Permalink
[FEATURE] Add experimental CSS variables and skeleton build (#393)
Browse files Browse the repository at this point in the history
This enables the usage via the processor/task APIs.
By default the CSS variables files are not built.

Co-authored-by: Matthias Oßwald <1410947+matz3@users.noreply.github.com>
  • Loading branch information
petermuessig and matz3 authored Feb 10, 2020
1 parent acf99b2 commit df8c39b
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 18 deletions.
31 changes: 27 additions & 4 deletions lib/processors/themeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ class ThemeBuilder {
* @param {module:@ui5/fs.Resource[]} resources Library files
* @param {Object} [options] Build options
* @param {boolean} [options.compress=false] Compress build output (CSS / JSON)
* @param {boolean} [options.cssVariables=false] Generates the CSS variables (css-variables.css, css-variables.source.less) and the skeleton for a theme (library-skeleton.css, [library-skeleton-RTL.css])
* @returns {Promise<module:@ui5/fs.Resource[]>} Resolving with array of created files
*/
build(resources, {compress = false} = {}) {
build(resources, {compress = false, cssVariables = false} = {}) {
const files = [];

const compile = (resource) => {
Expand All @@ -51,7 +52,8 @@ class ThemeBuilder {
},
compiler: {
compress
}
},
cssVariables
}).then((result) => {
const themeDir = path.dirname(resource.getPath());

Expand All @@ -71,6 +73,27 @@ class ThemeBuilder {
});

files.push(libCss, libCssRtl, libParams);

if (cssVariables) {
const libCssVarsSource = new Resource({
path: themeDir + "/css-variables.source.less",
string: result.cssVariablesSource
});
const libCssVars = new Resource({
path: themeDir + "/css-variables.css",
string: result.cssVariables
});
const libCssSkel = new Resource({
path: themeDir + "/library-skeleton.css",
string: result.cssSkeleton
});
const libCssSkelRtl = new Resource({
path: themeDir + "/library-skeleton-RTL.css",
string: result.cssSkeletonRtl
});

files.push(libCssVarsSource, libCssVars, libCssSkel, libCssSkelRtl);
}
});
};

Expand Down Expand Up @@ -100,12 +123,12 @@ class ThemeBuilder {
* @param {fs|module:@ui5/fs.fsInterface} parameters.fs Node fs or custom [fs interface]{@link module:resources/module:@ui5/fs.fsInterface}
* @param {Object} [parameters.options] Options
* @param {Object} [parameters.options.compress=false] Compress build output (CSS / JSON)
* @param {boolean} [parameters.options.cssVariables=false] Generates the CSS variables (css-variables.css, css-variables.source.less) and the skeleton for a theme (library-skeleton.css, [library-skeleton-RTL.css])
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving with theme resources
*/
module.exports = ({resources, fs, options}) => {
const themeBuilder = new ThemeBuilder({fs});
const compress = options && options.compress;
return themeBuilder.build(resources, {compress}).then((files) => {
return themeBuilder.build(resources, options).then((files) => {
themeBuilder.clearCache();
return files;
});
Expand Down
4 changes: 3 additions & 1 deletion lib/tasks/buildThemes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const fsInterface = require("@ui5/fs").fsInterface;
* @param {string} parameters.options.inputPattern Search pattern for *.less files to be built
* @param {string} [parameters.options.librariesPattern] Search pattern for .library files
* @param {boolean} [parameters.options.compress=true]
* @param {boolean} [parameters.options.cssVariables=false]
* @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
*/
module.exports = function({workspace, dependencies, options}) {
Expand Down Expand Up @@ -60,7 +61,8 @@ module.exports = function({workspace, dependencies, options}) {
resources,
fs: fsInterface(combo),
options: {
compress
compress,
cssVariables: !!options.cssVariables
}
});
}).then((processedResources) => {
Expand Down
81 changes: 70 additions & 11 deletions test/lib/processors/themeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,50 +38,80 @@ function prepareResources({library} = {}) {
};
}

function getExpectedResults({compress, library}) {
let css; let cssRtl; let json;
function getExpectedResults({compress, library, cssVariables}) {
const result = {};
if (compress) {
css =
result.css =
`.someClass{color:#000;padding:1px 2px 3px 4px}`;

cssRtl =
result.cssRtl =
`.someClass{color:#000;padding:1px 4px 3px 2px}`;
json = `{"someColor":"#000"}`;
result.json = `{"someColor":"#000"}`;
} else {
css =
result.css =
`.someClass {
color: #000000;
padding: 1px 2px 3px 4px;
}
`;

cssRtl =
result.cssRtl =
`.someClass {
color: #000000;
padding: 1px 4px 3px 2px;
}
`;

json =
result.json =
`{
"someColor": "#000000"
}`;
}

if (library !== false) {
css +=
result.css +=
`
/* Inline theming parameters */
#sap-ui-theme-sap\\.ui\\.foo{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23${compress ? "000" : "000000"}%22%7D')}
`;
cssRtl +=
result.cssRtl +=
`
/* Inline theming parameters */
#sap-ui-theme-sap\\.ui\\.foo{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23${compress ? "000" : "000000"}%22%7D')}
`;
}

return {css, cssRtl, json};
if (cssVariables) {
result.cssVariablesSource =
`@someColor: #000000;
:root {
--someColor: @someColor;
}
`;
result.cssVariables =
`:root {
--someColor: #000000;
}
/* Inline theming parameters */
#sap-ui-theme-sap\\.ui\\.foo{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23000000%22%7D')}
`;
result.cssSkeleton =
`.someClass {
color: var(--someColor);
padding: 1px 2px 3px 4px;
}
`;
result.cssSkeletonRtl =
`.someClass {
color: var(--someColor);
padding: 1px 4px 3px 2px;
}
`;
}

return result;
}

test("Processor: Builds a less file (default options)", async (t) => {
Expand Down Expand Up @@ -206,3 +236,32 @@ test("ThemeBuilder: Builds a less file (no library)", async (t) => {
t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct");
t.is(await jsonResource.getString(), expected.json, "JSON should be correct");
});

test("Processor: Builds a less file (cssVariables = true)", async (t) => {
const {resource, memoryAdapter} = prepareResources();

const [
cssResource,
cssRtlResource,
jsonResource,
cssVariablesSourceResource,
cssVariablesResource,
cssSkeletonResource,
cssSkeletonRtlResource
] = await themeBuilderProcessor({
resources: [resource],
fs: fsInterface(memoryAdapter),
options: {
cssVariables: true
}
});

const expected = getExpectedResults({cssVariables: true});
t.is(await cssResource.getString(), expected.css, "CSS should be correct");
t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct");
t.is(await jsonResource.getString(), expected.json, "JSON should be correct");
t.is(await cssVariablesSourceResource.getString(), expected.cssVariablesSource, "CSS Variables source should be correct");
t.is(await cssVariablesResource.getString(), expected.cssVariables, "CSS Variables should be correct");
t.is(await cssSkeletonResource.getString(), expected.cssSkeleton, "Skeleton CSS should be correct");
t.is(await cssSkeletonRtlResource.getString(), expected.cssSkeletonRtl, "Right-to-left skeleton CSS should be correct");
});
72 changes: 70 additions & 2 deletions test/lib/tasks/buildThemes.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ test.serial("buildThemes", async (t) => {
resources: [lessResource],
fs: {},
options: {
compress: true // default
compress: true, // default
cssVariables: false // default
}
}, "Processor should be called with expected arguments");

Expand Down Expand Up @@ -116,7 +117,8 @@ test.serial("buildThemes (compress = false)", async (t) => {
resources: [lessResource],
fs: {},
options: {
compress: false
compress: false,
cssVariables: false
}
}, "Processor should be called with expected arguments");

Expand All @@ -126,3 +128,69 @@ test.serial("buildThemes (compress = false)", async (t) => {
t.true(workspace.write.calledWithExactly(cssRtlResource));
t.true(workspace.write.calledWithExactly(jsonParametersResource));
});

test.serial("buildThemes (cssVariables = true)", async (t) => {
t.plan(10);

const lessResource = {};

const workspace = {
byGlob: async (globPattern) => {
if (globPattern === "/resources/test/library.source.less") {
return [lessResource];
} else {
return [];
}
},
write: sinon.stub()
};

const cssResource = {};
const cssRtlResource = {};
const jsonParametersResource = {};
const cssVariablesSourceResource = {};
const cssVariablesResource = {};
const cssSkeletonResource = {};
const cssSkeletonRtlResource = {};

t.context.themeBuilderStub.returns([
cssResource,
cssRtlResource,
jsonParametersResource,
cssVariablesSourceResource,
cssVariablesResource,
cssSkeletonResource,
cssSkeletonRtlResource
]);

await buildThemes({
workspace,
options: {
projectName: "sap.ui.demo.app",
inputPattern: "/resources/test/library.source.less",
cssVariables: true
}
});

t.deepEqual(t.context.themeBuilderStub.callCount, 1,
"Processor should be called once");

t.deepEqual(t.context.themeBuilderStub.getCall(0).args[0], {
resources: [lessResource],
fs: {},
options: {
compress: true,
cssVariables: true
}
}, "Processor should be called with expected arguments");

t.deepEqual(workspace.write.callCount, 7,
"workspace.write should be called 7 times");
t.true(workspace.write.calledWithExactly(cssResource));
t.true(workspace.write.calledWithExactly(cssRtlResource));
t.true(workspace.write.calledWithExactly(jsonParametersResource));
t.true(workspace.write.calledWithExactly(cssVariablesSourceResource));
t.true(workspace.write.calledWithExactly(cssVariablesResource));
t.true(workspace.write.calledWithExactly(cssSkeletonResource));
t.true(workspace.write.calledWithExactly(cssSkeletonRtlResource));
});

0 comments on commit df8c39b

Please sign in to comment.