Skip to content

Commit

Permalink
Global search - keyboard + hiding
Browse files Browse the repository at this point in the history
  • Loading branch information
anselmbradford committed Feb 5, 2016
1 parent 54a5fa1 commit 48a446f
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 87 deletions.
2 changes: 2 additions & 0 deletions cfgov/jinja2/v1/_includes/molecules/global-search.html
Expand Up @@ -44,12 +44,14 @@
<div class="m-global-search_content-suggestions">
<p class="h5">Suggested search terms:</p>
<ul class="list list__horizontal">
{# TODO: Add suggested link URLs. #}
<li class="list_item"><a class="list_link" href="#">Regulations</a></li>
<li class="list_item"><a class="list_link" href="#">Compliance guides</a></li>
<li class="list_item"><a class="list_link" href="#">Mortgage</a></li>
<li class="list_item"><a class="list_link" href="#">College loans</a></li>
</ul>
</div>
</form>
<button class="m-global-search_tab-trigger" aria-hidden></button>
</div>
</div>
16 changes: 16 additions & 0 deletions cfgov/unprocessed/css/misc.less
Expand Up @@ -631,6 +631,22 @@
text-transform: uppercase;
}

// Creates semi-transparent drop-shadow after element.
.u-drop-shadow-after() {
&:after {
display: block;
height: 5px;
width: 100%;

position: relative;
top: 5px;

background: @gray;
// Whitespace in content so element can have dimensions set.
content: '\a0';
opacity: 0.2;
}
}

/* topdoc
name: EOF
Expand Down
33 changes: 20 additions & 13 deletions cfgov/unprocessed/css/molecules/global-search.less
Expand Up @@ -94,7 +94,7 @@
- cfgov-molecules
*/

.m-global-search {
.js .m-global-search {

// Absolute position when there is a flyout at mobile/tablet sizes.
.respond-to-max( @bp-sm-max, {
Expand Down Expand Up @@ -179,6 +179,7 @@
}

&_content {

.respond-to-min( @bp-med-min {
display: none;
} );
Expand All @@ -188,7 +189,7 @@
&[aria-expanded="true"] {
display: block;

transform: translateX(0);
transform: translateX( 0 );
}

// Create flyout at table/mobile size.
Expand Down Expand Up @@ -217,17 +218,7 @@
padding-bottom: @margin_half__em;
}

// Drop-shadow under the search flyout menu at mobile/tablet size.
&:after {
position: relative;
top: 5px;
content: '\a0';
display: block;
height: 5px;
width: 100%;
opacity: 0.2;
background: @gray;
}
.u-drop-shadow-after();
} );

&-suggestions {
Expand Down Expand Up @@ -267,6 +258,22 @@
} );
}

.m-global-search {
// Tab trigger is used to capture press of the tab key so that
// global search can be collapsed when it hits this element.
&_tab-trigger {
position: absolute;
top: -9999px;
left: -9999px;
}
}

.no-js .m-global-search {
&_trigger {
display: none;
}
}

/* topdoc
name: EOF
eof: true
Expand Down
160 changes: 160 additions & 0 deletions cfgov/unprocessed/js/modules/FlyoutMenu.js
@@ -0,0 +1,160 @@
'use strict';

// Required modules.
var EventObserver = require( '../modules/util/EventObserver' );

/**
* FlyoutMenu
* @class
*
* @classdesc Initializes a new FlyoutMenu molecule.
*
* @param {HTMLNode} element
* The DOM element within which to search for the molecule.
* @param {string} triggerSel - The selector for the menu trigger.
* @param {string} contentSel - The selector for the menu content.
* @param {string} altTriggerSel - The selector for a second menu trigger.
* @returns {Object} A FlyoutMenu instance.
*/
function FlyoutMenu( element, triggerSel, contentSel, altTriggerSel ) {

var _isExpanded = false;

var _triggerDom = element.querySelector( triggerSel );
var _contentDom = element.querySelector( contentSel );
var _altTriggerDom = element.querySelector( altTriggerSel );

var _transitionEndEvent = _getTransitionEndEvent( _contentDom );

/**
* @returns {Object} The FlyoutMenu instance.
*/
function init() {
_triggerDom.addEventListener( 'click', _triggerClicked.bind( this ) );

if ( altTriggerSel ) {
_altTriggerDom.addEventListener( 'click', _triggerClicked.bind( this ) );
}

return this;
}

/**
* Event handler for when the search input trigger is clicked,
* which opens/closes the search input.
* @param {MouseEvent} event - The flyout trigger was clicked.
*/
function _triggerClicked( event ) {
event.preventDefault();
this.dispatchEvent( 'triggerClick', { target: event.target } );
if ( _isExpanded ) {
this.collapse();
} else {
this.expand();
}
}

/**
* Open the search box.
* @returns {Object} A FlyoutMenu instance.
*/
function expand() {
if ( !_isExpanded ) {
this.dispatchEvent( 'expandBegin', { target: this } );
_isExpanded = true;
_addTransitionListener( this, _contentDom, _expandEnd );
_triggerDom.setAttribute( 'aria-expanded', 'true' );
_contentDom.setAttribute( 'aria-expanded', 'true' );
}

return this;
}

/**
* Close the search box.
* @returns {Object} A FlyoutMenu instance.
*/
function collapse() {
if ( _isExpanded ) {
this.dispatchEvent( 'collapseBegin', { target: this } );
_isExpanded = false;
_addTransitionListener( this, _contentDom, _collapseEnd );
_triggerDom.setAttribute( 'aria-expanded', 'false' );
_contentDom.setAttribute( 'aria-expanded', 'false' );
}

return this;
}

/**
* Transition height property and call a callback function.
* Call callback directly if CSS transitions aren't supported.
* @param {FlyoutMenu} target - Reference to this instance.
* @param {HTMLNode} dom - Element on which to listen for transition event.
* @param {Function}
* callback - Callback function for completion of transition.
*/
function _addTransitionListener( target, dom, callback ) {
if ( _transitionEndEvent ) {
dom.addEventListener( _transitionEndEvent, callback.bind( target ) );
} else {
// TODO: Remove callback-return ESLint ignore
callback(); // eslint-disable-line callback-return, inline-comments, max-len
}
}

/**
* Expand animation has completed.
*/
function _expandEnd() {
_contentDom.removeEventListener( _transitionEndEvent, _expandEnd );
this.dispatchEvent( 'expandEnd', { target: this } );
}

/**
* Collapse animation has completed.
*/
function _collapseEnd() {
_contentDom.removeEventListener( _transitionEndEvent, _collapseEnd );
this.dispatchEvent( 'collapseEnd', { target: this } );
}

// Attach public events.
var eventObserver = new EventObserver();
this.addEventListener = eventObserver.addEventListener;
this.removeEventListener = eventObserver.removeEventListener;
this.dispatchEvent = eventObserver.dispatchEvent;

this.init = init;
this.expand = expand;
this.collapse = collapse;

return this;
}

// TODO: Move to a utility module and share this between Expandables and here.
/**
* @param {HTMLNode} elm
* The element to check for support of transition end event.
* @returns {string} The browser-prefixed transition end event.
*/
function _getTransitionEndEvent( elm ) {
var transition;
var transitions = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
};

for ( var t in transitions ) {
if ( transitions.hasOwnProperty( t ) &&
typeof elm.style[t] !== 'undefined' ) {
transition = transitions[t];
break;
}
}
return transition;
}

module.exports = FlyoutMenu;

0 comments on commit 48a446f

Please sign in to comment.