Skip to content
Draft
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
4 changes: 3 additions & 1 deletion src/app/components/jsx-helpers/raw-html.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ type RawHTMLArgs = {
Tag?: string;
html?: TrustedHTML;
embed?: boolean;
} & React.HTMLAttributes<HTMLDivElement>;
} & React.HTMLAttributes<HTMLDivElement> & {
href?: string;
};

export default function RawHTML({
Tag = 'div',
Expand Down
2 changes: 0 additions & 2 deletions src/app/layouts/default/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import useMainClassContext, {
} from '~/contexts/main-class';
import useLanguageContext from '~/contexts/language';
import ReactModal from 'react-modal';
import Welcome from './welcome/welcome';
import TakeoverDialog from './takeover-dialog/takeover-dialog';
import cn from 'classnames';
import './default.scss';
Expand Down Expand Up @@ -53,7 +52,6 @@ function Main({children}: React.PropsWithChildren<object>) {
ref={ref}
tabIndex={-1}
>
<Welcome />
<TakeoverDialog />
{children}
</div>
Expand Down
4 changes: 1 addition & 3 deletions src/app/layouts/default/footer/copyright.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ type Props = {
}

export default function Copyright({copyright, apStatement}: Props) {
const updatedCopyright = copyright
? copyright.replace(/-\d+/, `-${new Date().getFullYear()}`)
: copyright;
const updatedCopyright = copyright?.replace(/-\d+/, `-${new Date().getFullYear()}`);

return (
<React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import JITLoad from '~/helpers/jit-load';
import StickyNote from './sticky-note/sticky-note';
import {useStickyData} from '../shared';
import Menus from './menus/menus';
import './header.scss';
Expand All @@ -9,7 +9,7 @@ export default function Header() {

return (
<div className="page-header">
<JITLoad importFn={() => import('./sticky-note/sticky-note.js')} stickyData={stickyData} />
<StickyNote stickyData={stickyData} />
<Menus />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/layouts/default/header/menus/dropdown-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {useState} from 'react';

function useContextValue({prefix} = {prefix: 'menulabel'}) {
const [activeDropdown, setActiveDropdown] = useState<
React.MutableRefObject<HTMLAnchorElement> | Record<string, never>
React.MutableRefObject<HTMLAnchorElement | null> | Record<string, never>
>({});
const [submenuLabel, setSubmenuLabel] = useState<string>();

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,24 +37,36 @@ export function MenuItem({label, url, local=undefined}) {
);
}

function OptionalWrapper({isWrapper = true, children}) {
function OptionalWrapper({isWrapper = true, children}: {
isWrapper?: boolean;
children: React.ReactNode;
}) {
return isWrapper ? (
<div className="nav-menu-item dropdown">{children}</div>
) : (
children
);
}

type DropdownProps = {
Tag?: keyof JSX.IntrinsicElements;
className?: string;
label: string;
children: React.ReactNode;
excludeWrapper?: boolean;
navAnalytics?: string;
};

export default function Dropdown({
Tag = 'li',
className = undefined,
label,
children,
excludeWrapper = false,
navAnalytics
}) {
const topRef = useRef();
const dropdownRef = useRef(null);
}: DropdownProps) {
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 +114,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) => {
if (activeDropdown === topRef) {
event.preventDefault();
closeMenu();
Expand All @@ -114,8 +137,8 @@ function DropdownController({
[openMenu, closeMenu, activeDropdown, topRef]
);
const closeOnBlur = React.useCallback(
({currentTarget, relatedTarget}) => {
if (currentTarget.parentNode.contains(relatedTarget)) {
(event: React.FocusEvent<HTMLAnchorElement>) => {
if (event.currentTarget.parentNode?.contains(event.relatedTarget as Node)) {
return;
}
closeDesktopMenu();
Expand Down Expand Up @@ -154,7 +177,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,29 @@
import useDropdownContext from '../../dropdown-context';
import {isMobileDisplay} from '~/helpers/device';

function findNext(dropdownRef: React.MutableRefObject<HTMLDivElement>) {
function findNext(dropdownRef: React.RefObject<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 = dropdownRef.current ? Array.from(dropdownRef.current.querySelectorAll('a')) : [];
const idx = targets.indexOf(document.activeElement as HTMLAnchorElement);
const nextIdx = (idx + 1) % targets.length;

return targets[nextIdx];
}

function findPrev(
topRef: React.MutableRefObject<HTMLAnchorElement>,
dropdownRef: React.MutableRefObject<HTMLDivElement>
topRef: React.RefObject<HTMLAnchorElement | null>,
dropdownRef: React.RefObject<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 = dropdownRef.current ? Array.from(dropdownRef.current.querySelectorAll('a')) : [];
const idx = targets.indexOf(document.activeElement as HTMLAnchorElement);

if (idx === 0) {
Expand All @@ -40,8 +40,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 All @@ -68,16 +68,16 @@ export default function useNavigateByKey({
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
if (document.activeElement === topRef.current) {
(dropdownRef.current.firstChild as HTMLAnchorElement)?.focus();
if (document.activeElement === topRef?.current) {
(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
@@ -0,0 +1,3 @@
import LoginMenuWithDropdown from './login-menu-with-dropdown';

export default LoginMenuWithDropdown;
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 getSettings from '~/helpers/window-settings';
import {assertDefined} from '~/helpers/data';

const settings = window.SETTINGS;
const settings = getSettings();
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 @@ -8,9 +8,9 @@ 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;
(e: React.MouseEvent<HTMLAnchorElement>) => {
if ((e as React.MouseEvent & {defaultPrevented: boolean}).defaultPrevented) {
(e as React.MouseEvent & {defaultPrevented: boolean}).defaultPrevented = false;
}
},
[]
Expand All @@ -34,7 +34,7 @@ export default function LoginMenu() {

return (
loggedIn ?
<JITLoad importFn={() => import('./login-menu-with-dropdown')} /> :
<JITLoad importFn={() => import('./load-login-menu-with-dropdown.js')} /> :
<LoginLink />
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,41 @@ import GiveButton from '../give-button/give-button';
import {treatSpaceOrEnterAsClick} from '~/helpers/events';
import './main-menu.scss';

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

return <MenuItem label={item.label} url={item.partial_url} />;
function DropdownItem({item}: {item: MenuDropDown}) {
return (
<Dropdown
label={item.name}
navAnalytics={`Main Menu (${item.name})`}
>
{item.menu.map((menuItem) => (
<MenuItem key={menuItem.label} label={menuItem.label} url={menuItem.partial_url} />
))}
</Dropdown>
);
}

function MenusFromStructure({structure}) {

function MenusFromStructure({structure}: {structure: MenuDropDown[]}) {
return (
<React.Fragment>
{structure.map((item) => (
<DropdownOrMenuItem key={item.label} item={item} />
<DropdownItem key={item.name} item={item} />
))}
</React.Fragment>
);
}

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

if (!structure) {
return null;
Expand All @@ -60,7 +65,7 @@ function SubjectsMenu() {
const categories = useSubjectCategoryContext();
const {language} = useLanguageContext();
// This will have to be revisited if/when we implement more languages
const otherLocale = ['en', 'es'].filter((la) => la !== language)[0];
const otherLocale = (['en', 'es'] as const).filter((la) => la !== language)[0];
const {pathname} = useLocation();

if (!categories.length) {
Expand Down Expand Up @@ -104,23 +109,26 @@ function SubjectsMenu() {
);
}

function navigateWithArrows(event) {
// eslint-disable-next-line complexity
function navigateWithArrows(event: React.KeyboardEvent<HTMLUListElement>) {
const target = event.target as HTMLElement;

switch (event.key) {
case 'ArrowRight':
event.preventDefault();
event.stopPropagation();
event.target
(target
.closest('li')
.nextElementSibling?.querySelector('a')
.focus();
?.nextElementSibling?.querySelector('a') as HTMLAnchorElement)
?.focus();
break;
case 'ArrowLeft':
event.preventDefault();
event.stopPropagation();
event.target
(target
.closest('li')
.previousElementSibling?.querySelector('a')
.focus();
?.previousElementSibling?.querySelector('a') as HTMLAnchorElement)
?.focus();
break;
default:
break;
Expand Down
Loading
Loading