Skip to content

Commit e3be2b5

Browse files
project can specify light and dark brand files
1 parent e14d224 commit e3be2b5

File tree

8 files changed

+167
-28
lines changed

8 files changed

+167
-28
lines changed

src/project/project-shared.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -521,12 +521,12 @@ export async function projectResolveBrand(
521521
) as BrandJson;
522522
return new Brand(brand, dirname(brandPath), project.dir);
523523
}
524-
async function loadLocalBrand(brandPath: string) : Promise<Brand> {
524+
async function loadRelativeBrand(brandPath: string, dir: string = dirname(fileName!)) : Promise<Brand> {
525525
let resolved: string = "";
526526
if (brandPath.startsWith("/")) {
527527
resolved = join(project.dir, brandPath);
528528
} else {
529-
resolved = join(dirname(fileName!), brandPath);
529+
resolved = join(dir, brandPath);
530530
}
531531
return await loadBrand(resolved);
532532
}
@@ -538,28 +538,28 @@ export async function projectResolveBrand(
538538
let fileNames = ["_brand.yml", "_brand.yaml"].map((file) =>
539539
join(project.dir, file)
540540
);
541-
if (project?.config?.brand === false) {
541+
let brand = project?.config?.brand as Boolean | string | {light?: string, dark?: string};
542+
if (brand === false) {
542543
project.brandCache.brand = undefined;
543544
return project.brandCache.brand;
544545
}
545-
if (typeof project?.config?.brand === "string") {
546-
fileNames = [join(project.dir, project.config.brand)];
546+
if (typeof brand === "object" && brand &&
547+
("light" in brand || "dark" in brand)) {
548+
project.brandCache.brand = {
549+
light: brand.light ? await loadRelativeBrand(brand.light, project.dir) : undefined,
550+
dark: brand.dark ? await loadRelativeBrand(brand.dark, project.dir) : undefined,
551+
};
552+
return project.brandCache.brand;
553+
}
554+
if (typeof brand === "string") {
555+
fileNames = [join(project.dir, brand)];
547556
}
548557

549558
for (const brandPath of fileNames) {
550559
if (!existsSync(brandPath)) {
551560
continue;
552561
}
553-
const brand = await readAndValidateYamlFromFile(
554-
brandPath,
555-
refSchema("brand", "Format-independent brand configuration."),
556-
"Brand validation failed for " + brandPath + ".",
557-
) as BrandJson;
558-
project.brandCache.brand = {light: new Brand(
559-
brand,
560-
dirname(brandPath),
561-
project.dir,
562-
)};
562+
project.brandCache.brand = {light: await loadBrand(brandPath)};
563563
}
564564
return project.brandCache.brand;
565565
} else {
@@ -568,6 +568,7 @@ export async function projectResolveBrand(
568568
if (brand === false) {
569569
return undefined;
570570
}
571+
console.log('resolve brand', fileName)
571572
if (brand === true || brand === undefined) {
572573
return project.resolveBrand();
573574
}
@@ -576,14 +577,14 @@ export async function projectResolveBrand(
576577
return fileInformation.brand;
577578
}
578579
if (typeof brand === "string") {
579-
fileInformation.brand = {light: await loadLocalBrand(brand)};
580+
fileInformation.brand = {light: await loadRelativeBrand(brand)};
580581
return fileInformation.brand;
581582
} else {
582583
assert(typeof brand === "object");
583584
if ("light" in brand || "dark" in brand) {
584585
let light, dark;
585586
if (typeof brand.light === "string") {
586-
light = await loadLocalBrand(brand.light)
587+
light = await loadRelativeBrand(brand.light)
587588
} else {
588589
light = new Brand(
589590
brand.light!,
@@ -592,7 +593,7 @@ export async function projectResolveBrand(
592593
);
593594
}
594595
if (typeof brand.dark === "string") {
595-
dark = await loadLocalBrand(brand.dark)
596+
dark = await loadRelativeBrand(brand.dark)
596597
} else {
597598
dark = new Brand(
598599
brand.dark!,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
brand:
2+
dark: red-background.yml
3+
light: blue-background.yml
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
color:
2+
background: "#ccddff"
3+
foreground: "#336644"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: "dark brand - ggplot"
3+
theme:
4+
light: simplex
5+
dark: cyborg # same effect as [brand, cyborg]
6+
execute:
7+
echo: false
8+
warning: false
9+
---
10+
11+
```{r}
12+
#| echo: false
13+
#| warning: false
14+
library(ggplot2)
15+
16+
ggplot_theme <- function(bgcolor, fgcolor) {
17+
theme_minimal(base_size = 11) %+%
18+
theme(
19+
panel.border = element_blank(),
20+
panel.grid.major.y = element_blank(),
21+
panel.grid.minor.y = element_blank(),
22+
panel.grid.major.x = element_blank(),
23+
panel.grid.minor.x = element_blank(),
24+
text = element_text(colour = fgcolor),
25+
axis.text = element_text(colour = fgcolor),
26+
rect = element_rect(colour = bgcolor, fill = bgcolor),
27+
plot.background = element_rect(fill = bgcolor, colour = NA),
28+
axis.line = element_line(colour = fgcolor),
29+
axis.ticks = element_line(colour = fgcolor)
30+
)
31+
}
32+
33+
brand_ggplot <- function(brand_yml) {
34+
brand <- yaml::yaml.load_file(brand_yml)
35+
ggplot_theme(brand$color$background, brand$color$foreground)
36+
}
37+
38+
blue_theme <- brand_ggplot("blue-background.yml")
39+
red_theme <- brand_ggplot("red-background.yml")
40+
41+
colour_scale <- scale_colour_manual(values = c("darkorange", "purple", "cyan4"))
42+
```
43+
44+
45+
```{r}
46+
#| renderings: [light, dark]
47+
ggplot(mtcars, aes(mpg, wt)) +
48+
geom_point(aes(colour = factor(cyl))) + blue_theme + colour_scale
49+
ggplot(mtcars, aes(mpg, wt)) +
50+
geom_point(aes(colour = factor(cyl))) + red_theme + colour_scale
51+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
color:
2+
background: "#42070b"
3+
foreground: "#cceedd"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: "dark brand - ggplot"
3+
execute:
4+
echo: false
5+
warning: false
6+
---
7+
8+
```{r}
9+
#| echo: false
10+
#| warning: false
11+
library(ggplot2)
12+
13+
ggplot_theme <- function(bgcolor, fgcolor) {
14+
theme_minimal(base_size = 11) %+%
15+
theme(
16+
panel.border = element_blank(),
17+
panel.grid.major.y = element_blank(),
18+
panel.grid.minor.y = element_blank(),
19+
panel.grid.major.x = element_blank(),
20+
panel.grid.minor.x = element_blank(),
21+
text = element_text(colour = fgcolor),
22+
axis.text = element_text(colour = fgcolor),
23+
rect = element_rect(colour = bgcolor, fill = bgcolor),
24+
plot.background = element_rect(fill = bgcolor, colour = NA),
25+
axis.line = element_line(colour = fgcolor),
26+
axis.ticks = element_line(colour = fgcolor)
27+
)
28+
}
29+
30+
brand_ggplot <- function(brand_yml) {
31+
brand <- yaml::yaml.load_file(brand_yml)
32+
ggplot_theme(brand$color$background, brand$color$foreground)
33+
}
34+
35+
blue_theme <- brand_ggplot("blue-background.yml")
36+
red_theme <- brand_ggplot("red-background.yml")
37+
38+
colour_scale <- scale_colour_manual(values = c("darkorange", "purple", "cyan4"))
39+
```
40+
41+
42+
```{r}
43+
#| renderings: [light, dark]
44+
ggplot(mtcars, aes(mpg, wt)) +
45+
geom_point(aes(colour = factor(cyl))) + blue_theme + colour_scale
46+
ggplot(mtcars, aes(mpg, wt)) +
47+
geom_point(aes(colour = factor(cyl))) + red_theme + colour_scale
48+
```

tests/integration/playwright/tests/html-dark-mode-nojs.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@ test('Light brand default, no JS', async ({ page }) => {
2020
await expect(locatr).toHaveClass('fullcontent quarto-light');
2121
await expect(locatr).toHaveCSS('background-color', 'rgb(252, 252, 252)');
2222
});
23+
24+
25+
test('Project dark brand default, no JS', async ({ page }) => {
26+
// This document use a custom theme file that change the background color of the title banner
27+
// Same user defined color should be used in both dark and light theme
28+
await page.goto('./html/dark-brand/project-light-dark/simple.html');
29+
const locatr = await page.locator('body').first();
30+
await expect(locatr).toHaveClass('fullcontent quarto-dark');
31+
await expect(locatr).toHaveCSS('background-color', 'rgb(66, 7, 11)');
32+
});
33+
Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,45 @@
11
import { test, expect } from '@playwright/test';
22

3-
test('Dark and light brand after user themes', async ({ page }) => {
4-
// This document use a custom theme file that change the background color of the title banner
5-
// Same user defined color should be used in both dark and light theme
6-
await page.goto('./html/dark-brand/brand-after-theme.html');
3+
async function check_red_blue(page) {
74
const locatr = await page.locator('body').first();
85
await expect(locatr).toHaveClass('fullcontent quarto-dark');
96
await expect(locatr).toHaveCSS('background-color', 'rgb(66, 7, 11)');
107
await page.locator("a.quarto-color-scheme-toggle").click();
118
const locatr2 = await page.locator('body').first();
129
await expect(locatr2).toHaveCSS('background-color', 'rgb(204, 221, 255)');
13-
});
10+
}
1411

15-
16-
test('Dark and light brand before user themes', async ({ page }) => {
17-
// This document use a custom theme file that change the background color of the title banner
18-
// Same user defined color should be used in both dark and light theme
19-
await page.goto('./html/dark-brand/brand-before-theme.html');
12+
async function check_theme_overrides(page) {
2013
const locatr = await page.locator('body').first();
2114
await expect(locatr).toHaveClass('fullcontent quarto-light');
2215
await expect(locatr).toHaveCSS('background-color', 'rgb(252, 252, 252)');
2316
await page.locator("a.quarto-color-scheme-toggle").click();
2417
const locatr2 = await page.locator('body').first();
2518
await expect(locatr2).toHaveCSS('background-color', 'rgb(6, 6, 6)');
19+
}
20+
// themes used in these documents have background colors
21+
22+
test('Dark and light brand after user themes', async ({ page }) => {
23+
// brand overrides theme background color
24+
await page.goto('./html/dark-brand/brand-after-theme.html');
25+
await check_red_blue(page);
2626
});
27+
28+
test('Dark and light brand before user themes', async ({ page }) => {
29+
// theme will override brand
30+
await page.goto('./html/dark-brand/brand-before-theme.html');
31+
await check_theme_overrides(page);
32+
});
33+
34+
// project tests
35+
36+
test('Project specifies dark and light brands', async ({ page }) => {
37+
await page.goto('./html/dark-brand/project-light-dark/simple.html');
38+
await check_red_blue(page);
39+
});
40+
41+
test('Project brand before user themes', async ({ page }) => {
42+
// theme will override brand
43+
await page.goto('./html/dark-brand/project-light-dark/brand-under-theme.html');
44+
await check_theme_overrides(page);
45+
});

0 commit comments

Comments
 (0)