Compare commits

10 Commits
Author SHA1 Message Date
jakob.scheidandGitea c739442cfc test(settings): add describe to settingsValidator test 2026-07-28 19:58:24 +02:00
jakob.scheidandGitea 56fb495b62 test(i18n): add describe to LanguageSwitchButton test 2026-07-28 19:58:24 +02:00
jakob.scheidandGitea f8915297f4 test(i18n): add describe to currentLanguage test 2026-07-28 19:58:24 +02:00
jakob.scheidandGitea 5bf6711eb7 test(style): add describe to cssDimensions test 2026-07-28 19:58:24 +02:00
jakob.scheid 55164c3b2f Merge branch 'fix/update-title-on-language-change'
Deploy on dev / Deploy on dev (push) Successful in 38s
2026-07-28 19:55:22 +02:00
jakob.scheid b245aa4a39 feat: update the page title using a watcher
Added a watcher (watchEffect) to App.vue to update the page title
always when a dependency changes. The afterEach hook was removed from
the router.
2026-07-28 19:55:12 +02:00
jakob.scheidandGitea 5b93f547f3 Merge pull request 'Feat: update the page title using a watcher' (#148) from fix/update-title-on-language-change into main
Deploy on dev / Deploy on dev (push) Successful in 38s
Reviewed-on: #148
Reviewed-by: Jakob Gregory
2026-07-28 19:50:52 +02:00
jakob.scheid 3e30e85354 feat: update the page title using a watcher
Added a watcher (watchEffect) to App.vue to update the page title
always when a dependency changes. The afterEach hook was removed from
the router.
2026-07-28 19:50:19 +02:00
jakob.scheidandGitea f4d8952c54 Merge pull request 'Feat(color-scheme): set the color scheme on the document body' (#149) from feature/remove-top-offset-on-mobile into main
Deploy on dev / Deploy on dev (push) Successful in 37s
Reviewed-on: #149
Reviewed-by: Jakob Gregory
2026-07-28 19:47:47 +02:00
jakob.scheid 2206b9952b feat(color-scheme): set color scheme on body
The color scheme is now set on the document body. The reactivity is
ensured by a watcher on the color scheme ref. The color CSS variables
are now set on the body.
2026-07-28 19:46:18 +02:00
8 changed files with 884 additions and 860 deletions
+26 -7
View File
@@ -18,21 +18,40 @@ limitations under the License.
import Navbar from './features/nav/components/Navbar.vue'; import Navbar from './features/nav/components/Navbar.vue';
import Footer from './features/footer/components/Footer.vue'; import Footer from './features/footer/components/Footer.vue';
import { updatePageTitle } from './router';
import { useColorScheme } from './features/colorScheme/composables/useColorScheme'; import { useColorScheme } from './features/colorScheme/composables/useColorScheme';
import { ref, provide, watch } from 'vue'; import { ref, provide, watch, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const { getColorScheme, updateColorScheme } = useColorScheme(); const { getColorScheme, updateColorScheme } = useColorScheme();
const colorScheme = ref(getColorScheme()); const colorScheme = ref(getColorScheme());
provide('colorScheme', colorScheme); provide('colorScheme', colorScheme);
watch(colorScheme, val => updateColorScheme(val)) watch(colorScheme, (newValue) => {
updateColorScheme(newValue);
document.body.style.setProperty(colorScheme, {
auto: 'normal',
dark: 'dark',
light: 'light'
});
if (newValue === 'dark') {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
if (newValue === 'auto') {
document.body.classList.add('color-scheme-auto');
} else {
document.body.classList.remove('color-scheme-auto');
}
});
watchEffect(() => updatePageTitle(route));
</script> </script>
<template> <template>
<div <div id="app-wrapper">
:style="{ colorScheme: colorScheme === 'auto' ? 'normal' : (colorScheme === 'dark' ? 'dark' : 'light')}"
:class="{ dark: colorScheme === 'dark', 'color-scheme-auto': colorScheme === 'auto' }"
id="app-wrapper"
>
<Navbar /> <Navbar />
<router-view class="main-content" /> <router-view class="main-content" />
@@ -39,7 +39,7 @@ vi.mock('@/features/icons/components/Icon.vue', () => ({
} }
})); }));
describe('LanguageSwitchButton.vue', () => { describe('LanguageSwitchButton', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
localStorage.clear(); localStorage.clear();
@@ -17,7 +17,8 @@ limitations under the License.
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { validateSettingsConfig, validateEntry, validateSelectionOptions, assertType, assertString } from '../settingsValidator'; import { validateSettingsConfig, validateEntry, validateSelectionOptions, assertType, assertString } from '../settingsValidator';
describe('validateSettingsConfig', () => { describe('settingsValidator', () => {
describe('validateSettingsConfig', () => {
test.for([ test.for([
{ raw: false, expected: false }, { raw: false, expected: false },
{ raw: true, expected: false }, { raw: true, expected: false },
@@ -527,9 +528,9 @@ describe('validateSettingsConfig', () => {
}; };
expect(result.valid).toBe(expected); expect(result.valid).toBe(expected);
}); });
}); });
describe('validateEntry', () => { describe('validateEntry', () => {
test.for([ test.for([
[{ name: 'enableFeature42', type: 'bool', i18n: 'feature.42.enable' }], [{ name: 'enableFeature42', type: 'bool', i18n: 'feature.42.enable' }],
[{ name: 'enableFeature42', type: 'bool', i18n: 'feature.42.enable', default: true }], [{ name: 'enableFeature42', type: 'bool', i18n: 'feature.42.enable', default: true }],
@@ -661,9 +662,9 @@ describe('validateEntry', () => {
])('throws an error for the entry %s', ([ entry ]) => { ])('throws an error for the entry %s', ([ entry ]) => {
expect(() => validateEntry(entry)).throws(Error); expect(() => validateEntry(entry)).throws(Error);
}); });
}); });
describe('validateSelectionOptions', () => { describe('validateSelectionOptions', () => {
test.for([ test.for([
[[{ name: 'test', i18n: 'test.label' }]], [[{ name: 'test', i18n: 'test.label' }]],
[[{ name: 'test', i18n: 'test.label' }, { name: 'test2', i18n: 'test2.label' }]], [[{ name: 'test', i18n: 'test.label' }, { name: 'test2', i18n: 'test2.label' }]],
@@ -687,9 +688,9 @@ describe('validateSelectionOptions', () => {
])('throws an error for the options %s', ([ options ]) => { ])('throws an error for the options %s', ([ options ]) => {
expect(() => validateSelectionOptions(options)).throws(Error); expect(() => validateSelectionOptions(options)).throws(Error);
}); });
}); });
describe('assertType', () => { describe('assertType', () => {
test.for([ test.for([
['bool'], ['bool'],
['number'], ['number'],
@@ -722,9 +723,9 @@ describe('assertType', () => {
])('throws an error for the value %s', ([ value ]) => { ])('throws an error for the value %s', ([ value ]) => {
expect(() => assertType(value)).throw(Error); expect(() => assertType(value)).throw(Error);
}); });
}); });
describe('assertString', () => { describe('assertString', () => {
test.for([ test.for([
['a'], ['a'],
['b'], ['b'],
@@ -755,4 +756,5 @@ describe('assertString', () => {
])('throws an error for the value %s', ([ value ]) => { ])('throws an error for the value %s', ([ value ]) => {
expect(() => assertString(value)).throws(Error); expect(() => assertString(value)).throws(Error);
}); });
});
}); });
+5 -6
View File
@@ -59,18 +59,17 @@ const router = createRouter({
routes routes
}); });
// set page title export const updatePageTitle = function updatePageTitle (route) {
router.afterEach(to => {
const title = const title =
typeof to.meta.title === 'function' typeof route.meta.title === 'function'
? to.meta.title(to) ? route.meta.title(route)
: to.meta.title; : route.meta.title;
if (title) { if (title) {
document.title = `${title} - Seekra`; document.title = `${title} - Seekra`;
} else { } else {
document.title = 'Seekra'; document.title = 'Seekra';
}; };
}); };
export default router; export default router;
+2
View File
@@ -15,6 +15,8 @@ limitations under the License.
*/ */
body { body {
background-color: var(--light-bg);
color: var(--dark);
margin: 0; margin: 0;
font-size: 16px; font-size: 16px;
} }
+3 -3
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#app-wrapper { body {
--primary-color-l-8: oklch(0.897 0.202 260); --primary-color-l-8: oklch(0.897 0.202 260);
--primary-color-l-7: oklch(0.847 0.202 260); --primary-color-l-7: oklch(0.847 0.202 260);
--primary-color-l-6: oklch(0.797 0.202 260); --primary-color-l-6: oklch(0.797 0.202 260);
@@ -87,7 +87,7 @@ limitations under the License.
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
#app-wrapper.color-scheme-auto { body.color-scheme-auto {
--dark-l-8: var(--white-d-8); --dark-l-8: var(--white-d-8);
--dark-l-7: var(--white-d-7); --dark-l-7: var(--white-d-7);
--dark-l-6: var(--white-d-6); --dark-l-6: var(--white-d-6);
@@ -121,7 +121,7 @@ limitations under the License.
} }
} }
#app-wrapper.dark { body.dark {
--dark-l-8: var(--white-d-8); --dark-l-8: var(--white-d-8);
--dark-l-7: var(--white-d-7); --dark-l-7: var(--white-d-7);
--dark-l-6: var(--white-d-6); --dark-l-6: var(--white-d-6);
+5 -3
View File
@@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { expect, test } from 'vitest'; import { describe, expect, test } from 'vitest';
import { ensureUnit } from '../cssDimensions'; import { ensureUnit } from '../cssDimensions';
test.for([ describe('cssDimensions', () => {
test.for([
{ dimension: null, expected: '0px' }, { dimension: null, expected: '0px' },
{ dimension: undefined, expected: '0px' }, { dimension: undefined, expected: '0px' },
@@ -117,6 +118,7 @@ test.for([
{ dimension: 'max(42px,5rem)', expected: 'max(42px,5rem)' }, { dimension: 'max(42px,5rem)', expected: 'max(42px,5rem)' },
{ dimension: 'clamp(42vh,23vw,13cap)', expected: 'clamp(42vh,23vw,13cap)' }, { dimension: 'clamp(42vh,23vw,13cap)', expected: 'clamp(42vh,23vw,13cap)' },
{ dimension: 'clamp( 42vh,23vw,13cap )', expected: 'clamp( 42vh,23vw,13cap )' } { dimension: 'clamp( 42vh,23vw,13cap )', expected: 'clamp( 42vh,23vw,13cap )' }
])('ensureUnit returns $expected with input $dimension', ({ dimension, expected }) => { ])('ensureUnit returns $expected with input $dimension', ({ dimension, expected }) => {
expect(ensureUnit(dimension)).toBe(expected); expect(ensureUnit(dimension)).toBe(expected);
});
}); });
+5 -5
View File
@@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { expect, test } from 'vitest'; import { describe, expect, test } from 'vitest';
import getCurrentLanguage from '../currentLanguage'; import getCurrentLanguage from '../currentLanguage';
const locales = [ describe('currentLanguage', () => {
test.for([
{ navigatorLanguage: 'en', localStorageLanguage: null, expected: 'en'}, { navigatorLanguage: 'en', localStorageLanguage: null, expected: 'en'},
{ navigatorLanguage: 'de', localStorageLanguage: null, expected: 'de'}, { navigatorLanguage: 'de', localStorageLanguage: null, expected: 'de'},
{ navigatorLanguage: 'fr', localStorageLanguage: null, expected: 'fr'}, { navigatorLanguage: 'fr', localStorageLanguage: null, expected: 'fr'},
@@ -45,9 +46,7 @@ const locales = [
{ navigatorLanguage: 'de-de', localStorageLanguage: 'en', expected: 'en'}, { navigatorLanguage: 'de-de', localStorageLanguage: 'en', expected: 'en'},
{ navigatorLanguage: 'zh-Hans-CN', localStorageLanguage: 'fr', expected: 'fr'}, { navigatorLanguage: 'zh-Hans-CN', localStorageLanguage: 'fr', expected: 'fr'},
{ navigatorLanguage: 'en-US-u-ca-gregory', localStorageLanguage: 'zh', expected: 'zh'} { navigatorLanguage: 'en-US-u-ca-gregory', localStorageLanguage: 'zh', expected: 'zh'}
]; ])('returns the language $expected (navigator: $navigatorLanguage; local storage: $localStorageLanguage)', ({ navigatorLanguage, localStorageLanguage, expected }) => {
test.for(locales)('returns the language $expected (navigator: $navigatorLanguage; local storage: $localStorageLanguage)', ({ navigatorLanguage, localStorageLanguage, expected }) => {
Object.defineProperty(navigator, 'language', { Object.defineProperty(navigator, 'language', {
value: navigatorLanguage, value: navigatorLanguage,
configurable: true configurable: true
@@ -58,4 +57,5 @@ test.for(locales)('returns the language $expected (navigator: $navigatorLanguage
}; };
expect(getCurrentLanguage()).toBe(expected); expect(getCurrentLanguage()).toBe(expected);
});
}); });