Skip to content

Commit

Permalink
Feature/#716 increase contrast (#863)
Browse files Browse the repository at this point in the history
* #716: add set increase contrast logic in app

* #716: add custom button for high contrast

* #716: increase opacity of submit form button when state hover or focus

* #716: increase opacity of submit form button when state hover or focus

* #716: MR feedback

* #716 update package-lock.json jsons
  • Loading branch information
StephanStrehlerCGI authored Nov 3, 2023
1 parent fbf5537 commit 73acd17
Show file tree
Hide file tree
Showing 17 changed files with 424 additions and 311 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions digiwf-apps/packages/apps/digiwf-tasklist/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ declare module '@vue/runtime-core' {
BaseForm: typeof import('./src/components/form/BaseForm.vue')['default']
BaseLdapInput: typeof import('./src/components/form/BaseLdapInput.vue')['default']
BaseMarkdownOutput: typeof import('./src/components/form/BaseMarkdownOutput.vue')['default']
ContrastModeSelection: typeof import('./src/components/UI/ContrastModeSelection.vue')['default']
CsvOutput: typeof import('./src/components/form/CsvOutput.vue')['default']
DwfButton: typeof import('./src/components/common/DwfButton.vue')['default']
FileOutput: typeof import('./src/components/form/FileOutput.vue')['default']
GroupTaskItem: typeof import('./src/components/task/GroupTaskItem.vue')['default']
HighContrastIcon: typeof import('./src/components/UI/icons/HighContrastIcon.vue')['default']
ImageOutput: typeof import('./src/components/form/ImageOutput.vue')['default']
LoadingFab: typeof import('./src/components/UI/LoadingFab.vue')['default']
PdfOutput: typeof import('./src/components/form/PdfOutput.vue')['default']
Expand Down Expand Up @@ -88,6 +91,7 @@ declare module '@vue/runtime-core' {
VStepper: typeof import('vuetify/lib')['VStepper']
VStepperHeader: typeof import('vuetify/lib')['VStepperHeader']
VStepperStep: typeof import('vuetify/lib')['VStepperStep']
VSwitch: typeof import('vuetify/lib')['VSwitch']
VTextField: typeof import('vuetify/lib')['VTextField']
VToolbarTitle: typeof import('vuetify/lib')['VToolbarTitle']
VTooltip: typeof import('vuetify/lib')['VTooltip']
Expand Down
429 changes: 160 additions & 269 deletions digiwf-apps/packages/apps/digiwf-tasklist/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions digiwf-apps/packages/apps/digiwf-tasklist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"test": "jest src --coverage"
},
"dependencies": {
"@jamescoyle/vue-icon": "^0.1.2",
"@mdi/js": "^7.3.67",
"@muenchen/digiwf-date-input": "^1.2.0",
"@muenchen/digiwf-engine-api-internal": "^1.2.0",
"@muenchen/digiwf-form-renderer": "^1.2.0",
Expand Down
31 changes: 27 additions & 4 deletions digiwf-apps/packages/apps/digiwf-tasklist/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,31 @@
<v-spacer/>
{{ username }}

<v-icon class="white--text">
mdi-account-circle
</v-icon>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn
aria-label="Avatar Icon Button"
text
fab
v-bind="attrs"
v-on="on"
>
<v-icon
aria-label="Avatar Icon"
class="white--text" >
mdi-account-circle
</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item>
<v-list-item-title>
<contrast-mode-selection/>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>


</v-app-bar>

Expand Down Expand Up @@ -169,9 +191,10 @@ import {InfoTO, ServiceInstanceTO, UserTO,} from "@muenchen/digiwf-engine-api-in
import AppMenuList from "./components/UI/appMenu/AppMenuList.vue";
import {apiGatewayUrl} from "./utils/envVariables";
import {queryClient} from "./middleware/queryClient";
import ContrastModeSelection from "./components/UI/ContrastModeSelection.vue";
@Component({
components: {AppMenuList}
components: {ContrastModeSelection, AppMenuList}
})
export default class App extends Vue {
drawer = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script lang="ts">
import {defineComponent} from "vue";
import {useTheme} from "../../plugins/vuetify";
import {useAccessibility} from "../../store/modules/accessibility";
import HighContrastIcon from "./icons/HighContrastIcon.vue";
export default defineComponent({
components: {HighContrastIcon},
setup: () => {
const theme = useTheme();
const {isHighContrastModeEnabled, setHighContrastModeEnabled} = useAccessibility();
const changeMode = () => {
const isEnabled = isHighContrastModeEnabled();
if (isEnabled) {
theme.deactivateContrastMode();
} else {
theme.activateContrastMode();
}
setHighContrastModeEnabled(!isEnabled);
};
return {
onClick: changeMode,
isHighContrastModeEnabled
};
}
});
</script>

<template>
<v-switch
:aria-label="isHighContrastModeEnabled() ? 'Hohen Kontrast deaktivieren' : 'Hohen Kontrast aktivieren'"
:value="isHighContrastModeEnabled()"
label="Hohen Kontrast"
@click.stop="onClick"
>
<template v-slot:label>
<high-contrast-icon /> Hoher Kontrast
</template>
</v-switch>
</template>

<style scoped>
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<svg-icon
type="mdi"
:path="path"/>
</template>


<script>
import SvgIcon from "@jamescoyle/vue-icon";
import {mdiContrastBox } from "@mdi/js";
export default {
name: "HighContrastIcon",
components: {
SvgIcon
},
data() {
return {
path: mdiContrastBox ,
};
}
};
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup lang="ts">
import {useAccessibility} from "../../store/modules/accessibility";
defineProps({
// eslint-disable-next-line vue/prop-name-casing
"aria-label": {
type: String,
required: true
}
});
defineEmits(["click"]);
const isHighContrastModeEnabled = useAccessibility().isHighContrastModeEnabled;
</script>

<template>
<v-btn
:aria-label="$props['aria-label']"
:class="(isHighContrastModeEnabled()) ? 'high-contrast' : ''"
text
color="primary"
large
@click="$emit('click')"
>
<slot></slot>
</v-btn>
</template>

<style scoped>
.high-contrast:focus, .high-contrast:hover {
border: 1px solid;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<v-flex class="d-flex" style="width: 100%">
<v-spacer/>
<v-btn
class="mt-5"
class="mt-5 form-submit-button"
color="primary"
:disabled="isCompleting || readonly"
@click="complete">
Expand Down Expand Up @@ -85,3 +85,9 @@ export default class AppJsonForm extends Vue {
}
</script>

<style scoped>
.form-submit-button:focus, .form-submit-button:hover {
opacity: 0.6; /* first fix for increasing contrast. User feedback is required */
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@
<sort-by-select/>
</v-flex>
<div class="d-flex align-center">
<v-btn
<dwf-button
aria-label="Aufgaben aktualisieren"
text
color="primary"
large
@click="$emit('loadTasks')"
>
<div style="min-width: 30px">
Expand All @@ -41,7 +38,8 @@
<v-icon v-else> mdi-refresh</v-icon>
</div>
Aktualisieren
</v-btn>

</dwf-button>
</div>
</v-flex>
<v-flex v-if="errorMessage">
Expand Down Expand Up @@ -97,9 +95,10 @@ import SearchField from "../common/SearchField.vue";
import {HumanTask} from "../../middleware/tasks/tasksModels";
import {PropType} from "vue";
import SortBySelect from "../common/SortBySelect.vue";
import DwfButton from "../common/DwfButton.vue";
export default {
components: {SortBySelect, SearchField, AppToast},
components: {DwfButton, SortBySelect, SearchField, AppToast},
props: {
filter: {
type: String,
Expand Down
54 changes: 43 additions & 11 deletions digiwf-apps/packages/apps/digiwf-tasklist/src/plugins/vuetify.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,54 @@
import '@mdi/font/css/materialdesignicons.css';
import Vue from 'vue';
import Vuetify from 'vuetify';
import "@mdi/font/css/materialdesignicons.css";
import Vue, {getCurrentInstance} from "vue";
import Vuetify from "vuetify";
import {VuetifyThemeVariant} from "vuetify/types/services/theme";
import store from "../store";

Vue.use(Vuetify);

export const lightTheme: Partial<VuetifyThemeVariant> = {
primary: '#ff7c02',
secondary: '#333333',
accent: '#7BA4D9',
success: '#69BE28',
error: '#FF0000',
};

export const highContrastTheme: Partial<VuetifyThemeVariant> = {
...lightTheme,
};

// https://medium.com/@jogarcia/vuetify-multiple-themes-c580f41ece65
const theme = {
themes: {
light: {
primary: '#ff7c02',
secondary: '#333333',
accent: '#7BA4D9',
success: '#69BE28',
error: '#FF0000',
},
light: store.getters["accessibility/isHighContrastModeEnabled"] ? highContrastTheme : lightTheme,
},
options: {customProperties: true}, // enable css vars
};

export default new Vuetify({
const vuetify = new Vuetify({
theme: theme
});

export const useVuetify = () => {
const instance = getCurrentInstance();
if (!instance) {
throw new Error("useVuetify should be called in setup().");
}
return instance.proxy.$vuetify;
};

export const useTheme = () => {
const vuetify = useVuetify();
return {
currentTheme: vuetify.theme.currentTheme,
activateContrastMode: () => {
vuetify.theme.themes.light = highContrastTheme as any;
},
deactivateContrastMode: () => {
vuetify.theme.themes.light = lightTheme as any;
},
};
};

export default vuetify;
6 changes: 4 additions & 2 deletions digiwf-apps/packages/apps/digiwf-tasklist/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import user, {UserState} from './modules/user';
import menu, {MenuState} from "../store/modules/menu";
import info, {InfoState} from "../store/modules/info";
import {filters, FilterState} from "./modules/filters";
import {accessibility} from "./modules/accessibility";

Vue.use(Vuex);

Expand All @@ -19,14 +20,15 @@ export interface RootState {

const vuexLocal = new VuexPersistence<RootState>({
storage: window.localStorage,
modules: ["filters"]
modules: ["filters", "accessibility"]
});
export const Vuexstore = new Vuex.Store<RootState>({
modules: {
user,
menu,
info,
filters
filters,
accessibility
},
strict: debug,
plugins: [vuexLocal.plugin]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {accessibility, AccessibilityState} from "./accessibility";

describe("accessibility", () => {
describe("getters:isHighContrastModeEnabled", () => {
it("should return current value if high contrast mode is set to false", () => {
const state: AccessibilityState = {
highContrastModeEnabled: false,
};
const result = accessibility.getters.isHighContrastModeEnabled(state);
expect(result).toBeFalsy();

});
it("should return current value if high contrast mode is set to true", () => {
const state: AccessibilityState = {
highContrastModeEnabled: true,
};
const result = accessibility.getters.isHighContrastModeEnabled(state);
expect(result).toBeTruthy();
});
it("should return default value false if no value is set", () => {
const state: AccessibilityState = {
highContrastModeEnabled: undefined,
} as any; // so that we can set value to undefined
const result = accessibility.getters.isHighContrastModeEnabled(state);
expect(result).toBeFalsy();
});
});
});
Loading

0 comments on commit 73acd17

Please sign in to comment.