Skip to content

Commit

Permalink
fix: Enable use of external css using @import (#10953)
Browse files Browse the repository at this point in the history
Add fix for using a external css file by having
the `@import url(...)` in a css file.
Also supports imports with media queries.

fixes #10228
  • Loading branch information
caalador committed May 10, 2021
1 parent e06014a commit ac643c4
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 5 deletions.
9 changes: 9 additions & 0 deletions flow-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ The full information would be preferred:

For creating a plugin see [Writing a plugin](https://webpack.js.org/contribute/writing-a-plugin/)

## Working with plugin in a project

Testing a plugin is often easier by using it in a project.
As by default all plugins are re-copied from the `flow-server.jar` making changes
would mean building the server module each time.

The plugins are copied to `target/plugins` from there they are "installed" to `node_modules` with (p)npm.
To make editing and testing easier one can add `"update": false,` to the plugin package json that will make it not be overwritten.

## Using a Flow webpack plugin

The flow plugins get installed to `node_modules/@vaadin` which means that using them we should use the for `@vaadin/${plugin-name}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,44 @@ const getStyleModule = (id) => {
return cssText;
};
`;

const createLinkReferences = `
const createLinkReferences = (css, target) => {
// Unresolved urls are written as '@import url(text);' to the css
// [0] is the full match
// [1] matches the media query
// [2] matches the url
const importMatcher = /(?:@media\\s(.+?))?(?:\\s{)?\\@import\\surl\\((.+?)\\);(?:})?/g;
var match;
var styleCss = css;
// For each external url import add a link reference
while((match = importMatcher.exec(css)) !== null) {
styleCss = styleCss.replace(match[0], "");
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = match[2];
if (match[1]) {
link.media = match[1];
}
// For target document append to head else append to target
if (target === document) {
document.head.appendChild(link);
} else {
target.appendChild(link);
}
};
return styleCss;
};
`;

const injectGlobalCssMethod = `
// target: Document | ShadowRoot
export const injectGlobalCss = (css, target, first) => {
const sheet = new CSSStyleSheet();
sheet.replaceSync(css);
sheet.replaceSync(createLinkReferences(css,target));
if (first) {
target.adoptedStyleSheets = [sheet, ...target.adoptedStyleSheets];
} else {
Expand Down Expand Up @@ -105,6 +138,7 @@ function generateThemeFile(themeFolder, themeName, themeProperties, productionMo
themeFile += `import {applyTheme as applyBaseTheme} from './theme-${themeProperties.parent}.generated.js';`;
}

themeFile += createLinkReferences;
themeFile += injectGlobalCssMethod;
themeFile += addCssBlockMethod;
themeFile += addStyleIncludeMethod;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.global {
color: blue;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@import url('https://fonts.googleapis.com/css?family=Poppins');
@import url('https://fonts.googleapis.com/css?family=Oswald') screen and (orientation:landscape);

@import url("docImport.css");

@font-face {
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
@@ -1,3 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Itim');

:host {
width: 1200px;
height: 600px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.vaadin.flow.webcomponent;

import java.util.List;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
Expand Down Expand Up @@ -172,4 +175,50 @@ public void documentCssFonts_fromLocalCssFile_fontNotAppliedToEmbeddedComponent(
"Expected no Ostrich font to be applied to embedded component",
(Boolean) ostrichFontStylesFoundForEmbedded);
}

@Test
public void documentCssImport_onlyExternalAddedToHeadAsLink() {
open();
checkLogsForErrors();

final WebElement documentHead = getDriver()
.findElement(By.xpath("/html/head"));
final List<WebElement> links = documentHead
.findElements(By.tagName("link"));

List<String> linkUrls = links.stream()
.map(link -> link.getAttribute("href"))
.collect(Collectors.toList());
Assert.assertTrue("Missing link for external url", linkUrls
.contains("https://fonts.googleapis.com/css?family=Poppins"));
Assert.assertTrue("Link with media query was not found", linkUrls
.contains("https://fonts.googleapis.com/css?family=Oswald"));
Assert.assertFalse("Found import that webpack should have resolved",
linkUrls.contains("docImport.css"));

final List<WebElement> mediaLinks = links.stream()
.filter(link -> link.getAttribute("href").equals(
"https://fonts.googleapis.com/css?family=Oswald"))
.collect(Collectors.toList());
Assert.assertFalse("No expected media link found",
mediaLinks.isEmpty());
Assert.assertEquals("Wrong media attribute contents",
"screen and (orientation:landscape)",
mediaLinks.get(0).getAttribute("media"));
}

@Test
public void stylesCssImport_externalLinkAddedToShadowroot() {
open();
checkLogsForErrors();
final TestBenchElement themedComponent = $("themed-component").first();
final List<WebElement> links = findInShadowRoot(themedComponent,
By.tagName("link"));

List<String> linkUrls = links.stream()
.map(link -> link.getAttribute("href"))
.collect(Collectors.toList());
Assert.assertTrue("Missing link for external url", linkUrls
.contains("https://fonts.googleapis.com/css?family=Itim"));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import url('https://fonts.googleapis.com/css?family=Itim');
@import 'sub-css/sub.css';

@font-face {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.vaadin.flow.uitest.ui.theme;

import java.util.List;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
Expand Down Expand Up @@ -261,6 +264,26 @@ public void themeRulesOverrideLumo() {

}

@Test
public void documentCssImport_onlyExternalAddedToHeadAsLink() {
open();
checkLogsForErrors();

final WebElement documentHead = getDriver()
.findElement(By.xpath("/html/head"));
final List<WebElement> links = documentHead
.findElements(By.tagName("link"));

List<String> linkUrls = links.stream()
.map(link -> link.getAttribute("href"))
.collect(Collectors.toList());

Assert.assertTrue("Missing link for external url", linkUrls
.contains("https://fonts.googleapis.com/css?family=Itim"));
Assert.assertFalse("Found import that webpack should have resolved",
linkUrls.contains("sub-css/sub.css"));
}

@Override
protected String getTestPath() {
String path = super.getTestPath();
Expand Down

0 comments on commit ac643c4

Please sign in to comment.