Skip to content

Commit

Permalink
Merge pull request #286 from samvera-labs/focusable-tab-labels
Browse files Browse the repository at this point in the history
Make tab labels focusable in the demo site
  • Loading branch information
Dananji authored Nov 15, 2023
2 parents a15edf6 + c0f11ea commit 9f68dc7
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 114 deletions.
175 changes: 115 additions & 60 deletions demo/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import '../dist/ramp.css';
const App = ({ manifestURL }) => {
const [userURL, setUserURL] = React.useState(manifestURL);
const [manifestUrl, setManifestUrl] = React.useState(manifestURL);
const [activeTab, setActiveTab] = React.useState('Details');

const tabValues = [
{ title: 'Details', ref: React.useRef(null) },
{ title: 'Transcripts', ref: React.useRef(null) },
{ title: 'Files', ref: React.useRef(null) },
{ title: 'Markers', ref: React.useRef(null) },
];

React.useEffect(() => {
setManifestUrl(manifestUrl);
Expand All @@ -30,10 +36,6 @@ const App = ({ manifestURL }) => {
setUserURL(e.target.value);
};

const handleShowTab = (tab) => {
setActiveTab(tab);
};

return (
<div className='ramp-demo'>
<h1>Ramp</h1>
Expand Down Expand Up @@ -76,52 +78,7 @@ const App = ({ manifestURL }) => {
<AutoAdvanceToggle />
<StructuredNavigation />
</div>
<div className="tabs">
<Tab
activeTab={activeTab == 'Details'}
key={'Details'}
label={'Details'}
onClick={handleShowTab}
>
<MetadataDisplay showHeading={false} />
</Tab>
<Tab
activeTab={activeTab == 'Transcripts'}
key={'Transcripts'}
label={'Transcripts'}
onClick={handleShowTab}
><Transcript
playerID="iiif-media-player"
transcripts={[
{
canvasId: 0,
items: [
{
title: 'From Manifest',
url: manifestUrl,
}
],
},
]}
/>
</Tab>
<Tab
activeTab={activeTab == 'Files'}
key={'Files'}
label={'Files'}
onClick={handleShowTab}
>
<SupplementalFiles showHeading={false} />
</Tab>
<Tab
activeTab={activeTab == 'Markers'}
key={'Markers'}
label={'Markers'}
onClick={handleShowTab}
>
<MarkersDisplay showHeading={false} />
</Tab>
</div>
<Tabs tabValues={tabValues} manifestUrl={manifestUrl} />
</div>
</div>
</IIIFPlayer>
Expand All @@ -130,19 +87,117 @@ const App = ({ manifestURL }) => {
);
};

const Tab = ({ label, onClick, activeTab, children }) => {
const onClickHandler = () => {
onClick(label);

/*Reference: https://accessible-react.eevis.codes/components/tabs */
const Tabs = ({ tabValues, manifestUrl }) => {
const [activeTab, setActiveTab] = React.useState(0);

let tabs = [];

const handleClick = (index) => {
setActiveTab(index);
};

const handleNextTab = (firstTabInRound, nextTab, lastTabInRound) => {
const tabToSelect =
activeTab === lastTabInRound ? firstTabInRound : nextTab;
setActiveTab(tabToSelect);
tabValues[tabToSelect].ref.current.focus();
};

const handleKeyPress = (event) => {
const tabCount = Object.keys(tabValues).length;

if (event.key === "ArrowLeft") {
const last = tabCount;
const next = activeTab - 1;
handleNextTab(last, next, 0);
}
if (event.key === "ArrowRight") {
const first = 0;
const next = activeTab + 1;
handleNextTab(first, next, tabCount);
}
};

tabValues.map((t, index) => {
tabs.push(
<Tab
key={index}
id={t.title.toLowerCase()}
tabPanelId={`${t.title.toLowerCase()}Tab`}
index={index}
handleChange={handleClick}
activeTab={activeTab}
title={t.title}
tabRef={t.ref}
/>
);
});

return (
<div className="tab-nav">
<input type="radio" id={label} name="tab-nav" checked={activeTab} onChange={onClickHandler} />
<label htmlFor={label}>{label}</label>
<div className="tab-content">
{children}
<section className="tabs-wrapper">
<div className="switcher">
<ul
role="tablist"
className="tablist switcher"
aria-label="more Ramp components in tabs"
onKeyDown={handleKeyPress}>
{tabs}
</ul>
</div>
</div>
<TabPanel id="detailsTab" tabId="details" tabIndex={0} activeTab={activeTab}>
<MetadataDisplay showHeading={false} />
</TabPanel>
<TabPanel id="transcriptsTab" tabId="transcripts" tabIndex={1} activeTab={activeTab}>
<Transcript
playerID="iiif-media-player"
transcripts={[
{ canvasId: 0, items: [{ title: 'From Manifest', url: manifestUrl }] },
]}
/>
</TabPanel>
<TabPanel id="filesTab" tabId="files" tabIndex={2} activeTab={activeTab}>
<SupplementalFiles showHeading={false} />
</TabPanel>
<TabPanel id="markersTab" tabId="markers" tabIndex={3} activeTab={activeTab}>
<MarkersDisplay showHeading={false} />
</TabPanel>
</section>
);
};

const Tab = ({ id, tabPanelId, index, handleChange, activeTab, title, tabRef }) => {
const handleClick = () => { handleChange(index); };
return (
<li role="presentation">
<button
role="tab"
id={id}
aria-selected={activeTab === index}
aria-controls={tabPanelId}
onClick={handleClick}
tabIndex={activeTab === index ? 0 : -1}
ref={tabRef}
>
{title}
</button>
</li>
);
};

const TabPanel = ({ id, tabId, activeTab, tabIndex, children }) => {
return (
<section
role="tabpanel"
id={id}
aria-labelledby={tabId}
hidden={activeTab !== tabIndex}
tabIndex={0}
className="tabpanel"
>
{children}
</section>
);
};

Expand Down
95 changes: 41 additions & 54 deletions demo/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,78 +28,65 @@ div.iiif-player-demo {
margin-right: 2rem;
}

// Reference: https://codepen.io/alvarotrigo/pen/xxXoMJy
.tabs {
flex: 1;
position: relative;
/*Reference: https://accessible-react.eevis.codes/components/tabs */
.tablist {
display: flex;
border-radius: 0.5rem 0.5rem 0 0;
flex-direction: row;
}

.tab-nav {
flex: 1;
.tablist>li {
list-style: none;
}

.tab-nav>label {
display: block;
box-sizing: border-box;
/* tab content must clear this */
height: 2.5rem;

padding: 10px;
text-align: center;
cursor: pointer;
transition: background 0.5s ease;
.tablist>li>button {
padding: 0.5rem;
background-color: transparent;
border: none;
color: black;
}
font-size: 1rem;
font-family: inherit;
cursor: pointer;

.tab-nav>label:hover {
background: #d3d3d3;
border-radius: 0.5rem 0.5rem 0 0;
&:hover {
background-color: #d3d3d3;
border-radius: .5rem .5rem 0 0;
}
}

.tab-content {
position: absolute;
.tablist>li>[aria-selected="true"] {
border: 1px solid #d3d3d3;
height: max-content;

left: 0;
bottom: 0;
right: 0;
/* clear the tab labels */
top: 2.5rem;

border-radius: 0 0 0.5rem 0.5rem;

transition:
opacity 0.8s ease,
transform 0.8s ease;

/* show/hide */
opacity: 0;
transform: scale(0.1);
transform-origin: top left;
border-bottom: none;
border-radius: .5rem .5rem 0 0;
}

.tabs-wrapper {
flex: 1;
position: relative;
padding: 1rem;
}

/* MAKE IT WORK ----- */
.tab-nav [type=radio] {
display: none;
/* From Every Layout (https://every-layout.dev/) */
.switcher>* {
display: flex;
flex-wrap: wrap;
margin: 0;
padding-left: 0;
}

[type=radio]:checked~label {
z-index: 2;
border: 1px solid #d3d3d3;
border-bottom: none;
border-radius: 0.5rem 0.5rem 0 0;
.switcher>*>* {
flex-grow: 1;
flex-basis: auto;
margin: 0;
}

[type=radio]:checked~label~.tab-content {
z-index: 1;
.switcher>*> :nth-last-child(n + 5),
.switcher>*> :nth-last-child(n + 5)~* {
flex-basis: 100%;
}

/* show/hide */
opacity: 1;
transform: scale(1);
.tabpanel {
border: 1px solid #d3d3d3;
border-radius: 0 0 0.5rem 0.5rem;
}
}

Expand Down

0 comments on commit 9f68dc7

Please sign in to comment.