Skip to content

Commit

Permalink
Merge pull request #2628 from evidence-dev/feat/2499-theming
Browse files Browse the repository at this point in the history
Theming & Appearances
  • Loading branch information
ItsMeBrianD authored Dec 10, 2024
2 parents dfc1f4c + e2c95d1 commit 56dc36c
Show file tree
Hide file tree
Showing 318 changed files with 7,806 additions and 4,827 deletions.
10 changes: 10 additions & 0 deletions .changeset/clever-hounds-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@evidence-dev/evidence': major
'@evidence-dev/component-utilities': major
'@evidence-dev/preprocess': major
'@evidence-dev/sdk': major
'@evidence-dev/core-components': major
'@evidence-dev/tailwind': major
---

Theming & Appearances
5 changes: 5 additions & 0 deletions .changeset/cuddly-geckos-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@evidence-dev/icons': patch
---

Icons use currentColor instead of hardcoded color
4 changes: 3 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ packages/evidence/scripts/svelte.config.js
packages/ui/icons/src

# Don't lint playwright reports
**/playwright-report*
**/playwright-report*

storybook-static
2 changes: 1 addition & 1 deletion e2e/base-path/tests/tests.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ test.describe('Page', () => {
await expect(page.getByText('This is Page B', { exact: true })).toBeVisible();
await expect(new URL(page.url()).pathname).toBe(`${basePath}/page-b/`);

const logoLink = await page.getByAltText('Home');
const logoLink = await page.getByAltText('Home').first();
await logoLink.click();
await waitForPageToLoad(page);

Expand Down
108 changes: 108 additions & 0 deletions e2e/test-utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// @ts-check

/** @typedef {import('@playwright/test').Page} Page */
import { expect } from '@playwright/test';

Expand All @@ -18,3 +20,109 @@ export function waitForWasm(page) {
});
});
}

/**
* @param {import('@playwright/test').Page} page
* @returns {Promise<boolean>}
*/
export const isKebabMenuOpen = async (page) => {
try {
const menu = await page.getByRole('menu', { name: 'Menu' });
const isVisible = await menu.isVisible();
return isVisible;
} catch (e) {
console.error('isKebabMenuOpen failed', e);
throw e;
}
};

/**
* @param {import('@playwright/test').Page} page
* @returns {Promise<boolean>} True if the kebab menu was opened, false if it was already open
*/
export const openKebabMenu = async (page) => {
if (await isKebabMenuOpen(page)) return false;

try {
// Click button to open menu
await page.getByRole('button', { name: 'Menu' }).click();
// Make sure menu is open
await expect(page.getByRole('menu', { name: 'Menu' })).toBeVisible();
return true;
} catch (e) {
console.error('openKebabMenu failed', e);
throw e;
}
};

/**
* @param {import('@playwright/test').Page} page
* @returns {Promise<boolean>} True if the kebab menu was closed, false if it was already closed
*/
export const closeKebabMenu = async (page) => {
if (!(await isKebabMenuOpen(page))) return false;

try {
// Click button to close menu
await page.getByRole('button', { name: 'Menu' }).click();
// Make sure menu is closed
await expect(page.getByRole('menu', { name: 'Menu' })).not.toBeVisible();
return true;
} catch (e) {
console.error('closeKebabMenu failed', e);
throw e;
}
};

/**
* @param {import('@playwright/test').Page} page
* @param {{ closeKebabMenu?: boolean }} options
* @returns {Promise<'light' | 'dark' | 'system'>}
*/
const getAppearance = async (page, options = { closeKebabMenu: true }) => {
try {
await openKebabMenu(page);

const textContent = await page.getByRole('menuitem', { name: 'Appearance' }).textContent();
const appearance = textContent?.split(/\s+/)[1].toLowerCase();
if (!appearance || !['light', 'dark', 'system'].includes(appearance)) {
throw new Error(`Invalid appearance ${appearance}`);
}
return /** @type {'light' | 'dark' | 'system'} */ (appearance);
} catch (e) {
console.error('getAppearance failed', e);
throw e;
} finally {
if (options.closeKebabMenu) {
await closeKebabMenu(page);
}
}
};

/**
* @param {import('@playwright/test').Page} page
* @param {'light' | 'dark' | 'system'} appearance
*/
export const switchAppearance = async (page, appearance) => {
try {
let current = await getAppearance(page);
if (current === appearance) return;

await openKebabMenu(page);
// Switch appearance a max of 2 times (since there's 3 options)
for (let i = 0; i < 2; i++) {
await page.getByRole('menuitem', { name: 'Appearance' }).click();
current = await getAppearance(page, { closeKebabMenu: false });
if (current === appearance) break;
}

if (current !== appearance) {
throw new Error(`Appearance ${current} doesnt match ${appearance} after switching`);
}
} catch (e) {
console.error('switchAppearance failed', e);
throw e;
} finally {
await closeKebabMenu(page);
}
};
20 changes: 15 additions & 5 deletions e2e/themes/evidence.config.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
themes:
light:
mySemanticColor: "#ff0000"
dark:
mySemanticColor: "#00ff00"
appearance:
default: light
switcher: true

theme:
colors:
base:
light: "#fdf4ff"
dark: "#170118"
primary:
light: "#ff0000"
dark: "#00ff00"
myCustomColor:
light: "#abcdef"
dark: "#fedcba"

plugins:
components:
Expand Down
8 changes: 4 additions & 4 deletions e2e/themes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"name": "e2e-themes",
"version": "0.0.18",
"scripts": {
"build": "cross-env VITE_EVIDENCE_THEMES=true evidence build",
"build:strict": "cross-env VITE_EVIDENCE_THEMES=true evidence build:strict",
"dev": "cross-env VITE_EVIDENCE_THEMES=true evidence dev",
"build": "cross-env evidence build",
"build:strict": "cross-env evidence build:strict",
"dev": "cross-env evidence dev",
"sources": "evidence sources",
"preview": "cross-env VITE_EVIDENCE_THEMES=true evidence preview",
"preview": "cross-env evidence preview",
"test:preview": "playwright test",
"test:dev": "cross-env DEV=true playwright test"
},
Expand Down
17 changes: 16 additions & 1 deletion e2e/themes/pages/index.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
<div data-testid="div-with-background" class="w-20 h-20 bg-mySemanticColor">
<div data-testid="div-primary-class" class="bg-primary">
div-primary-class
</div>

<div data-testid="div-primary-var" style="background: var(--primary)">
div-primary-var
</div>

<div data-testid="div-myCustomColor-class" class="bg-myCustomColor">
div-myCustomColor-class
</div>

<div data-testid="div-myCustomColor-var" style="background: var(--myCustomColor)">
div-myCustomColor-var
</div>

This is some body text
52 changes: 40 additions & 12 deletions e2e/themes/tests/tests.spec.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
// @ts-check
import { test, expect } from '@playwright/test';
import { waitForPageToLoad } from '../../test-utils';
import { switchAppearance, waitForPageToLoad } from '../../test-utils';

test('should change color based on theme', async ({ page }) => {
test('should change colors based on theme', async ({ page }) => {
await page.goto('/');
await waitForPageToLoad(page);

const divWithBackground = await page.getByTestId('div-with-background');
const divPrimaryClass = await page.getByTestId('div-primary-class');
const divPrimaryVar = await page.getByTestId('div-primary-var');
const divMyCustomColorClass = await page.getByTestId('div-myCustomColor-class');
const divMyCustomColorVar = await page.getByTestId('div-myCustomColor-var');

// Starts with system theme (dark)
await expect(divWithBackground).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await switchAppearance(page, 'system');
await expect(divPrimaryClass).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await expect(divPrimaryVar).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await expect(divMyCustomColorClass).toHaveCSS('background-color', 'rgb(254, 220, 186)');
await expect(divMyCustomColorVar).toHaveCSS('background-color', 'rgb(254, 220, 186)');

await page.getByLabel('Menu').click();
await switchAppearance(page, 'light');
await expect(divPrimaryClass).toHaveCSS('background-color', 'rgb(255, 0, 0)');
await expect(divPrimaryVar).toHaveCSS('background-color', 'rgb(255, 0, 0)');
await expect(divMyCustomColorClass).toHaveCSS('background-color', 'rgb(171, 205, 239)');
await expect(divMyCustomColorVar).toHaveCSS('background-color', 'rgb(171, 205, 239)');

// Light theme
await page.getByRole('menuitem', { name: 'Appearance' }).click();
await expect(divWithBackground).toHaveCSS('background-color', 'rgb(255, 0, 0)');
await switchAppearance(page, 'dark');
await expect(divPrimaryClass).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await expect(divPrimaryVar).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await expect(divMyCustomColorClass).toHaveCSS('background-color', 'rgb(254, 220, 186)');
await expect(divMyCustomColorVar).toHaveCSS('background-color', 'rgb(254, 220, 186)');
});

test('body text should be computed from base', async ({ page }) => {
await page.goto('/');
await waitForPageToLoad(page);

const body = await page.locator('body');
const text = await page.getByText('This is some body text');

await switchAppearance(page, 'system');
await expect(text).toHaveCSS('color', 'rgb(205, 200, 206)');
await expect(body).toHaveCSS('background-color', 'rgb(23, 1, 24)');

await switchAppearance(page, 'light');
await expect(text).toHaveCSS('color', 'rgb(45, 42, 46)');
await expect(body).toHaveCSS('background-color', 'rgb(253, 244, 255)');

// Dark theme
await page.getByRole('menuitem', { name: 'Appearance' }).click();
await expect(divWithBackground).toHaveCSS('background-color', 'rgb(0, 255, 0)');
await switchAppearance(page, 'dark');
await expect(text).toHaveCSS('color', 'rgb(205, 200, 206)');
await expect(body).toHaveCSS('background-color', 'rgb(23, 1, 24)');
});
5 changes: 3 additions & 2 deletions packages/evidence/scripts/build-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ fsExtra.outputFileSync(
import { sourceQueryHmr, configVirtual, queryDirectoryHmr } from '@evidence-dev/sdk/build/vite';
import { isDebug } from '@evidence-dev/sdk/utils';
import { log } from "@evidence-dev/sdk/logger";
import { evidenceThemes } from '@evidence-dev/tailwind/vite-plugin';
const logger = createLogger();
const strictFs = (process.env.NODE_ENV === 'development') ? false : true;
/** @type {import('vite').UserConfig} */
const config =
{
plugins: [sveltekit(), configVirtual(), queryDirectoryHmr, sourceQueryHmr()],
plugins: [sveltekit(), configVirtual(), queryDirectoryHmr, sourceQueryHmr(), evidenceThemes()],
optimizeDeps: {
include: ['echarts-stat', 'echarts', 'blueimp-md5', 'nanoid', '@uwdata/mosaic-sql',
// We need these to prevent HMR from doing a full page reload
Expand All @@ -74,7 +75,7 @@ fsExtra.outputFileSync(
])
],
exclude: ['svelte-icons', '@evidence-dev/universal-sql', '$evidence/config']
exclude: ['svelte-icons', '@evidence-dev/universal-sql', '$evidence/config', '$evidence/themes']
},
ssr: {
external: ['@evidence-dev/telemetry', 'blueimp-md5', 'nanoid', '@uwdata/mosaic-sql', '@evidence-dev/sdk/plugins']
Expand Down
5 changes: 5 additions & 0 deletions packages/lib/component-utilities/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"types": ["@evidence-dev/tailwind"]
}
}
1 change: 1 addition & 0 deletions packages/lib/component-utilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@evidence-dev/tailwind": "workspace:^",
"@faker-js/faker": "^8.0.2",
"vitest": "^2.0.5"
},
Expand Down
80 changes: 0 additions & 80 deletions packages/lib/component-utilities/src/colours.js

This file was deleted.

Loading

0 comments on commit 56dc36c

Please sign in to comment.