From badf817ab1ab2ce9993c7c1bb030521eab6c408e Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:27:28 -0500 Subject: [PATCH 01/37] Add aria label to project button --- src/l10n.json | 2 ++ src/views/explore/explore.jsx | 6 +++++- static/svgs/tabs/projects-active.svg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/l10n.json b/src/l10n.json index d270428f7..d9c70e300 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -72,6 +72,8 @@ "general.download": "Download", "general.password": "Password", "general.press": "Press", + "general.projectsSelected": "Projects Selected", + "general.projectsNotS": "Projects", "general.privacyPolicy": "Privacy Policy", "general.projects": "Projects", "general.profile": "Profile", diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index f2dd1ea25..817db1921 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -128,22 +128,26 @@ class Explore extends React.Component { active: (this.state.itemType === type) }); return ( + // **** ADD A CONDITIONAL HERE ****
  • {this.state.itemType === type ? [ ] : [ ]} - +
  • ); diff --git a/static/svgs/tabs/projects-active.svg b/static/svgs/tabs/projects-active.svg index 3738946b1..68d234805 100644 --- a/static/svgs/tabs/projects-active.svg +++ b/static/svgs/tabs/projects-active.svg @@ -1 +1 @@ -Icons \ No newline at end of file +Icons \ No newline at end of file From de17545c8734be6fd7e42e36f0f6b7009dc884c2 Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Thu, 1 Dec 2022 05:39:09 -0500 Subject: [PATCH 02/37] Add aria labels to elements --- src/l10n.json | 12 ++++++- src/views/explore/explore.jsx | 67 ++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/l10n.json b/src/l10n.json index d9c70e300..2694df735 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -72,7 +72,7 @@ "general.download": "Download", "general.password": "Password", "general.press": "Press", - "general.projectsSelected": "Projects Selected", + "general.projectsSelected": "Projects Tab Selected", "general.projectsNotS": "Projects", "general.privacyPolicy": "Privacy Policy", "general.projects": "Projects", @@ -91,6 +91,8 @@ "general.startOver": "Start over", "general.statistics": "Statistics", "general.studios": "Studios", + "general.studiosSelected": "Studios Tab Selected", + "general.studiosNotS": "Studios", "general.support": "Resources", "general.ideas": "Ideas", "general.tipsWindow": "Tips Window", @@ -111,13 +113,21 @@ "general.seeAllComments": "See all comments", "general.all": "All", + "general.allSelected": "All Selected", "general.animations": "Animations", + "general.animationsSelected": "Animations Selected", "general.art": "Art", + "general.artSelected": "Art Selected", "general.games": "Games", + "general.gamesSelected": "Games Selected", "general.music": "Music", + "general.musicSelected": "Music Selected", "general.results": "Results", + "general.resultsSelected": "Results Selected", "general.stories": "Stories", + "general.storiesSelected": "Stories Selected", "general.tutorials": "Tutorials", + "general.tutorialsSelected": "Tutorials Selected", "general.teacherAccounts": "Teacher Accounts", diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index 817db1921..596d4ea30 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -117,9 +117,24 @@ class Explore extends React.Component { }); return ( -
  • - -
  • + {classes === 'active' ? [ +
  • + +
  • + ] : [ +
  • + +
  • + ] + }
    ); } @@ -132,23 +147,43 @@ class Explore extends React.Component {
  • {this.state.itemType === type ? [ - + className={`tab-icon ${type}`} + key={`tab-${type}`} + src={`/svgs/tabs/${type}-active.svg`} + aria-label={this.props.intl.formatMessage({id: 'general.projectsSelected'})} + /> + ] : [ + + ] ] : [ - + type === 'projects' ? [ + + ] : [ + + ] ]} - +
  • +
    ); } From 479e2d363ef32ee06b40bdd074c1dfd4f35f60a9 Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Thu, 8 Dec 2022 04:17:45 -0500 Subject: [PATCH 03/37] Refactor tabs component --- src/views/explore/explore.jsx | 120 ++++++++++++---------------------- 1 file changed, 42 insertions(+), 78 deletions(-) diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index 596d4ea30..664c266ee 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -25,7 +25,6 @@ class Explore extends React.Component { bindAll(this, [ 'getExploreState', 'handleGetExploreMore', - 'changeItemType', 'handleChangeSortMode', 'getBubble', 'getTab' @@ -95,16 +94,7 @@ class Explore extends React.Component { } }); } - changeItemType () { - let newType; - for (const t of this.state.acceptableTypes) { - if (this.state.itemType !== t) { - newType = t; - break; - } - } - window.location = `${window.location.origin}/explore/${newType}/${this.state.tab}/${this.state.mode}`; - } + handleChangeSortMode (name, value) { if (this.state.acceptableModes.indexOf(value) !== -1) { window.location = @@ -117,76 +107,13 @@ class Explore extends React.Component { }); return ( - {classes === 'active' ? [ -
  • - -
  • - ] : [ -
  • - -
  • - ] - } -
    - ); - } - getTab (type) { - const classes = classNames({ - active: (this.state.itemType === type) - }); - return ( - // **** ADD A CONDITIONAL HERE **** -
  • - {this.state.itemType === type ? [ - type === 'projects' ? [ - - ] : [ - - ] - ] : [ - type === 'projects' ? [ - - ] : [ - - ] - ]} - +
  • -
    ); } + render () { return (
    @@ -198,10 +125,47 @@ class Explore extends React.Component {
    - + {/* {this.getTab('projects')} {this.getTab('studios')} - + */} + { + window.location = `${window.location.origin}/explore/projects/${this.state.category}/${this.state.mode}`; + }, + getContent: () =>( +
  • + {this.state.itemType === type ? [ + + ] : [ + + ]} + +
  • + ) + }, + { + name: 'studios', + onTrigger: () => { + window.location = `${window.location.origin}/explore/studios/${this.state.tab}/${this.state.mode}`; + }, + content: 'studios' + } + ]} + activeTabName={ this.state.itemType } + />
    {this.getBubble('all')} From 6e22ee068f44214eff35cb15dfa1c9186e401c4e Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:59:21 -0500 Subject: [PATCH 04/37] Refactor tab component --- src/components/tabs/tabs.jsx | 38 ++++++++++++---- src/views/explore/explore.jsx | 85 +++++++++++++++++++---------------- 2 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/components/tabs/tabs.jsx b/src/components/tabs/tabs.jsx index cc637ff56..fec36dfdf 100644 --- a/src/components/tabs/tabs.jsx +++ b/src/components/tabs/tabs.jsx @@ -10,17 +10,37 @@ require('./tabs.scss'); * Container for a custom, horizontal list of navigation elements * that can be displayed within a view or component. */ -const Tabs = props => ( -
    - - {props.children} - -
    -); +const Tabs = ({ items, activeTabName }) => { + const itemsRendered = items.map(({ name, onTrigger, getContent }) => { + const isActive = name === activeTabName + return ( +
  • + {getContent(isActive)} +
  • + ) + }) + return ( +
    + + {itemsRendered} + +
    + ) +}; Tabs.propTypes = { - children: PropTypes.node, - className: PropTypes.string + items: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + onTrigger: PropTypes.func.isRequired, + getContent: PropTypes.func.isRequired + }) + ).isRequired, + activeTabName: PropTypes.string.isRequired, }; module.exports = Tabs; diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index 664c266ee..f39ec6ed6 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -27,7 +27,6 @@ class Explore extends React.Component { 'handleGetExploreMore', 'handleChangeSortMode', 'getBubble', - 'getTab' ]); this.state = this.getExploreState(); @@ -125,45 +124,55 @@ class Explore extends React.Component {
    - {/* - {this.getTab('projects')} - {this.getTab('studios')} - */} { - window.location = `${window.location.origin}/explore/projects/${this.state.category}/${this.state.mode}`; - }, - getContent: () =>( -
  • - {this.state.itemType === type ? [ - - ] : [ - - ]} - -
  • - ) + items={[ + { + name: 'projects', + onTrigger: () => { + window.location = `${window.location.origin}/explore/projects/${this.state.category}/${this.state.mode}`; }, - { - name: 'studios', - onTrigger: () => { - window.location = `${window.location.origin}/explore/studios/${this.state.tab}/${this.state.mode}`; - }, - content: 'studios' - } - ]} + getContent: (isActive) => ( +
    + {isActive ? ( + + ) : ( + + ) + } + +
    + ) + }, + { + name: 'studios', + onTrigger: () => { + window.location = `${window.location.origin}/explore/studios/${this.state.category}/${this.state.mode}`; + }, + getContent: (isActive) => ( +
    + {isActive ? ( + + ) : ( + + ) + } + +
    + ) + } + ]} activeTabName={ this.state.itemType } />
    From 4d6b5bd2e9f225f6c56113bacf8ed3997ac5cdc0 Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Tue, 13 Dec 2022 08:31:56 -0500 Subject: [PATCH 05/37] Update tab item --- src/components/tabs/tabs.jsx | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/tabs/tabs.jsx b/src/components/tabs/tabs.jsx index fec36dfdf..d9a4d79b4 100644 --- a/src/components/tabs/tabs.jsx +++ b/src/components/tabs/tabs.jsx @@ -1,30 +1,51 @@ const classNames = require('classnames'); const PropTypes = require('prop-types'); +const { useRef } = require('react'); const React = require('react'); const SubNavigation = require('../../components/subnavigation/subnavigation.jsx'); require('./tabs.scss'); +const TabItem = ({ children, ...props }) => { + const tabItemRef = useRef() + + return
  • {children}
  • +} + /* * Container for a custom, horizontal list of navigation elements * that can be displayed within a view or component. */ const Tabs = ({ items, activeTabName }) => { - const itemsRendered = items.map(({ name, onTrigger, getContent }) => { + let activeIndex + const itemsRendered = items.map(({ name, onTrigger, getContent }, index) => { const isActive = name === activeTabName + activeIndex = index return (
  • {getContent(isActive)}
  • ) }) + + const handleKeyDown = event => { + if (!['ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' '].includes(event.key)) { + return + } + event.preventDefault() + console.log( + 'hi' + ) + } + return ( -
    +
    {itemsRendered} From 7ea0815428e0f5c789d8a827af130d247d700d32 Mon Sep 17 00:00:00 2001 From: Paul Clue <67766160+Paul-Clue@users.noreply.github.com> Date: Tue, 13 Dec 2022 19:13:03 -0500 Subject: [PATCH 06/37] Explore and Search refactor --- .../subnavigation/subnavigation.jsx | 2 + src/components/tabs/tabs.jsx | 92 +++++++++++----- src/components/tabs/tabs.scss | 3 +- src/views/explore/explore.jsx | 62 ++++++----- src/views/search/search.jsx | 100 +++++++++++------- 5 files changed, 164 insertions(+), 95 deletions(-) diff --git a/src/components/subnavigation/subnavigation.jsx b/src/components/subnavigation/subnavigation.jsx index e9d964975..b04ae38cb 100644 --- a/src/components/subnavigation/subnavigation.jsx +++ b/src/components/subnavigation/subnavigation.jsx @@ -20,6 +20,7 @@ const SubNavigation = props => ( 'sub-nav-align-right': props.align === 'right' } )} + role={props.role} > {props.children}
    @@ -27,6 +28,7 @@ const SubNavigation = props => ( SubNavigation.propTypes = { align: PropTypes.string, + role: PropTypes.string, children: PropTypes.node, className: PropTypes.string }; diff --git a/src/components/tabs/tabs.jsx b/src/components/tabs/tabs.jsx index d9a4d79b4..07553ef13 100644 --- a/src/components/tabs/tabs.jsx +++ b/src/components/tabs/tabs.jsx @@ -1,56 +1,92 @@ -const classNames = require('classnames'); const PropTypes = require('prop-types'); -const { useRef } = require('react'); +const {useRef} = require('react'); const React = require('react'); const SubNavigation = require('../../components/subnavigation/subnavigation.jsx'); require('./tabs.scss'); -const TabItem = ({ children, ...props }) => { - const tabItemRef = useRef() - - return
  • {children}
  • -} - /* * Container for a custom, horizontal list of navigation elements * that can be displayed within a view or component. */ -const Tabs = ({ items, activeTabName }) => { - let activeIndex - const itemsRendered = items.map(({ name, onTrigger, getContent }, index) => { - const isActive = name === activeTabName - activeIndex = index +const Tabs = ({items, activeTabName}) => { + const tabElementRefs = useRef({}); + + const itemsRendered = items.map(({name, onTrigger, getContent}) => { + const isActive = name === activeTabName; + + let tabRef; + if (tabElementRefs.current[name]) { + tabRef = tabElementRefs.current[name]; + } else { + tabRef = React.createRef(); + tabElementRefs.current[name] = tabRef; + } + return ( -
  • {getContent(isActive)} -
  • - ) - }) + + ); + }); const handleKeyDown = event => { if (!['ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' '].includes(event.key)) { - return + return; } - event.preventDefault() - console.log( - 'hi' - ) - } - + event.preventDefault(); + const focusedIndex = Object.values(tabElementRefs.current) + .findIndex(tabElementRef => + document.activeElement === tabElementRef.current + ); + if (event.key === 'ArrowLeft') { + let nextIndex; + if (focusedIndex === 0) { + nextIndex = Object.values(tabElementRefs.current).length - 1; + } else { + nextIndex = focusedIndex - 1; + } + Object.values(tabElementRefs.current)[nextIndex].current.focus(); + } else if (event.key === 'ArrowRight') { + let nextIndex; + if (focusedIndex === Object.values(tabElementRefs.current).length - 1) { + nextIndex = 0; + } else { + nextIndex = focusedIndex + 1; + } + Object.values(tabElementRefs.current)[nextIndex].current.focus(); + } else if (event.key === 'Home') { + Object.values(tabElementRefs.current)[0].current.focus(); + } else if (event.key === 'End') { + const lastTab = Object.values(tabElementRefs.current).length - 1; + Object.values(tabElementRefs.current)[lastTab].current.focus(); + } else if (event.key === 'Enter' || event.key === ' ') { + items[focusedIndex].onTrigger(); + } + }; + return ( -
    - +
    + {itemsRendered}
    - ) + ); }; Tabs.propTypes = { @@ -61,7 +97,7 @@ Tabs.propTypes = { getContent: PropTypes.func.isRequired }) ).isRequired, - activeTabName: PropTypes.string.isRequired, + activeTabName: PropTypes.string.isRequired }; module.exports = Tabs; diff --git a/src/components/tabs/tabs.scss b/src/components/tabs/tabs.scss index a612c3f76..791e9723f 100644 --- a/src/components/tabs/tabs.scss +++ b/src/components/tabs/tabs.scss @@ -14,13 +14,14 @@ justify-content: center; } -.tabs li { +.tabs button { margin: 0; border: 0; border-radius: 0; width: $cols2; text-align: center; color: $header-gray; + background-color: transparent; &.active { border-bottom: 3px solid $ui-aqua; diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index f39ec6ed6..c2e143023 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -26,7 +26,7 @@ class Explore extends React.Component { 'getExploreState', 'handleGetExploreMore', 'handleChangeSortMode', - 'getBubble', + 'getBubble' ]); this.state = this.getExploreState(); @@ -128,22 +128,25 @@ class Explore extends React.Component { items={[ { name: 'projects', - onTrigger: () => { - window.location = `${window.location.origin}/explore/projects/${this.state.category}/${this.state.mode}`; + onTrigger: () => { + window.location = `${window.location.origin}/explore/projects/` + + `${this.state.category}/${this.state.mode}`; }, - getContent: (isActive) => ( + getContent: isActive => (
    {isActive ? ( - - ) : ( - - ) + + ) : ( + + ) }
    @@ -151,29 +154,32 @@ class Explore extends React.Component { }, { name: 'studios', - onTrigger: () => { - window.location = `${window.location.origin}/explore/studios/${this.state.category}/${this.state.mode}`; + onTrigger: () => { + window.location = `${window.location.origin}/explore/studios/` + + `${this.state.category}/${this.state.mode}`; }, - getContent: (isActive) => ( + getContent: isActive => (
    {isActive ? ( - - ) : ( - - ) + + ) : ( + + ) }
    ) } ]} - activeTabName={ this.state.itemType } + activeTabName={this.state.itemType} />
    diff --git a/src/views/search/search.jsx b/src/views/search/search.jsx index 036f78105..0036f52b9 100644 --- a/src/views/search/search.jsx +++ b/src/views/search/search.jsx @@ -30,8 +30,7 @@ class Search extends React.Component { bindAll(this, [ 'getSearchState', 'handleChangeSortMode', - 'handleGetSearchMore', - 'getTab' + 'handleGetSearchMore' ]); this.state = this.getSearchState(); this.state.loaded = []; @@ -150,38 +149,6 @@ class Search extends React.Component { }); }); } - getTab (type) { - const termText = this.encodeSearchTerm(); - let targetUrl = `/search/${type}`; - if (termText) { - targetUrl += `?q=${termText}`; - } - let allTab = ( - -
  • - - -
  • -
    - ); - if (this.state.tab === type) { - allTab = ( - -
  • - - -
  • -
    - ); - } - return allTab; - } getProjectBox () { const results = (
    - - {this.getTab('projects')} - {this.getTab('studios')} - + { + const termText = this.encodeSearchTerm(); + let targetUrl = `/search/projects`; + if (termText) targetUrl += `?q=${termText}`; + window.location = targetUrl; + }, + getContent: isActive => ( +
    + {isActive ? ( + + ) : ( + + ) + } + +
    + ) + }, + { + name: 'studios', + onTrigger: () => { + const termText = this.encodeSearchTerm(); + let targetUrl = `/search/studios`; + if (termText) targetUrl += `?q=${termText}`; + window.location = targetUrl; + }, + getContent: isActive => ( +
    + {isActive ? ( + + ) : ( + + ) + } + +
    + ) + } + ]} + activeTabName={this.state.tab} + />