Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 9.2.1 #508

Merged
merged 4 commits into from
Mar 13, 2019
Merged
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
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
**For example**
- DP-1234: The short description text on a [service detail](http://mayflower.digital.mass.gov/?p=pages-detail-for-service-howto-location) page banner ([@organisms/by-template/page-banner](http://mayflower.digital.mass.gov/?p=organisms-page-banner)) should now render ([PR #493](https://github.com/massgov/mayflower/pull/493))

## 9.2.0 (05/05/2019)
## 9.2.1 (03/13/2019)

### Fixed
- (React) [TabContainer] DP-10868: Adds a11y support to Tab and TabContainer components.#497

### Added
- (Patternlab) [OrganizationNavigation] DP-12928: Add link list specific classes: `ma__org-nav-i-want-to__findService`, `ma__org-nav-i-want-to__learnAbout`,`ma__org-nav-i-want-to__login` to sections for GTM. #504

## 9.2.0 (03/05/2019)

### Added
- (Patternlab) [Header] DP-4562: Set focus state for search on mobile menu in mobileNav module. #473
Expand Down
2 changes: 1 addition & 1 deletion assets/scss/00-base/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ $c-error: $c-error-red;
$c-error-lighter: $c-error-red-lighter;
$c-error-lightest: $c-error-red-lightest;
$c-warning: $c-duckling-yellow;
$c-focus: $c-bay-blue-lighter;
$c-focus: #3e94cf;
// Need to add these colors to this listing new to add
// $c-success $c-focus $c-visited

Expand Down
57 changes: 39 additions & 18 deletions assets/scss/03-organisms/_tab-container.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

@mixin tabscroll {
flex-wrap: nowrap;
overflow-x: auto;
Expand All @@ -8,34 +9,39 @@
}
}

%focus {
&:focus {
outline: none;
box-shadow: inset 0px 0px 2px 2px $c-focus;
}
}

.ma__tab-container {
display: flex;
flex-direction: column;

&-head {
display: inline-flex;
margin-bottom: 0;
padding-inline-start: 0;
list-style: none;
@include tabscroll;
}

&-body {
padding: 1rem;
border: 1px solid $c-gray-light;
margin-top: -1px;

@extend %focus;
}

.ma__tab-title {
cursor: pointer;
padding: 1.75rem 1.5rem 0.8rem 1.5rem;
font-size: 1.25rem;
background-color: unset;
white-space: nowrap;
border-left: 1px solid $c-gray-light;
border-top: 1px solid $c-gray-light;
border-right: none;
border-bottom: none;
margin-bottom: -1px;
display: block;

&:first-child {
border-left-width: 1px;
Expand All @@ -45,19 +51,30 @@
border-right: 1px solid $c-gray-light;
}

&:focus {
outline: none;
border-top-color: $c-primary !important;
border-left-color: $c-gray-light !important;

button {
cursor: pointer;
padding: 1.77rem 1.5rem 0.81rem 1.5rem;
background-color: unset;
white-space: nowrap;
border: none;
display: block;
font-size: 1.25rem;
}

&--active {
border-top: 5px solid $c-primary;
padding-top: 1.5rem;
font-weight: 700;
color: $c-primary;
background-color: white;
z-index: 1;
outline: $c-error-red;

button {
padding-top: 1.5rem;
font-weight: 700;
color: $c-primary;

@extend %focus;
}
}
}

Expand All @@ -69,23 +86,27 @@

&-body {
border: none;
@extend %focus;
}
}

.ma__tab-title {
border: none;
border-bottom: 5px solid;
border-bottom-color: transparent;
padding: 1.5rem 1.5rem 0.8rem 1.5rem;

button {
padding: 1.5rem 1.5rem 0.8rem 1.5rem;
}

&--active {
color: $c-primary;
border-bottom: 5px solid $c-primary;
}

&:focus {
outline: none;
border-bottom-color: $c-primary !important;
button {
padding-bottom: 1.25rem;
@extend %focus;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block findService %}
{% if findService %}
<div class="ma__org-nav-i-want-to-section">
<div class="ma__org-nav-i-want-to-section ma__org-nav-i-want-to__findService">
{% set linkList = findService.linkList %}
{% include "@organisms/by-author/link-list.twig" %}
</div>
Expand All @@ -11,7 +11,7 @@

{% block learnAbout %}
{% if learnAbout %}
<div class="ma__org-nav-i-want-to-section">
<div class="ma__org-nav-i-want-to-section ma__org-nav-i-want-to__learnAbout">
{% set linkList = learnAbout.linkList %}
{% include "@organisms/by-author/link-list.twig" %}
</div>
Expand All @@ -20,7 +20,7 @@

{% block login %}
{% if login.href %}
<div class="ma__org-nav-i-want-to-section">
<div class="ma__org-nav-i-want-to-section ma__org-nav-i-want-to__login">
<h3 class="ma__comp-heading">Log in to...</h3>
<a class="ma__link-list__container" href="{{ login.href }}">{{ login.text }}</a>
</div>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 154 additions & 10 deletions react/src/components/organisms/TabContainer/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import shortid from 'shortid';
import './style.css';
import { TabContext } from './context';

class TabContainer extends React.Component {
constructor(props) {
super(props);
// Allows Tab to interact directly with the tab body div through context.
this.tabBodyRef = React.createRef();
// When false, runs key down code for nested containers.
this.preventBodyKeyDown = false;
this.defaultContent = null;
this.useDefault = true;
const tabIds = new Map();
// This only works for class components because it is not re-generated on every render.
this.spanId = shortid.generate();
this.setActiveTab = (tab, content = null) => {
const state = {
activeTab: tab
};
if (content) {
this.defaultContent = null;
state.activeContent = content;
}
this.setState(state);
};
this.focusOnTabBody = () => {
this.tabBodyRef.current.setAttribute('tabindex', '0');
this.tabBodyRef.current.focus();
};
this.focusTab = (tabId) => {
if (this.tabRefs[tabId] && this.tabRefs[tabId].current) {
this.tabRefs[tabId].current.focus();
}
};
const tabRefs = {};
let activeContent = null;
React.Children.forEach(props.children, (child, index) => {
tabIds.set(index, shortid.generate());
tabRefs[tabIds.get(index)] = React.createRef();
if (index === props.defaultTab) {
activeContent = child.props.children;
}
});
this.state = {
activeTab: null,
activeContent: null,
activeTab: tabIds.get(props.defaultTab),
activeContent,
// eslint-disable-next-line react/no-unused-state
setActiveTab: this.setActiveTab
setActiveTab: this.setActiveTab,
// eslint-disable-next-line react/no-unused-state
focusOnTabBody: this.focusOnTabBody,
// eslint-disable-next-line react/no-unused-state
focusTab: this.focusTab,
tabIds,
tabRefs,
tabContainerId: shortid.generate(),
tabContainerBodyId: shortid.generate()
};
}

Expand All @@ -29,21 +66,128 @@ class TabContainer extends React.Component {
'ma__tab-container': true,
'ma__tab-container--nested': this.props.nested
});
const ulClasses = classNames({
'ma__tab-container-head': true,
'ma__tab-container-head--nested': this.props.nested,
'ma__tab-container-head--parent': !this.props.nested
});
// eslint-disable-next-line react/prop-types
const { children, defaultTab } = this.props;
const active = defaultTab;
this.childrenWithProps = React.Children.map(children, (child, index) => {
if (index === active) {
return React.cloneElement(child, { default: true });
const newProps = {
default: index === defaultTab,
tabIdent: this.state.tabIds.get(index),
ref: this.state.tabRefs[this.state.tabIds.get(index)]
};
if (this.useDefault) {
if (active === index) {
newProps.default = true;
newProps.active = true;
} else {
newProps.default = false;
newProps.active = false;
}
this.useDefault = false;
} else {
newProps.default = false;
if (this.state.tabIds.get(index) === this.state.activeTab) {
newProps.active = true;
} else {
newProps.active = false;
}
}
return child;
return(child.props.children) ? React.cloneElement(child, newProps, child.props.children) : React.cloneElement(child, newProps);
});
return(
<TabContext.Provider value={this.state}>
<div className={classes}>
<div className="ma__tab-container-head">{this.childrenWithProps}
</div>
{this.state.activeTab && <div className="ma__tab-container-body">{this.state.activeContent}</div>}
<div
id={this.state.tabContainerId}
className={classes}
onKeyDown={(e) => {
if (e.key === 'ArrowUp') {
// If the active focus is on the body, move focus to the current tab.
if (document.activeElement === this.tabBodyRef.current) {
const currentTab = this.state.activeTab;
this.preventBodyKeyDown = false;
this.state.tabRefs[currentTab].current.removeAttribute('tabindex');
this.state.tabRefs[currentTab].current.focus();
} else if (this.props.nested) {
// If the tab container is nested and active focus is not on the body,
// set focus to the parent tab container.
const tab = e.currentTarget
.parentElement
.closest('div.ma__tab-container')
.getElementsByClassName('ma__tab-title--active')[0]
.getElementsByTagName('button')[0];
tab.removeAttribute('tabindex');
tab.focus();
this.preventBodyKeyDown = true;
} else {
this.preventBodyKeyDown = false;
}
}
if (e.key === 'ArrowDown') {
// If the tab container is not nested and has no nested tab container children...
if (!this.props.nested && this.tabBodyRef.current.getElementsByClassName('ma__tab-container--nested').length === 0) {
// Set focus on the tab container's body.
this.focusOnTabBody();
}
// If the tab container is not nested and has nested tab container children...
if (!this.preventBodyKeyDown && !this.props.nested && this.tabBodyRef.current.getElementsByClassName('ma__tab-container--nested').length > 0) {
// Set focus on the first tab of the nested tab container child.
const nested = this.tabBodyRef.current
.getElementsByClassName('ma__tab-container--nested')[0]
.getElementsByTagName('ul')[0]
.getElementsByClassName('ma__tab-title--active')[0]
.getElementsByTagName('button')[0];
nested.removeAttribute('tabindex');
this.tabBodyRef.current.removeAttribute('tabindex');
nested.focus();
// The next key down arrow should not run on the parent tab container, but on the child it will.
this.preventBodyKeyDown = true;
}
// If the tab container is nested...
if (this.props.nested) {
// And has nested tab containers...
if (this.tabBodyRef.current.getElementsByClassName('ma__tab-container--nested').length > 0) {
// Set focus on the first tab of the nested tab container child.
const nested = this.tabBodyRef.current
.getElementsByClassName('ma__tab-container--nested')[0]
.getElementsByTagName('ul')[0]
.getElementsByClassName('ma__tab-title--active')[0]
.getElementsByTagName('button')[0];
nested.removeAttribute('tabindex');
this.tabBodyRef.current.removeAttribute('tabindex');
nested.focus();
// The next key down arrow should not run on the parent tab container, but on the child it will.
this.preventBodyKeyDown = true;
} else {
this.focusOnTabBody();
}
}
}
}}
>
<span id={this.spanId} className="ma__visually-hidden">Use left and right arrows to navigate between tabs, up and down arrows to navigate between active tab and its content.</span>
<ul className={ulClasses} role="tablist">
{this.childrenWithProps}
</ul>
{
(
<div
aria-labelledby={this.state.activeTab}
className="ma__tab-container-body"
tabIndex={0}
ref={this.tabBodyRef}
role="tabpanel"
id={this.state.tabContainerBodyId}

>
{this.state.activeContent}
</div>
)
}
</div>
</TabContext.Provider>
);
Expand Down
Loading