Skip to content

Commit

Permalink
Refactor component IDs and add accessibility improvements
Browse files Browse the repository at this point in the history
List of changes:

1. Updated component IDs across multiple files for consistency and improved accessibility:
* SearchBar.vue
* DropdownItemsLayout.vue
* HeaderMobile.vue
* MenuMobileNavBar.vue
* MenuMobileNavigationDropdown.vue
* SidebarLeftFooter.vue
* SidebarLeftIndex.vue
* SidebarLeftMainSectionSelectors.vue

2. Added new component objects for improved testing and accessibility:
* MobileNav.ts
* Navigation.ts
* OrganizationMenu.ts
* EventMenu.ts

3. Updated SearchBar.ts with new methods for opening and closing search input.
4. Modified HeaderWebsite.ts to use the new SearchBar component.
5. Updated package.json to include axe-core and axe-html-reporter for accessibility testing.
  • Loading branch information
github-actions[bot] committed Sep 25, 2024
1 parent b2ef3fb commit 4e2f97d
Show file tree
Hide file tree
Showing 26 changed files with 869 additions and 288 deletions.
5 changes: 4 additions & 1 deletion frontend/components/SearchBar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div
v-if="location == SearchBarLocation.SIDEBAR"
id="search"
class="elem-shadow-sm mx-2 flex grow select-none items-center justify-between rounded-md bg-light-layer-2 py-1 pl-[12px] text-left text-light-distinct-text transition duration-200 focus-within:mb-[-3px] focus-within:border-2 focus-within:border-light-link-text dark:bg-dark-layer-2 dark:text-dark-distinct-text dark:focus-within:border-dark-link-text"
>
<div class="flex items-center space-x-2 pl-1">
Expand Down Expand Up @@ -69,10 +70,12 @@
</div>
<div
v-else
id="search"
class="relative inline-flex select-none items-center space-x-2 rounded-md border border-light-distinct-text bg-light-layer-2 py-1 pl-[12px] pr-[10px] text-left text-light-distinct-text focus-within:border-2 focus-within:border-light-cta-orange dark:border-dark-distinct-text dark:bg-dark-layer-2 dark:text-dark-distinct-text dark:focus-within:border-dark-cta-orange"
>
<Icon
@click="emit('on-search-toggle')"
id="search-toggle"
class="my-1 h-4 w-4 flex-shrink-0"
:name="expanded ? `${IconMap.X_LG}` : `${IconMap.SEARCH}`"
size="1em"
Expand All @@ -81,7 +84,7 @@
$t("_global.search")
}}</label>
<input
id="expanded-search-input"
id="input-search"
class="bg-transparent focus:outline-none"
:class="{ hidden: !expanded }"
type="text"
Expand Down
26 changes: 15 additions & 11 deletions frontend/components/dropdown/DropdownItemsLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,21 @@ const isSideMenu = computed(() => {
const getSelectorId = (label: string) => {
const idMap: Record<string, string> = {
help: 'sidebar-left-help',
doc: 'sidebar-left-docs',
legal: 'sidebar-left-legal',
profile: 'sidebar-left-profile',
events: 'sidebar-left-events',
orgs: 'sidebar-left-organizations',
notifications: 'sidebar-left-notifications',
settings: 'sidebar-left-settings',
'sign_out': 'sidebar-left-sign-out',
'sign_up': 'sidebar-left-sign-up',
'sign_in': 'sidebar-left-sign-in'
help: 'help',
doc: 'docs',
legal: 'legal',
profile: 'profile',
events: 'your-events',
orgs: 'your-organizations',
notifications: 'notifications',
settings: 'settings',
sign_out: 'sign-out',
sign_up: 'sign-up',
sign_in: 'sign-in',
new_event: 'create-new-event',
new_organization: 'create-new-organization',
new_group: 'create-new-group',
new_resource: 'create-new-resource'
};
const key = Object.keys(idMap).find(k => label.includes(k));
Expand Down
4 changes: 4 additions & 0 deletions frontend/components/header/HeaderMobile.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<header
v-if="!aboveMediumBP"
id="mobile-header"
ref="header"
class="sticky top-0 z-50 h-12 w-full bg-light-layer-2 drop-shadow-md duration-500 dark:bg-dark-layer-2"
>
Expand Down Expand Up @@ -29,15 +30,18 @@
/>
<DropdownCreate
v-if="userIsSignedIn"
id="create"
class="w-full"
:location="DropdownLocation.SIDE_MENU"
/>
<DropdownInfo
class="w-full"
id="info"
:location="DropdownLocation.SIDE_MENU"
/>
<DropdownUserOptions
class="w-full"
id="user-options"
:location="DropdownLocation.SIDE_MENU"
:userIsSignedIn="userIsSignedIn"
/>
Expand Down
12 changes: 12 additions & 0 deletions frontend/components/menu/mobile/MenuMobileNavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<ul class="flex">
<li v-for="(item, index) in menuItems" :key="index" class="flex-1">
<MenuMobileSelector
:id="getSelectorId(item.label)"
class="rounded-none"
:label="item.label"
:routeURL="item.routeURL"
Expand All @@ -23,4 +24,15 @@
const isActive = (routeURL: string) => {
return isRouteActive(routeURL);
};
const getSelectorId = (label: string) => {
const idMap: Record<string, string> = {
home: 'home',
events: 'events',
organizations: 'organizations',
};
const key = Object.keys(idMap).find(k => label.includes(k));
return key ? idMap[key] : undefined;
};
</script>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div
v-if="selectedMenuItem"
id="submenu"
class="fixed z-20 h-10 w-full bg-light-menu-selection dark:bg-dark-menu-selection"
>
<Listbox v-model="selectedMenuItem">
Expand Down Expand Up @@ -40,6 +41,7 @@
>
<NuxtLink @click="handleItemClick(menuEntry)">
<li
:id="(sidebarType === SidebarType.ORGANIZATION_PAGE ? 'org-' : 'event-') + menuEntry.label.split('.').pop()"
class="relative flex cursor-default select-none items-center py-2 pl-5 align-middle"
:class="{
'bg-light-layer-2 fill-light-text text-light-text dark:bg-dark-section-div dark:fill-dark-text dark:text-dark-text':
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/sidebar/left/SidebarLeftFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
>
<DropdownCreate
v-if="userIsSignedIn"
id="sidebar-left-create"
id="create"
class="w-full"
:location="DropdownLocation.SIDE_LEFT_MENU"
/>
<DropdownInfo
id="sidebar-left-info"
id="info"
class="w-full"
:location="DropdownLocation.SIDE_LEFT_MENU"
/>
<DropdownUserOptions
id="sidebar-left-user-options"
id="user-options"
class="w-full"
:location="DropdownLocation.SIDE_LEFT_MENU"
:userIsSignedIn="userIsSignedIn"
Expand Down
5 changes: 5 additions & 0 deletions frontend/components/sidebar/left/SidebarLeftIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
/>
</div>
<ul
id="submenu"
class="mb-1 flex w-full flex-col px-1"
:class="{
'mt-4':
Expand All @@ -74,6 +75,10 @@
: menuEntriesState.eventEntry.value"
>
<SidebarLeftSelector
:id="
(sidebarType === SidebarType.ORGANIZATION_PAGE ? 'org-' : 'event-')
+ menuEntry.label.split('.').pop()
"
:label="menuEntry.label"
:routeURL="menuEntry.routeURL"
:iconURL="menuEntry.iconURL"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const isActive = (routeURL: string) => {
const getSelectorId = (label: string) => {
const idMap: Record<string, string> = {
organizations: 'sidebar-left-organizations',
events: 'sidebar-left-events'
organizations: 'organizations',
events: 'events'
};
const key = Object.keys(idMap).find(k => label.includes(k));
Expand Down
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"@types/zxcvbn": "4.4.4",
"@vueuse/core": "10.9.0",
"@vueuse/nuxt": "10.9.0",
"axe-core": "^4.10.0",
"axe-html-reporter": "^2.2.11",
"cross-env": "7.0.3",
"eslint": "^8.0.0",
"eslint-plugin-vue": "9.24.1",
Expand All @@ -42,7 +44,7 @@
"vue-tsc": "2.0.11"
},
"dependencies": {
"@axe-core/playwright": "4.9.1",
"@axe-core/playwright": "4.10.0",
"@maplibre/maplibre-gl-directions": "0.7.0",
"@nuxtjs/axios": "5.13.6",
"@nuxtjs/plausible": "1.0.0",
Expand Down
6 changes: 5 additions & 1 deletion frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ export default defineConfig({
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters. */
reporter: "html",
reporter: [
['html'],
['list'],
['./tests/utils/axe-reporter.ts']
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
Expand Down
81 changes: 81 additions & 0 deletions frontend/tests/component-objects/EventMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Page, Locator } from "@playwright/test";
import { PageObjectBase } from "../utils/PageObjectBase";

const locators = {
TOGGLE: "#submenu",
ABOUT: "#event-about",
TEAM: "#event-team",
RESOURCES: "#event-resources",
TASKS: "#event-tasks",
DISCUSSION: "#event-discussion",
SETTINGS: "#event-settings",
};

export class EventMenu extends PageObjectBase {
constructor(page: Page) {
super(page, locators);
}

get toggle(): Locator {
return this.getLocator("TOGGLE");
}

get about(): Locator {
return this.getLocator("ABOUT");
}

get team(): Locator {
return this.getLocator("TEAM");
}

get resources(): Locator {
return this.getLocator("RESOURCES");
}

get tasks(): Locator {
return this.getLocator("TASKS");
}

get discussion(): Locator {
return this.getLocator("DISCUSSION");
}

get settings(): Locator {
return this.getLocator("SETTINGS");
}

async open(): Promise<void> {
if (!(await this.isOpen())) {
await this.toggle.click();
}
}

async close(): Promise<void> {
if (await this.isOpen()) {
await this.toggle.click();
}
}

async isOpen(): Promise<boolean> {
return await this.toggle.getAttribute("aria-expanded") === "true";
}

async isVisible(): Promise<boolean> {
return await this.toggle.isVisible();
}

async isOptionsVisible(): Promise<boolean> {
return await this.options.isVisible();
}

async getActiveSelectedOption(): Promise<string> {
const selector = await this.isMobile()
? "[data-headlessui-state='active selected']"
: ".style-menu-option-cta";
return (await this.options.locator(selector).textContent()) || "";
}

async selectOption(option: Locator): Promise<void> {
await option.click();
}
}
42 changes: 13 additions & 29 deletions frontend/tests/component-objects/HeaderWebsite.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { Page, Locator } from "@playwright/test";
import { PageObjectBase } from "../utils/PageObjectBase";
import { SearchBar } from "./SearchBar";

const locators = {
MOBILE_HEADER: "#mobile-header",
DESKTOP_HEADER: "#desktop-header",
ROADMAP_BUTTON: "#desktop-header #btn-roadmap",
GET_IN_TOUCH_BUTTON: "#btn-get-in-touch-large:visible, #btn-get-in-touch-medium:visible",
HAMBURGER: "#sidebar-right-hamburger:visible",
SIDEBAR: "#drawer-navigation",
THEME_DROPDOWN: ".dropdown-theme:visible",
SELECTED_LANGUAGE: ".dropdown-language:visible .selected-option",
LANGUAGE_DROPDOWN: ".dropdown-language:visible",
Expand All @@ -16,8 +15,11 @@ const locators = {
};

export class HeaderWebsite extends PageObjectBase {
readonly searchBar: SearchBar;

constructor(page: Page) {
super(page, locators);
this.searchBar = new SearchBar(page);
}

get mobileHeader(): Locator {
Expand All @@ -40,41 +42,18 @@ export class HeaderWebsite extends PageObjectBase {
return this.getLocator("GET_IN_TOUCH_BUTTON");
}

get hamburger(): Locator {
return this.getLocator("HAMBURGER");
}

get sidebar(): Locator {
return this.getLocator("SIDEBAR");
}

async openSidebar(): Promise<void> {
if (!(await this.sidebar.isVisible())) {
await this.hamburger.click();
}
}

async closeSidebar(): Promise<void> {
if (await this.sidebar.isVisible()) {
await this.hamburger.click();
}
}

async selectDropdownOption(
dropdown: Locator,
optionText: string
): Promise<void> {
await this.page.locator(`text=${optionText}`).click();
await dropdown.locator(`text=${optionText}`).click();
}

get themeDropdown(): Locator {
return this.getLocator("THEME_DROPDOWN");
}

async openThemeDropdown(): Promise<void> {
if (await this.isMobile()) {
await this.openSidebar();
}
await this.themeDropdown.click();
}

Expand All @@ -88,9 +67,6 @@ export class HeaderWebsite extends PageObjectBase {
}

async openLanguageDropdown(): Promise<void> {
if (await this.isMobile()) {
await this.openSidebar();
}
const isDropdownOpen = await this.getLocator("LANGUAGE_MENU").isVisible();
if (!isDropdownOpen) {
await this.languageDropdown.click();
Expand Down Expand Up @@ -127,4 +103,12 @@ export class HeaderWebsite extends PageObjectBase {
}
return undefined;
}

async searchFor(text: string): Promise<void> {
await this.searchBar.fillSearchInput(text);
}

async isSearchBarVisible(): Promise<boolean> {
return this.searchBar.isSearchInputVisible();
}
}
Loading

0 comments on commit 4e2f97d

Please sign in to comment.