Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/app/components/jsx-helpers/raw-html.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ function activateScripts(el: HTMLElement) {
processOne();
}

type RawHTMLArgs = {
type RawHTMLArgs = ({
Tag?: string;
html?: TrustedHTML;
embed?: boolean;
} & React.HTMLAttributes<HTMLDivElement>;
href?: string;
} & React.HTMLAttributes<HTMLDivElement>);

export default function RawHTML({
Tag = 'div',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import './dropdown.scss';
// for ordinary website navigations, per
// https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/

export function MenuItem({label, url, local=undefined}) {
export function MenuItem({label, url, local = undefined}: {
label: string;
url: string;
local?: string;
}) {
const {innerWidth: _} = useWindowContext();
const urlPath = url.replace('/view-all', '');
const {pathname} = useLocation();
Expand All @@ -33,7 +37,10 @@ export function MenuItem({label, url, local=undefined}) {
);
}

function OptionalWrapper({isWrapper = true, children}) {
function OptionalWrapper({isWrapper, children}: {
isWrapper: boolean;
children?: React.ReactNode;
}) {
return isWrapper ? (
<div className="nav-menu-item dropdown">{children}</div>
) : (
Expand All @@ -48,9 +55,16 @@ export default function Dropdown({
children,
excludeWrapper = false,
navAnalytics
}: {
Tag?: React.ElementType;
className?: string;
label: string;
children?: React.ReactNode;
excludeWrapper?: boolean;
navAnalytics?: string;
}) {
const topRef = useRef();
const dropdownRef = useRef(null);
const topRef = useRef<HTMLAnchorElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const ddId = `ddId-${label}`;
const {
closeMenu, closeDesktopMenu, openMenu, openDesktopMenu
Expand Down Expand Up @@ -98,12 +112,19 @@ function DropdownController({
closeMenu,
openMenu,
label
}: {
ddId: string;
closeDesktopMenu: () => void;
topRef: React.RefObject<HTMLAnchorElement>;
closeMenu: () => void;
openMenu: (event: React.MouseEvent) => void;
label: string;
}) {
const {activeDropdown, prefix} = useDropdownContext();
const isOpen = activeDropdown === topRef;
const labelId = `${prefix}-${label}`;
const toggleMenu = React.useCallback(
(event) => {
(event: React.MouseEvent<HTMLAnchorElement>) => {
if (activeDropdown === topRef) {
event.preventDefault();
closeMenu();
Expand All @@ -114,8 +135,8 @@ function DropdownController({
[openMenu, closeMenu, activeDropdown, topRef]
);
const closeOnBlur = React.useCallback(
({currentTarget, relatedTarget}) => {
if (currentTarget.parentNode.contains(relatedTarget)) {
({currentTarget, relatedTarget}: React.FocusEvent<HTMLAnchorElement>) => {
if (currentTarget.parentNode?.contains(relatedTarget)) {
return;
}
closeDesktopMenu();
Expand Down Expand Up @@ -154,7 +175,13 @@ function DropdownController({
);
}

function DropdownContents({id, label, dropdownRef, navAnalytics, children}) {
function DropdownContents({id, label, dropdownRef, navAnalytics, children}: {
id: string;
label: string;
dropdownRef: React.RefObject<HTMLDivElement>;
navAnalytics?: string;
children?: React.ReactNode;
}) {
return (
<div className="dropdown-container">
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function useMenuControls({
topRef,
label
}: {
topRef: React.MutableRefObject<HTMLAnchorElement>;
topRef: React.MutableRefObject<HTMLAnchorElement | null>;
label: string;
}) {
const {setSubmenuLabel, setActiveDropdown} = useDropdownContext();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import useDropdownContext from '../../dropdown-context';
import {isMobileDisplay} from '~/helpers/device';
import { assertDefined } from '~/helpers/data';

function findNext(dropdownRef: React.MutableRefObject<HTMLDivElement>) {
function findNext(dropdownRef: React.MutableRefObject<HTMLDivElement | null>) {
const nextSib = document.activeElement?.nextElementSibling;

if (nextSib?.matches('a')) {
return nextSib as HTMLAnchorElement;
}
const targets = Array.from(dropdownRef.current.querySelectorAll('a'));
const targets = Array.from(assertDefined(dropdownRef.current?.querySelectorAll('a')));
const idx = targets.indexOf(document.activeElement as HTMLAnchorElement);
const nextIdx = (idx + 1) % targets.length;

return targets[nextIdx];
}

// eslint-disable-next-line complexity
function findPrev(
topRef: React.MutableRefObject<HTMLAnchorElement>,
dropdownRef: React.MutableRefObject<HTMLDivElement>
topRef: React.MutableRefObject<HTMLAnchorElement | null>,
dropdownRef: React.MutableRefObject<HTMLDivElement | null>
) {
const prevSib = document.activeElement?.previousElementSibling;

if (prevSib?.matches('a')) {
return prevSib as HTMLAnchorElement;
}
const targets = Array.from(dropdownRef.current.querySelectorAll('a'));
const targets = Array.from(assertDefined(dropdownRef.current?.querySelectorAll('a')));
const idx = targets.indexOf(document.activeElement as HTMLAnchorElement);

if (idx === 0) {
Expand All @@ -40,8 +42,8 @@ export default function useNavigateByKey({
closeMenu,
closeDesktopMenu
}: {
topRef: React.MutableRefObject<HTMLAnchorElement>;
dropdownRef: React.MutableRefObject<HTMLDivElement>;
topRef: React.MutableRefObject<HTMLAnchorElement | null>;
dropdownRef: React.MutableRefObject<HTMLDivElement | null>;
closeMenu: () => void;
closeDesktopMenu: () => void;
}) {
Expand Down Expand Up @@ -69,15 +71,15 @@ export default function useNavigateByKey({
case 'ArrowDown':
event.preventDefault();
if (document.activeElement === topRef.current) {
(dropdownRef.current.firstChild as HTMLAnchorElement)?.focus();
(dropdownRef.current?.firstChild as HTMLAnchorElement)?.focus();
} else {
findNext(dropdownRef).focus();
}
break;
case 'ArrowUp':
event.preventDefault();
if (document.activeElement !== topRef.current) {
findPrev(topRef, dropdownRef).focus();
findPrev(topRef, dropdownRef)?.focus();
}
break;
case 'Escape':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import {useLocation} from 'react-router-dom';
import useUserContext from '~/contexts/user';
import linkHelper from '~/helpers/link';
import Dropdown, {MenuItem} from '../dropdown/dropdown';
import type {WindowWithSettings} from '~/helpers/window-settings';
import {assertDefined} from '~/helpers/data';

const settings = window.SETTINGS;
const settings = (window as WindowWithSettings).SETTINGS;
const reqFacultyAccessLink = `${settings.accountHref}/i/signup/educator/cs_form`;
const profileLink = `${settings.accountHref}/profile`;

Expand All @@ -21,7 +23,7 @@ function AccountItem() {


export default function LoginMenuWithDropdown() {
const {userModel} = useUserContext();
const userModel = assertDefined(useUserContext().userModel);

// updates logoutLink
useLocation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,12 @@ import linkHelper from '~/helpers/link';
function LoginLink() {
// It's not used directly, but loginLink changes when it does
useLocation();
const addressHinkyQAIssue = React.useCallback(
(e) => {
if (e.defaultPrevented) {
e.defaultPrevented = false;
}
},
[]
);

return (
<li className="login-menu nav-menu-item rightmost">
<a
href={linkHelper.loginLink()} className="pardotTrackClick"
data-local="true" role="menuitem" onClick={addressHinkyQAIssue}
data-local="true" role="menuitem"
>
Log in
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,43 @@ import GiveButton from '../give-button/give-button';
import {treatSpaceOrEnterAsClick} from '~/helpers/events';
import './main-menu.scss';

function DropdownOrMenuItem({item}) {
type MenuItemData = {
name?: string;
label?: string;
partial_url?: string;
menu?: MenuItemData[];
};

function DropdownOrMenuItem({item}: {item: MenuItemData}) {
if (! item.name && ! item.label) {
return null;
}
if ('menu' in item) {
return (
<Dropdown
label={item.name}
label={item.name!}
navAnalytics={`Main Menu (${item.name})`}
>
<MenusFromStructure structure={item.menu} />
<MenusFromStructure structure={item.menu!} />
</Dropdown>
);
}

return <MenuItem label={item.label} url={item.partial_url} />;
return <MenuItem label={item.label!} url={item.partial_url!} />;
}

function MenusFromStructure({structure}) {
function MenusFromStructure({structure}: {structure: MenuItemData[]}) {
return (
<React.Fragment>
{structure.map((item) => (
<DropdownOrMenuItem key={item.label} item={item} />
{structure.map((item, index) => (
<DropdownOrMenuItem key={item.label || index} item={item} />
))}
</React.Fragment>
);
}

function MenusFromCMS() {
const structure = useDataFromSlug('oxmenus');
const structure = useDataFromSlug('oxmenus') as MenuItemData[] | undefined;

if (!structure) {
return null;
Expand Down Expand Up @@ -74,8 +81,8 @@ function SubjectsMenu() {
navAnalytics="Main Menu (Subjects)"
>
{categories
.filter((obj) => obj.html !== 'K12')
.map((obj) => (
.filter((obj: {html: string; value: string}) => obj.html !== 'K12')
.map((obj: {html: string; value: string}) => (
<MenuItem
key={obj.value}
label={obj.html}
Expand Down Expand Up @@ -104,23 +111,24 @@ function SubjectsMenu() {
);
}

function navigateWithArrows(event) {
// eslint-disable-next-line complexity
function navigateWithArrows(event: React.KeyboardEvent<HTMLUListElement>) {
switch (event.key) {
case 'ArrowRight':
event.preventDefault();
event.stopPropagation();
event.target
(event.target as HTMLElement)
.closest('li')
.nextElementSibling?.querySelector('a')
.focus();
?.nextElementSibling?.querySelector('a')
?.focus();
break;
case 'ArrowLeft':
event.preventDefault();
event.stopPropagation();
event.target
(event.target as HTMLElement)
.closest('li')
.previousElementSibling?.querySelector('a')
.focus();
?.previousElementSibling?.querySelector('a')
?.focus();
break;
default:
break;
Expand Down
Loading