Refactored application components.

Added accessibility tags.
This commit is contained in:
Jay
2025-07-20 11:40:09 -04:00
parent 5400a7acdd
commit 3256e4968a
4 changed files with 251 additions and 98 deletions
+23 -23
View File
@@ -1,4 +1,4 @@
.appContainer {
.appWrapper {
height: 100vh;
width: 100%;
overflow: hidden;
@@ -20,24 +20,24 @@
display: flex;
}
.alphaZone {
.firstZone {
flex: 1;
}
.betaZone {
.secondZone {
flex: 1;
}
.colorPickerContainer {
.colorPickerWrapper {
}
.colorValuesContainer {
.colorValuesWrapper {
}
.paletteEditorContainer {
.paletteEditorWrapper {
}
.paletteLibraryContainer {
.paletteLibraryWrapper {
}
}
@@ -61,35 +61,35 @@
.mobileRightNav {
}
.mobileAlphaZone {
.mobileFirstZone {
flex: 1;
}
.mobileBetaZone {
.mobileSecondZone {
flex: 1;
}
.tabbedContainer {
.tabWrapper {
max-height: 100vh;
overflow-y: scroll;
scroll-snap-type: y mandatory;
}
.tabbedContainer .tab {
.tabWrapper .tab {
height: 100vh;
scroll-snap-align: start;
}
.colorPickerContainer {
.colorPickerWrapper {
}
.colorValuesContainer {
.colorValuesWrapper {
}
.paletteEditorContainer {
.paletteEditorWrapper {
}
.paletteLibraryContainer {
.paletteLibraryWrapper {
}
}
@@ -119,32 +119,32 @@
margin-left: auto;
}
.mobileAlphaZone {
.mobileFirstZone {
}
.mobileBetaZone {
.mobileSecondZone {
}
.tabbedContainer {
.tabWrapper {
display: flex;
overflow-x: scroll;
scroll-snap-type: x mandatory;
}
.tabbedContainer .tab {
.tabWrapper .tab {
min-width: 100vw;
scroll-snap-align: start;
}
.colorPickerContainer {
.colorPickerWrapper {
}
.colorValuesContainer {
.colorValuesWrapper {
}
.paletteEditorContainer {
.paletteEditorWrapper {
}
.paletteLibraryContainer {
.paletteLibraryWrapper {
}
}
+187 -49
View File
@@ -5,86 +5,224 @@ import ColorValues from "./components/ColorValues/ColorValues";
import { LeftMenu, RightMenu } from "./components/SideMenu";
import clsx from "clsx";
function App() {
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
// Menu Button Components
const toggleRightMenu = () => setIsRightMenuOpen(!isRightMenuOpen);
const toggleLeftMenu = () => setIsLeftMenuOpen(!isLeftMenuOpen);
const RightMenuButton = () => (
<button className={styles.rightMenuButton} onClick={toggleRightMenu}>
</button>
);
const LeftMenuButton = () => (
<button className={styles.leftMenuButton} onClick={toggleLeftMenu}>
</button>
);
interface MenuButtonProps {
onClick: () => void;
isOpen: boolean;
}
function LeftMenuButton({ onClick, isOpen }: MenuButtonProps) {
return (
<div className={styles.appContainer}>
<div className={styles.mobileContent}>
<div className={styles.mobileTopNav}>
<LeftMenuButton />
<RightMenuButton />
</div>
<button
className={styles.leftMenuButton}
onClick={onClick}
aria-label="Open left menu"
aria-haspopup="dialog"
aria-expanded={isOpen}
>
</button>
);
}
<div className={styles.mobileLeftNav}>
<LeftMenuButton />
</div>
function RightMenuButton({ onClick, isOpen }: MenuButtonProps) {
return (
<button
className={styles.rightMenuButton}
onClick={onClick}
aria-label="Open right menu"
aria-haspopup="dialog"
aria-expanded={isOpen}
>
</button>
);
}
<div className={styles.mobileAlphaZone}>
<div className={styles.tabbedContainer}>
<div className={clsx(styles.tab, styles.colorPickerContainer)}>
// Mobile Layout Components
interface MenuStateProps {
isRightMenuOpen: boolean;
isLeftMenuOpen: boolean;
setIsRightMenuOpen: (state: boolean) => void;
setIsLeftMenuOpen: (state: boolean) => void;
}
function MobileTopNav({
onLeftMenuClick,
onRightMenuClick,
isRightMenuOpen,
isLeftMenuOpen,
}: {
onLeftMenuClick: () => void;
onRightMenuClick: () => void;
isRightMenuOpen: boolean;
isLeftMenuOpen: boolean;
}) {
return (
<nav className={styles.mobileTopNav} aria-label="Mobile top navigation">
<LeftMenuButton onClick={onLeftMenuClick} isOpen={isLeftMenuOpen} />
<RightMenuButton onClick={onRightMenuClick} isOpen={isRightMenuOpen} />
</nav>
);
}
function MobileLeftNav({ onClick, isOpen }: MenuButtonProps) {
return (
<nav className={styles.mobileLeftNav} aria-label="Mobile left navigation">
<LeftMenuButton onClick={onClick} isOpen={isOpen} />
</nav>
);
}
function MobileRightNav({ onClick, isOpen }: MenuButtonProps) {
return (
<nav className={styles.mobileRightNav} aria-label="Mobile right navigation">
<RightMenuButton onClick={onClick} isOpen={isOpen} />
</nav>
);
}
function MobileFirstZone() {
return (
<section className={styles.mobileFirstZone} aria-label="Color tools">
<div className={styles.tabWrapper} role="tablist">
<div
className={clsx(styles.tab, styles.colorPickerWrapper)}
role="tabpanel"
aria-label="Color Picker"
>
<ColorPicker />
</div>
<div className={clsx(styles.tab, styles.colorValuesContainer)}>
<div
className={clsx(styles.tab, styles.colorValuesWrapper)}
role="tabpanel"
aria-label="Color values"
>
<ColorValues />
</div>
</div>
</div>
</section>
);
}
<div className={styles.mobileBetaZone}>
<div className={styles.paletteEditorContainer}></div>
</div>
function MobileSecondZone() {
return (
<section className={styles.mobileSecondZone} aria-label="Palette tools">
<div
className={styles.paletteEditorWrapper}
aria-label="Palette editor"
></div>
</section>
);
}
<div className={styles.mobileRightNav}>
<RightMenuButton />
</div>
function MobileContent({
isLeftMenuOpen,
setIsLeftMenuOpen,
isRightMenuOpen,
setIsRightMenuOpen,
}: MenuStateProps) {
const toggleRightMenu = () => setIsRightMenuOpen(!isRightMenuOpen);
const toggleLeftMenu = () => setIsLeftMenuOpen(!isLeftMenuOpen);
return (
<main className={styles.mobileContent}>
<MobileTopNav
onLeftMenuClick={toggleLeftMenu}
onRightMenuClick={toggleRightMenu}
isLeftMenuOpen={isLeftMenuOpen}
isRightMenuOpen={isRightMenuOpen}
/>
<MobileLeftNav onClick={toggleLeftMenu} isOpen={isLeftMenuOpen} />
<MobileFirstZone />
<MobileSecondZone />
<MobileRightNav onClick={toggleRightMenu} isOpen={isRightMenuOpen} />
<LeftMenu
isOpen={isLeftMenuOpen}
onClose={() => setIsLeftMenuOpen(false)}
>
<div>User Info</div>
<div id="user-info" aria-label="User information">
User Info
</div>
</LeftMenu>
<RightMenu
isOpen={isRightMenuOpen}
onClose={() => setIsRightMenuOpen(false)}
>
<div className={styles.paletteLibraryContainer}>Palette Library</div>
</RightMenu>
<div
id="palette-library"
className={styles.paletteLibraryWrapper}
aria-label="Palette library"
>
Palette Library
</div>
</RightMenu>
</main>
);
}
<div className={styles.mainContent}>
<div className={styles.alphaZone}>
<div className={styles.colorPickerContainer}>
// Desktop Layout Components
function FirstZone() {
return (
<section className={styles.firstZone} aria-label="Color tools">
<div className={styles.colorPickerWrapper} aria-label="Color picker">
<ColorPicker />
</div>
<div className={styles.colorValuesContainer}>
<div className={styles.colorValuesWrapper} aria-label="Color values">
<ColorValues />
</div>
</div>
</section>
);
}
<div className={styles.betaZone}>
<div className={styles.paletteEditorContainer}></div>
<div className={styles.paletteLibraryContainer}></div>
</div>
</div>
function SecondZone() {
return (
<section className={styles.secondZone} aria-label="Palette tools">
<div
className={styles.paletteEditorWrapper}
aria-label="Palette editor"
></div>
<div
className={styles.paletteLibraryWrapper}
aria-label="Palette library"
></div>
</section>
);
}
function DesktopContent() {
return (
<main className={styles.mainContent}>
<FirstZone />
<SecondZone />
</main>
);
}
// Main App Component
function App() {
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
return (
<div className={styles.appWrapper} role="application">
<MobileContent
isLeftMenuOpen={isLeftMenuOpen}
setIsLeftMenuOpen={setIsLeftMenuOpen}
isRightMenuOpen={isRightMenuOpen}
setIsRightMenuOpen={setIsRightMenuOpen}
/>
<DesktopContent />
</div>
);
}
@@ -9,6 +9,14 @@
transition: 0.3s ease-out;
}
.sideMenu:not(.open) {
visibility: hidden;
}
.sideMenu.open {
visibility: visible;
}
.sideMenuUnderlay {
position: fixed;
top: 0;
+7
View File
@@ -14,6 +14,7 @@ interface BaseMenuProps extends SideMenuProps {
function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
const isLeftMenu = position === "left";
const menuId = `${position}-side-menu`;
const handleUnderlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === e.currentTarget) {
@@ -25,8 +26,13 @@ function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
<div
className={clsx(styles.sideMenuUnderlay, { [styles.open]: isOpen })}
onClick={handleUnderlayClick}
aria-hidden={!isOpen}
>
<div
id={menuId}
role="dialog"
aria-modal="true"
aria-label={`${position} side menu`}
data-cy={`${position}-menu`}
className={clsx(
styles.sideMenu,
@@ -43,6 +49,7 @@ function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
>
<div className={styles.topNav}>
<button
aria-label="Close menu"
data-cy="close-menu"
className={styles.closeButton}
onClick={onClose}