Author SHA1 Message Date
jakob.scheidandGitea a17de3f132 test(settings): add test cases for empty setting value return value 2026-08-05 20:47:11 +02:00
jakob.scheidandGitea 1959be3b49 feat(settings): make setting value function synchronous
Added a store to cache the settings configuration. The getSetting
function from useSettings is now synchronous and uses this cache.
The settings cache is set when loading the settings initially
(asnychronously).
2026-08-05 20:47:11 +02:00
jakob.scheidandGitea 229087d3e6 test(settings): add tests for useSettings composable
Added some tests for the getSetting function from the useSettings
composable.
2026-08-05 20:47:11 +02:00
jakob.scheidandGitea c82a113b4e feat(settings): add composable for settings values
Added the composable useSettings that provides the function getSetting.
This function returns the set value of the setting, or otherwise the
default value. If there is no default value, it returns undefined.
2026-08-05 20:47:11 +02:00
8 changed files with 10 additions and 119 deletions
+1 -2
View File
@@ -26,7 +26,7 @@ import { useRoute } from 'vue-router';
const route = useRoute();
const { getColorScheme, updateColorScheme } = useColorScheme();
const colorScheme = ref('auto');
const colorScheme = ref(getColorScheme());
provide('colorScheme', colorScheme);
watch(colorScheme, (newValue) => {
updateColorScheme(newValue);
@@ -46,7 +46,6 @@ watch(colorScheme, (newValue) => {
document.body.classList.remove('color-scheme-auto');
}
});
colorScheme.value = getColorScheme();
watchEffect(() => updatePageTitle(route));
</script>
@@ -54,7 +54,6 @@ const copyrightPeriod =
.footer-segment {
display: flex;
justify-content: center;
align-items: center;
gap: 32px;
padding: var(--padding-y);
background-color: var(--light-bg);
+3 -11
View File
@@ -16,7 +16,7 @@ limitations under the License.
<script setup>
import { useSettingsStore } from '../stores/settingsStore';
import { ref, useId, watch } from 'vue';
import { useId } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
@@ -35,16 +35,8 @@ const switchId = useId();
const store = useSettingsStore();
const enabled = ref(null);
watch(store, (newStore) => {
enabled.value = newStore.get(props.path);
});
// set value after registering watcher to avoid immediate value change
enabled.value = store.get(props.path) ?? props.setting.default;
const toggle = function toggle () {
enabled.value = !enabled.value;
store.set(props.path, enabled.value);
store.set(props.path, !store.get(props.path));
};
</script>
@@ -55,7 +47,7 @@ const toggle = function toggle () {
</label>
<div
class="switch-wrapper"
:class="{ enabled }"
:class="{ enabled: store.get(props.path) }"
:title="t('settings.switch.title')"
:aria-label="t('settings.switch.ariaLabel')"
:aria-labelledby="labelId"
@@ -20,23 +20,17 @@ import { useSettingsStore } from '../../stores/settingsStore.js';
import { expect, describe, test } from 'vitest';
import { nextTick } from 'vue';
const getWrapper = function getWrapper ({
i18n = 'switch1',
defaultValue = undefined,
translations = {},
piniaOptions = {}
} = {}) {
const getWrapper = function getWrapper ({ i18n = 'switch1', translations = {} } = {}) {
return mountComponent(Switch, {
attrs: {
setting: {
type: 'bool',
name: 'switch',
i18n,
default: defaultValue
i18n
},
path: 'switch'
}
}, translations, [], piniaOptions);
}, translations);
};
describe('Switch', () => {
@@ -59,42 +53,6 @@ describe('Switch', () => {
expect(switchElementClasses2).not.toContain('enabled');
});
test('shows default value', () => {
const wrapper = getWrapper({
defaultValue: true
});
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeTruthy();
});
test('shows stored value instead of default value', () => {
const wrapper = getWrapper({
defaultValue: true,
piniaOptions: {
setupStores: () => {
const store = useSettingsStore();
store.set('switch', false);
}
}
});
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeFalsy();
});
test('reacts on store change', async () => {
const wrapper = getWrapper();
const store = useSettingsStore();
const switchElement = wrapper.find('.switch-wrapper');
expect(switchElement.classes('enabled')).toBeFalsy();
store.set('switch', true);
await nextTick();
expect(switchElement.classes('enabled')).toBeTruthy();
});
test('toggles value in store', async () => {
const wrapper = getWrapper();
+2 -29
View File
@@ -1,30 +1,3 @@
{
"contents": [
{
"name": "appearance",
"i18n": "settings.settings.appearance.title",
"content": [
{
"name": "colorScheme",
"type": "selection",
"i18n": "settings.settings.appearance.contents.colorScheme.title",
"default": "auto",
"options": [
{
"name": "auto",
"i18n": "settings.settings.appearance.contents.colorScheme.options.auto"
},
{
"name": "light",
"i18n": "settings.settings.appearance.contents.colorScheme.options.light"
},
{
"name": "dark",
"i18n": "settings.settings.appearance.contents.colorScheme.options.dark"
}
]
}
]
}
]
}
"contents": []
}
+1 -1
View File
@@ -36,7 +36,7 @@ const toggleSidebar = function toggleSidebar () {
<div class="sidebar-controls">
<SidebarExpandButton @click="toggleSidebar" class="sidebar-expand-button" />
</div>
<div class="sidebar-content" v-show="props.expanded">
<div class="sidebar-content" v-if="props.expanded">
<slot />
</div>
</nav>
-15
View File
@@ -60,21 +60,6 @@
"option": {
"ariaLabel": "Wählt die Option {option} aus"
}
},
"settings": {
"appearance": {
"title": "Erscheinungsbild",
"contents": {
"colorScheme": {
"title": "Farbschema",
"options": {
"auto": "An Systemfarbschema anpassen",
"light": "Hell",
"dark": "Dunkel"
}
}
}
}
}
}
}
-15
View File
@@ -60,21 +60,6 @@
"option": {
"ariaLabel": "Selects option {option}"
}
},
"settings": {
"appearance": {
"title": "Appearance",
"contents": {
"colorScheme": {
"title": "Color Scheme",
"options": {
"auto": "Adapt to system color scheme",
"light": "Light",
"dark": "Dark"
}
}
}
}
}
}
}