Skip to content

Commit

Permalink
feat: document level npm css
Browse files Browse the repository at this point in the history
Now there is a possibility to use the
"documentCss" key in theme.json to
inject npm css into the document rool level
element.

This is mostly for embedded web components
as they do not target document by default

Fixes #9533
  • Loading branch information
caalador committed Dec 9, 2020
1 parent bd84a1d commit d77623c
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jsoup.nodes.Element;

import com.vaadin.flow.component.PushConfiguration;
import com.vaadin.flow.component.WebComponentExporter;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.BodySize;
import com.vaadin.flow.component.page.Inline;
Expand All @@ -35,6 +36,7 @@
import com.vaadin.flow.component.page.TargetElement;
import com.vaadin.flow.component.page.Viewport;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.theme.Theme;

import static com.vaadin.flow.server.startup.AbstractAnnotationValidator.getClassAnnotations;
import static com.vaadin.flow.server.startup.VaadinAppShellInitializer.getValidAnnotations;
Expand Down Expand Up @@ -180,6 +182,10 @@ public String validateClass(Class<?> clz) {
List<Class<?>> validOnlyForAppShell = (List) getValidAnnotations();
// PageTitle can be in AppShell and Views
validOnlyForAppShell.remove(PageTitle.class);
if(WebComponentExporter.class.isAssignableFrom(clz)) {
// Webcomponent exporter should have the theme annotation
validOnlyForAppShell.remove(Theme.class);
}

String offending = getClassAnnotations(clz, validOnlyForAppShell);
if (!offending.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ static Map<String, String> getReplacementsMap(String tag,
if (themeName != null && !themeName.isEmpty()) {
replacements.put("ThemeImport",
"import {applyTheme} from 'theme/theme-generated.js';\n\n");
replacements.put("ApplyTheme", "applyTheme(shadow);\n");
replacements.put("ApplyTheme", "applyTheme(shadow);\n ");
} else {
replacements.put("ThemeImport", "");
replacements.put("ApplyTheme", "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function handleThemes(themeName, themesFolder, projectStaticAssetsOutputFolder)

copyStaticAssets(themeProperties, projectStaticAssetsOutputFolder, logger);

const themeFile = generateThemeFile(themeFolder, themeName);
const themeFile = generateThemeFile(themeFolder, themeName, themeProperties);

fs.writeFileSync(path.resolve(themeFolder, themeName + '.js'), themeFile);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
],
"repository": "vaadin/flow",
"name": "@vaadin/application-theme-plugin",
"version": "0.2.1",
"version": "0.2.2",
"main": "application-theme-plugin.js",
"author": "Vaadin Ltd",
"license": "Apache-2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ export const injectGlobalCss = (css, target) => {
*
* @param {string} themeFolder folder of the theme
* @param {string} themeName name of the handled theme
* @param {json} themeProperties theme.json contents
* @returns {string} theme file content
*/
function generateThemeFile(themeFolder, themeName) {
function generateThemeFile(themeFolder, themeName, themeProperties) {
const globalFiles = glob.sync('*.css', {
cwd: themeFolder,
nodir: true,
Expand All @@ -73,12 +74,21 @@ function generateThemeFile(themeFolder, themeName) {
const variable = camelCase(filename);
imports.push(`import ${variable} from './${filename}';\n`);
if (filename == themeFileAlwaysAddToDocument) {
globalCssCode.push(`injectGlobalCss(${variable}.toString(), document);\n `);
globalCssCode.push(`injectGlobalCss(${variable}.toString(), document);\n `);
} else {
globalCssCode.push(`injectGlobalCss(${variable}.toString(), target);\n `);
}
});

let i = 0;
if (themeProperties.documentCss) {
themeProperties.documentCss.forEach((cssImport) => {
const variable = 'module' + i++;
imports.push(`import ${variable} from '${cssImport}';\n`);
globalCssCode.push(`injectGlobalCss(${variable}.toString(), target);\n `);
globalCssCode.push(`injectGlobalCss(${variable}.toString(), document);\n `);
});
}
componentsFiles.forEach((componentCss) => {
const filename = path.basename(componentCss);
const tag = filename.replace('.css', '');
Expand Down
5 changes: 4 additions & 1 deletion flow-server/src/main/resources/webpack.generated.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ module.exports = {
name(resourcePath, resourceQuery) {
const urlResource = resourcePath.substring(frontendFolder.length);
if(urlResource.match(themePartRegex)){
return /^(\\|\/)theme\1[\s\S]*?\1(.*)/.exec(urlResource)[2].replace(/\\/, "/");
return /^(\\|\/)theme\1[\s\S]*?\1(.*)/.exec(urlResource)[2].replace(/\\/g, "/");
}
if(urlResource.match(/(\\|\/)node_modules\1/)) {
return /(\\|\/)node_modules\1(?!.*node_modules)([\S]*)/.exec(urlResource)[2].replace(/\\/g, "/");
}
return '[path][name].[ext]';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServletRequest;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -29,7 +28,9 @@
import org.slf4j.LoggerFactory;
import org.slf4j.impl.SimpleLoggerFactory;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.PushConfiguration;
import com.vaadin.flow.component.WebComponentExporter;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.BodySize;
import com.vaadin.flow.component.page.Inline;
Expand All @@ -39,6 +40,7 @@
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.component.page.TargetElement;
import com.vaadin.flow.component.page.Viewport;
import com.vaadin.flow.component.webcomponent.WebComponent;
import com.vaadin.flow.di.Lookup;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.server.AppShellRegistry;
Expand Down Expand Up @@ -99,6 +101,22 @@ public static class MyAppShellWithMultipleAnnotations
public static class OffendingClass {
}

public static class WebHolder extends Component {
}

@Theme(themeClass = AbstractTheme.class)
public static class NonOffendingExporter
extends WebComponentExporter<WebHolder> {
public NonOffendingExporter() {
super("web-component");
}

@Override
public void configureInstance(WebComponent<WebHolder> webComponent,
WebHolder component) {
}
}

public static class MyAppShellWithConfigurator
implements AppShellConfigurator {
@Override
Expand Down Expand Up @@ -386,6 +404,12 @@ public void should_throw_when_offendingClass() throws Exception {
initializer.process(classes, servletContext);
}

@Test
public void offendingEmbeddedThemeClass_shouldNotThrow() throws Exception {
classes.add(NonOffendingExporter.class);
initializer.process(classes, servletContext);
}

@Test
public void should_throw_when_multipleAppShell() throws Exception {
exception.expect(InvalidApplicationConfigurationException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
font-family: "Ostrich";
src: url("./font/ostrich-sans-regular.ttf") format("TrueType");
}

.global {
color: blue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
background-image: url("./img/bg.jpg");
font-family: "Ostrich";
}

.internal {
color: green;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"documentCss": ["@fortawesome/fontawesome-free/css/all.css"],
"assets": {
"@fortawesome/fontawesome-free": {
"svgs/regular/**": "fortawesome/icons"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package com.vaadin.flow.webcomponent;

import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;

@NpmPackage(value = "@fortawesome/fontawesome-free", version = "5.15.1")
public class ThemedComponent extends Div {

public static final String TEST_TEXT_ID = "test-text";
Expand All @@ -26,13 +28,19 @@ public class ThemedComponent extends Div {
public static final String MY_LIT_ID = "button";
public static final String EMBEDDED_ID = "embedded";

public static final String HAND_ID = "sparkle-hand";

public ThemedComponent() {
setId(EMBEDDED_ID);
final Span textSpan = new Span(
"Welcome to the embedded application theme test");
textSpan.setId(TEST_TEXT_ID);

add(textSpan);
Span hand = new Span();
hand.setId(HAND_ID);
hand.addClassNames("internal", "fas", "fa-hand-sparkles");

add(textSpan, hand);

add(new Div());
add(new MyPolymerField().withId(MY_POLYMER_ID));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

<body>

<span class="internal global" id="overflow">Internal should not apply</span>

<themed-component></themed-component>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.vaadin.testbench.TestBenchElement;

import static com.vaadin.flow.webcomponent.ThemedComponent.EMBEDDED_ID;
import static com.vaadin.flow.webcomponent.ThemedComponent.HAND_ID;
import static com.vaadin.flow.webcomponent.ThemedComponent.MY_LIT_ID;
import static com.vaadin.flow.webcomponent.ThemedComponent.MY_POLYMER_ID;

Expand Down Expand Up @@ -58,6 +59,20 @@ public void applicationTheme_GlobalCss_isUsedOnlyInEmbeddeComponent() {

Assert.assertNotEquals("Ostrich", body.getCssValue("font-family"));

final TestBenchElement embeddedComponent = themedComponent
.$(DivElement.class).id(EMBEDDED_ID);

final SpanElement handElement = embeddedComponent.$(SpanElement.class)
.id(HAND_ID);

Assert
.assertEquals("Color should have been applied", "rgba(0, 128, 0, 1)",
handElement.getCssValue("color"));

Assert
.assertEquals("Embedded style should not match external component",
"rgba(0, 0, 255, 1)",
$(SpanElement.class).id("overflow").getCssValue("color"));
getDriver().get(getRootURL() + "/VAADIN/static/img/bg.jpg");
Assert.assertFalse("app-theme background file should be served",
driver.getPageSource().contains("Could not navigate"));
Expand Down Expand Up @@ -88,4 +103,27 @@ public void componentThemeIsApplied_forPolymerAndLit() {
Assert.assertEquals("Lit radiobutton should have red background",
"rgba(255, 0, 0, 1)", radio.getCssValue("background-color"));
}

@Test
public void documentCssFonts_fontsAreAppliedAndAvailable() {
open();
checkLogsForErrors();
final TestBenchElement themedComponent = $("themed-component").first();
final TestBenchElement embeddedComponent = themedComponent
.$(DivElement.class).id(EMBEDDED_ID);

final SpanElement handElement = embeddedComponent.$(SpanElement.class)
.id(HAND_ID);
Assert.assertEquals("Font family faulty", "\"Font Awesome 5 Free\"",
handElement.getCssValue("font-family"));
Assert.assertEquals("Font weight faulty", "900",
handElement.getCssValue("font-weight"));
Assert.assertEquals("display value faulty", "inline-block",
handElement.getCssValue("display"));

getDriver().get(getRootURL()
+ "/path/VAADIN/static/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2");
Assert.assertFalse("Font resource should be available",
driver.getPageSource().contains("HTTP ERROR 404 Not Found"));
}
}

0 comments on commit d77623c

Please sign in to comment.