Refactored application components.
Added accessibility tags.
This commit is contained in:
+23
-23
@@ -1,4 +1,4 @@
|
|||||||
.appContainer {
|
.appWrapper {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -20,24 +20,24 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alphaZone {
|
.firstZone {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.betaZone {
|
.secondZone {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorPickerContainer {
|
.colorPickerWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorValuesContainer {
|
.colorValuesWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteEditorContainer {
|
.paletteEditorWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteLibraryContainer {
|
.paletteLibraryWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,35 +61,35 @@
|
|||||||
.mobileRightNav {
|
.mobileRightNav {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileAlphaZone {
|
.mobileFirstZone {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileBetaZone {
|
.mobileSecondZone {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedContainer {
|
.tabWrapper {
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
scroll-snap-type: y mandatory;
|
scroll-snap-type: y mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedContainer .tab {
|
.tabWrapper .tab {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
scroll-snap-align: start;
|
scroll-snap-align: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorPickerContainer {
|
.colorPickerWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorValuesContainer {
|
.colorValuesWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteEditorContainer {
|
.paletteEditorWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteLibraryContainer {
|
.paletteLibraryWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,32 +119,32 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileAlphaZone {
|
.mobileFirstZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileBetaZone {
|
.mobileSecondZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedContainer {
|
.tabWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
scroll-snap-type: x mandatory;
|
scroll-snap-type: x mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedContainer .tab {
|
.tabWrapper .tab {
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
scroll-snap-align: start;
|
scroll-snap-align: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorPickerContainer {
|
.colorPickerWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorValuesContainer {
|
.colorValuesWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteEditorContainer {
|
.paletteEditorWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
.paletteLibraryContainer {
|
.paletteLibraryWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+213
-75
@@ -5,86 +5,224 @@ import ColorValues from "./components/ColorValues/ColorValues";
|
|||||||
import { LeftMenu, RightMenu } from "./components/SideMenu";
|
import { LeftMenu, RightMenu } from "./components/SideMenu";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
// Menu Button Components
|
||||||
|
|
||||||
|
interface MenuButtonProps {
|
||||||
|
onClick: () => void;
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LeftMenuButton({ onClick, isOpen }: MenuButtonProps) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={styles.leftMenuButton}
|
||||||
|
onClick={onClick}
|
||||||
|
aria-label="Open left menu"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
>
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RightMenuButton({ onClick, isOpen }: MenuButtonProps) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={styles.rightMenuButton}
|
||||||
|
onClick={onClick}
|
||||||
|
aria-label="Open right menu"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
>
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.colorValuesWrapper)}
|
||||||
|
role="tabpanel"
|
||||||
|
aria-label="Color values"
|
||||||
|
>
|
||||||
|
<ColorValues />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileSecondZone() {
|
||||||
|
return (
|
||||||
|
<section className={styles.mobileSecondZone} aria-label="Palette tools">
|
||||||
|
<div
|
||||||
|
className={styles.paletteEditorWrapper}
|
||||||
|
aria-label="Palette editor"
|
||||||
|
></div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 id="user-info" aria-label="User information">
|
||||||
|
User Info
|
||||||
|
</div>
|
||||||
|
</LeftMenu>
|
||||||
|
|
||||||
|
<RightMenu
|
||||||
|
isOpen={isRightMenuOpen}
|
||||||
|
onClose={() => setIsRightMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="palette-library"
|
||||||
|
className={styles.paletteLibraryWrapper}
|
||||||
|
aria-label="Palette library"
|
||||||
|
>
|
||||||
|
Palette Library
|
||||||
|
</div>
|
||||||
|
</RightMenu>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.colorValuesWrapper} aria-label="Color values">
|
||||||
|
<ColorValues />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
function App() {
|
||||||
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
|
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
|
||||||
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
|
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
|
||||||
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.appContainer}>
|
<div className={styles.appWrapper} role="application">
|
||||||
<div className={styles.mobileContent}>
|
<MobileContent
|
||||||
<div className={styles.mobileTopNav}>
|
isLeftMenuOpen={isLeftMenuOpen}
|
||||||
<LeftMenuButton />
|
setIsLeftMenuOpen={setIsLeftMenuOpen}
|
||||||
<RightMenuButton />
|
isRightMenuOpen={isRightMenuOpen}
|
||||||
</div>
|
setIsRightMenuOpen={setIsRightMenuOpen}
|
||||||
|
/>
|
||||||
<div className={styles.mobileLeftNav}>
|
<DesktopContent />
|
||||||
<LeftMenuButton />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.mobileAlphaZone}>
|
|
||||||
<div className={styles.tabbedContainer}>
|
|
||||||
<div className={clsx(styles.tab, styles.colorPickerContainer)}>
|
|
||||||
<ColorPicker />
|
|
||||||
</div>
|
|
||||||
<div className={clsx(styles.tab, styles.colorValuesContainer)}>
|
|
||||||
<ColorValues />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.mobileBetaZone}>
|
|
||||||
<div className={styles.paletteEditorContainer}></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.mobileRightNav}>
|
|
||||||
<RightMenuButton />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<LeftMenu
|
|
||||||
isOpen={isLeftMenuOpen}
|
|
||||||
onClose={() => setIsLeftMenuOpen(false)}
|
|
||||||
>
|
|
||||||
<div>User Info</div>
|
|
||||||
</LeftMenu>
|
|
||||||
|
|
||||||
<RightMenu
|
|
||||||
isOpen={isRightMenuOpen}
|
|
||||||
onClose={() => setIsRightMenuOpen(false)}
|
|
||||||
>
|
|
||||||
<div className={styles.paletteLibraryContainer}>Palette Library</div>
|
|
||||||
</RightMenu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.mainContent}>
|
|
||||||
<div className={styles.alphaZone}>
|
|
||||||
<div className={styles.colorPickerContainer}>
|
|
||||||
<ColorPicker />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.colorValuesContainer}>
|
|
||||||
<ColorValues />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.betaZone}>
|
|
||||||
<div className={styles.paletteEditorContainer}></div>
|
|
||||||
<div className={styles.paletteLibraryContainer}></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,14 @@
|
|||||||
transition: 0.3s ease-out;
|
transition: 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sideMenu:not(.open) {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sideMenu.open {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.sideMenuUnderlay {
|
.sideMenuUnderlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ interface BaseMenuProps extends SideMenuProps {
|
|||||||
|
|
||||||
function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
|
function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
|
||||||
const isLeftMenu = position === "left";
|
const isLeftMenu = position === "left";
|
||||||
|
const menuId = `${position}-side-menu`;
|
||||||
|
|
||||||
const handleUnderlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleUnderlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (e.target === e.currentTarget) {
|
if (e.target === e.currentTarget) {
|
||||||
@@ -25,8 +26,13 @@ function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
|
|||||||
<div
|
<div
|
||||||
className={clsx(styles.sideMenuUnderlay, { [styles.open]: isOpen })}
|
className={clsx(styles.sideMenuUnderlay, { [styles.open]: isOpen })}
|
||||||
onClick={handleUnderlayClick}
|
onClick={handleUnderlayClick}
|
||||||
|
aria-hidden={!isOpen}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
id={menuId}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label={`${position} side menu`}
|
||||||
data-cy={`${position}-menu`}
|
data-cy={`${position}-menu`}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
styles.sideMenu,
|
styles.sideMenu,
|
||||||
@@ -43,6 +49,7 @@ function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
|
|||||||
>
|
>
|
||||||
<div className={styles.topNav}>
|
<div className={styles.topNav}>
|
||||||
<button
|
<button
|
||||||
|
aria-label="Close menu"
|
||||||
data-cy="close-menu"
|
data-cy="close-menu"
|
||||||
className={styles.closeButton}
|
className={styles.closeButton}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|||||||
Reference in New Issue
Block a user