From 9d55cf44a46371125e424d5dd2205598e325cdf7 Mon Sep 17 00:00:00 2001 From: Kevin Ball Date: Fri, 19 May 2017 16:44:03 -0700 Subject: [PATCH 01/25] Add visual test --- test/visual/dropdown/offsets.html | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/visual/dropdown/offsets.html diff --git a/test/visual/dropdown/offsets.html b/test/visual/dropdown/offsets.html new file mode 100644 index 0000000000..5f847019d6 --- /dev/null +++ b/test/visual/dropdown/offsets.html @@ -0,0 +1,49 @@ + + + + + + + Foundation for Sites Testing + + + +
+

Dropdown: Positioning Content

+ +

These dropdowns test various positioning and position offsets

+ +

This dropdown should be offset by 10 down and 30 to the right

+ + +

This dropdown has position left and alignment center, and should go off the screen left because it has allow-overlap true

+ + +

This dropdown has position left and should go left if there is room and otherwise below

+ + +

This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button

+ + +

This dropdown has position left and alignment top should align with its bottom right corner at the bottom left corner of the butotn

+ + +
+ + + + + + From 4a4814ab48a117b452961ce30cf297f213609d9e Mon Sep 17 00:00:00 2001 From: Kevin Ball Date: Fri, 11 Nov 2016 13:16:22 -0800 Subject: [PATCH 02/25] Box should always take into account vOffset and hOffset --- js/foundation.util.box.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js index d1f220928e..ca13f8c9cf 100644 --- a/js/foundation.util.box.js +++ b/js/foundation.util.box.js @@ -116,55 +116,55 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { switch (position) { case 'top': return { - left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left), + left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset), top: $anchorDims.offset.top - ($eleDims.height + vOffset) } break; case 'left': return { left: $anchorDims.offset.left - ($eleDims.width + hOffset), - top: $anchorDims.offset.top + top: $anchorDims.offset.top + vOffset } break; case 'right': return { left: $anchorDims.offset.left + $anchorDims.width + hOffset, - top: $anchorDims.offset.top + top: $anchorDims.offset.top + vOffset } break; case 'center top': return { - left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2), + left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2) + hOffset, top: $anchorDims.offset.top - ($eleDims.height + vOffset) } break; case 'center bottom': return { - left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)), + left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset, top: $anchorDims.offset.top + $anchorDims.height + vOffset } break; case 'center left': return { left: $anchorDims.offset.left - ($eleDims.width + hOffset), - top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + top: ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2) } break; case 'center right': return { left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1, - top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + vOffset } break; case 'center': return { - left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2), - top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2) + left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset, + top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset) } break; case 'reveal': return { - left: ($eleDims.windowDims.width - $eleDims.width) / 2, + left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset, top: $eleDims.windowDims.offset.top + vOffset } case 'reveal full': @@ -175,7 +175,7 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { break; case 'left bottom': return { - left: $anchorDims.offset.left, + left: $anchorDims.offset.left - hOffset, top: $anchorDims.offset.top + $anchorDims.height + vOffset }; break; @@ -187,7 +187,7 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { break; default: return { - left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left + hOffset), + left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset), top: $anchorDims.offset.top + $anchorDims.height + vOffset } } From f224b0c8da35e50f06deba114dcfb28d4bea6e8a Mon Sep 17 00:00:00 2001 From: Kevin Ball Date: Fri, 19 May 2017 21:32:34 -0700 Subject: [PATCH 03/25] Create explicit positioning method for Box and move dropdown positioning to be EXPLICIT --- js/foundation.dropdown.js | 194 +++++++++++------- js/foundation.util.box.js | 137 ++++++++----- .../visual/dropdown/explicit-positioning.html | 127 ++++++++++++ test/visual/dropdown/offsets.html | 130 +++++++++--- test/visual/dropdown/overflow.html | 92 +++++++++ 5 files changed, 531 insertions(+), 149 deletions(-) create mode 100644 test/visual/dropdown/explicit-positioning.html create mode 100644 test/visual/dropdown/overflow.html diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js index db63ba200e..5b078b1e0e 100644 --- a/js/foundation.dropdown.js +++ b/js/foundation.dropdown.js @@ -3,7 +3,7 @@ import $ from 'jquery'; import { Keyboard } from './foundation.util.keyboard'; import { Box } from './foundation.util.box'; -import { GetYoDigits } from './foundation.util.core'; +import { GetYoDigits, rtl as Rtl } from './foundation.util.core'; import { Plugin } from './foundation.plugin'; // import "foundation.util.triggers.js"; @@ -18,6 +18,17 @@ import { Plugin } from './foundation.plugin'; * @requires foundation.util.triggers */ +const POSITIONS = ['left', 'right', 'top', 'bottom']; +const VERTICAL_ALIGNMENTS = ['top', 'bottom', 'center']; +const HORIZONTAL_ALIGNMENTS = ['left', 'right', 'center']; + +const ALIGNMENTS = { + 'left': VERTICAL_ALIGNMENTS, + 'right': VERTICAL_ALIGNMENTS, + 'top': HORIZONTAL_ALIGNMENTS, + 'bottom': HORIZONTAL_ALIGNMENTS +} + class Dropdown extends Plugin { /** * Creates a new instance of a dropdown. @@ -61,9 +72,9 @@ class Dropdown extends Plugin { }else{ this.$parent = null; } - this.options.positionClass = this.getPositionClass(); - this.counter = 4; - this.usedPositions = []; + this._setupPositionAndAlignment(); + + this.triedPositions = {}; this.$element.attr({ 'aria-hidden': 'true', 'data-yeti-box': $id, @@ -73,6 +84,18 @@ class Dropdown extends Plugin { this._events(); } + _setupPositionAndAlignment() { + if(this.options.position === 'left' || this.options.position === 'right') { + this.isHorizontallyPositioned = true; + } + if(this.options.position === 'top' || this.options.position === 'bottom') { + this.isVerticallyPositioned = true; + } + + this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position; + this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment; + } + /** * Helper function to determine current orientation of dropdown pane. * @function @@ -88,86 +111,97 @@ class Dropdown extends Plugin { return position; } + _getDefaultPosition() { + // handle legacy classnames + var position = this.$element[0].className.match(/(top|left|right|bottom)/g); + if(position) { + return position[0]; + } else { + return 'bottom' + } + } + + _getDefaultAlignment() { + // handle legacy float appraoch + var horizontalPosition = /float-(\S+)/.exec(this.$anchor[0].className); + if(horizontalPosition) { + return horizontalPosition[1]; + } + + switch(this.position) { + case 'bottom': + case 'top': + return Rtl() ? 'left' : 'right'; + case 'left': + case 'right': + return 'bottom'; + } + } + /** - * Adjusts the dropdown panes orientation by adding/removing positioning classes. + * Adjusts the dropdown pane possible positions by iterating through alignments + * and positions. NOTE: Only used if position is auto, otherwise only alignments + * will be tried within the specified position. * @function * @private - * @param {String} position - position class to remove. */ - _reposition(position) { - this.usedPositions.push(position ? position : 'bottom'); - //default, try switching to opposite side - if(!position && (this.usedPositions.indexOf('top') < 0)){ - this.$element.addClass('top'); - }else if(position === 'top' && (this.usedPositions.indexOf('bottom') < 0)){ - this.$element.removeClass(position); - }else if(position === 'left' && (this.usedPositions.indexOf('right') < 0)){ - this.$element.removeClass(position) - .addClass('right'); - }else if(position === 'right' && (this.usedPositions.indexOf('left') < 0)){ - this.$element.removeClass(position) - .addClass('left'); - } + _reposition() { + } + - //if default change didn't work, try bottom or left first - else if(!position && (this.usedPositions.indexOf('top') > -1) && (this.usedPositions.indexOf('left') < 0)){ - this.$element.addClass('left'); - }else if(position === 'top' && (this.usedPositions.indexOf('bottom') > -1) && (this.usedPositions.indexOf('left') < 0)){ - this.$element.removeClass(position) - .addClass('left'); - }else if(position === 'left' && (this.usedPositions.indexOf('right') > -1) && (this.usedPositions.indexOf('bottom') < 0)){ - this.$element.removeClass(position); - }else if(position === 'right' && (this.usedPositions.indexOf('left') > -1) && (this.usedPositions.indexOf('bottom') < 0)){ - this.$element.removeClass(position); + /** + * Adjusts the dropdown pane possible positions by iterating through alignments + * on the current position. + * @function + * @private + */ + _realign() { + this._addTriedPosition(this.position, this.alignment) + var alignments = ALIGNMENTS[this.position] + var currentIdx = alignments.indexOf(this.alignment); + if(currentIdx === alignments.length - 1) { + this.alignment = alignments[0]; + } else { + this.alignment = alignments[currentIdx + 1]; } - //if nothing cleared, set to bottom - else{ - this.$element.removeClass(position); + } + + _addTriedPosition(position, alignment) { + this.triedPositions[position] = this.triedPositions[position] || [] + this.triedPositions[position].push(alignment); + } + + _positionsExhausted() { + if(this.options.position === 'auto') { + } else { + return this.triedPositions[this.position] && this.triedPositions[this.position].length == ALIGNMENTS[this.position].length; } - this.classChanged = true; - this.counter--; } /** - * Sets the position and orientation of the dropdown pane, checks for collisions. + * Sets the position and orientation of the dropdown pane, checks for collisions if allow-overlap is not true. * Recursively calls itself if a collision is detected, with a new position class. * @function * @private */ _setPosition() { if(this.$anchor.attr('aria-expanded') === 'false'){ return false; } - var position = this.getPositionClass(), - $eleDims = Box.GetDimensions(this.$element), - $anchorDims = Box.GetDimensions(this.$anchor), - _this = this, - direction = (position === 'left' ? 'left' : ((position === 'right') ? 'left' : 'top')), - param = (direction === 'top') ? 'height' : 'width', - offset = (param === 'height') ? this.options.vOffset : this.options.hOffset; - - if(($eleDims.width >= $eleDims.windowDims.width) || (!this.counter && !Box.ImNotTouchingYou(this.$element, this.$parent))){ - var newWidth = $eleDims.windowDims.width, - parentHOffset = 0; - if(this.$parent){ - var $parentDims = Box.GetDimensions(this.$parent), - parentHOffset = $parentDims.offset.left; - if ($parentDims.width < newWidth){ - newWidth = $parentDims.width; - } - } + var $eleDims = Box.GetDimensions(this.$element), + $anchorDims = Box.GetDimensions(this.$anchor); - this.$element.offset(Box.GetOffsets(this.$element, this.$anchor, 'center bottom', this.options.vOffset, this.options.hOffset + parentHOffset, true)).css({ - 'width': newWidth - (this.options.hOffset * 2), - 'height': 'auto' - }); - this.classChanged = true; - return false; - } - this.$element.offset(Box.GetOffsets(this.$element, this.$anchor, position, this.options.vOffset, this.options.hOffset)); + this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset)); - while(!Box.ImNotTouchingYou(this.$element, this.$parent, true) && this.counter){ - this._reposition(position); - this._setPosition(); + if(!this.options.allowOverlap) { + while(!Box.ImNotTouchingYou(this.$element, this.$parent, this.isVerticallyPositioned, this.isHorizontallyPositioned) && !this._positionsExhausted()){ + if(this.options.position === 'auto') { + this._reposition(); + } else { + console.log('realigning'); + this._realign(); + } + this._setPosition(); + } } } @@ -393,16 +427,16 @@ Dropdown.defaults = { * Number of pixels between the dropdown pane and the triggering element on open. * @option * @type {number} - * @default 1 + * @default 0 */ - vOffset: 1, + vOffset: 0, /** * Number of pixels between the dropdown pane and the triggering element on open. * @option * @type {number} - * @default 1 + * @default 0 */ - hOffset: 1, + hOffset: 0, /** * Class applied to adjust open position. JS will test and fill this in. * @option @@ -410,6 +444,28 @@ Dropdown.defaults = { * @default '' */ positionClass: '', + + /** + * Position of dropdown. Can be left, right, bottom, top, or auto. + * @option + * @type {string} + * @default 'auto' + */ + position: 'auto', + /** + * Alignment of dropdown relative to anchor. Can be left, right, bottom, top, center, or auto. + * @option + * @type {string} + * @default 'auto' + */ + alignment: 'auto', + /** + * Allow overlap of container/window. If false, dropdown will first try to position as defined by data-position and data-alignment, but reposition if it would cause an overflow. + * @option + * @type {boolean} + * @default false + */ + allowOverlap: false, /** * Allow the plugin to trap focus to the dropdown pane if opened with keyboard commands. * @option diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js index ca13f8c9cf..a5c6d082fb 100644 --- a/js/foundation.util.box.js +++ b/js/foundation.util.box.js @@ -6,7 +6,8 @@ import { rtl as Rtl } from "./foundation.util.core"; var Box = { ImNotTouchingYou: ImNotTouchingYou, GetDimensions: GetDimensions, - GetOffsets: GetOffsets + GetOffsets: GetOffsets, + GetExplicitOffsets: GetExplicitOffsets } /** @@ -99,7 +100,9 @@ function GetDimensions(elem, test){ /** * Returns an object of top and left integer pixel values for dynamically rendered elements, - * such as: Tooltip, Reveal, and Dropdown + * such as: Tooltip, Reveal, and Dropdown. Maintained for backwards compatibility, and where + * you don't know alignment, but generally from + * 6.4 forward you should use GetExplicitOffsets, as GetOffsets conflates position and alignment. * @function * @param {jQuery} element - jQuery object for the element being positioned. * @param {jQuery} anchor - jQuery object for the element's anchor point. @@ -110,58 +113,34 @@ function GetDimensions(elem, test){ * TODO alter/rewrite to work with `em` values as well/instead of pixels */ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { - var $eleDims = GetDimensions(element), - $anchorDims = anchor ? GetDimensions(anchor) : null; - switch (position) { case 'top': - return { - left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset), - top: $anchorDims.offset.top - ($eleDims.height + vOffset) - } - break; - case 'left': - return { - left: $anchorDims.offset.left - ($eleDims.width + hOffset), - top: $anchorDims.offset.top + vOffset - } - break; - case 'right': - return { - left: $anchorDims.offset.left + $anchorDims.width + hOffset, - top: $anchorDims.offset.top + vOffset - } - break; + return Rtl() ? + GetExplicitOffsets(element, anchor, 'top', 'left', vOffset, hOffset, isOverflow) : + GetExplicitOffsets(element, anchor, 'top', 'right', vOffset, hOffset, isOverflow); + case 'bottom': + return Rtl() ? + GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow) : + GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow); case 'center top': - return { - left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2) + hOffset, - top: $anchorDims.offset.top - ($eleDims.height + vOffset) - } - break; + return GetExplicitOffsets(element, anchor, 'top', 'center', vOffset, hOffset, isOverflow); case 'center bottom': - return { - left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset, - top: $anchorDims.offset.top + $anchorDims.height + vOffset - } - break; + return GetExplicitOffsets(element, anchor, 'bottom', 'center', vOffset, hOffset, isOverflow); case 'center left': - return { - left: $anchorDims.offset.left - ($eleDims.width + hOffset), - top: ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2) - } - break; + return GetExplicitOffsets(element, anchor, 'left', 'center', vOffset, hOffset, isOverflow); case 'center right': - return { - left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1, - top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + vOffset - } - break; + return GetExplicitOffsets(element, anchor, 'right', 'center', vOffset, hOffset, isOverflow); + case 'left bottom': + return GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow); + case 'right bottom': + return GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow); + // Backwards compatibility... this along with the reveal and reveal full + // classes are the only ones that didn't reference anchor case 'center': return { left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset, top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset) } - break; case 'reveal': return { left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset, @@ -173,24 +152,72 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { top: $eleDims.windowDims.offset.top } break; - case 'left bottom': - return { - left: $anchorDims.offset.left - hOffset, - top: $anchorDims.offset.top + $anchorDims.height + vOffset - }; - break; - case 'right bottom': - return { - left: $anchorDims.offset.left + $anchorDims.width + hOffset - $eleDims.width, - top: $anchorDims.offset.top + $anchorDims.height + vOffset - }; - break; default: return { left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset), top: $anchorDims.offset.top + $anchorDims.height + vOffset } + + } + +} + +function GetExplicitOffsets(element, anchor, position, alignment, vOffset, hOffset, isOverflow) { + var $eleDims = GetDimensions(element), + $anchorDims = anchor ? GetDimensions(anchor) : null; + + var topVal, leftVal; + + // set position related attribute + + switch (position) { + case 'top': + topVal = $anchorDims.offset.top - ($eleDims.height + vOffset); + break; + case 'bottom': + topVal = $anchorDims.offset.top + $anchorDims.height + vOffset; + break; + case 'left': + leftVal = $anchorDims.offset.left - ($eleDims.width + hOffset); + break; + case 'right': + leftVal = $anchorDims.offset.left + $anchorDims.width + hOffset; + break; + } + + + // set alignment related attribute + switch (position) { + case 'top': + case 'bottom': + switch (alignment) { + case 'left': + leftVal = $anchorDims.offset.left + hOffset; + break; + case 'right': + leftVal = $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset; + break; + case 'center': + leftVal = isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset; + break; + } + break; + case 'right': + case 'left': + switch (alignment) { + case 'bottom': + topVal = $anchorDims.offset.top - vOffset + $anchorDims.height - $eleDims.height; + break; + case 'top': + topVal = $anchorDims.offset.top + vOffset + break; + case 'center': + topVal = ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2) + break; + } + break; } + return {top: topVal, left: leftVal}; } export {Box}; diff --git a/test/visual/dropdown/explicit-positioning.html b/test/visual/dropdown/explicit-positioning.html new file mode 100644 index 0000000000..935ca2422f --- /dev/null +++ b/test/visual/dropdown/explicit-positioning.html @@ -0,0 +1,127 @@ + + + + + + + Foundation for Sites Testing + + + +
+

Dropdown: Explicit Positioning Content - no offsets

+ +

These dropdowns test various positioning and alignments. Valid + positions are left/right/top/bottom. Valid alignments are + left/right/top/bottom/center. Left align means left sides should line up. + Right align means right sides should line up. Center align means centers should line up. +

+ +

Top and Bottom positioned

+
+
+

Bottom Left

+ + +
+ +
+

Bottom Center

+ + +
+ +
+

Bottom Right

+ + +
+ +
+

Top Left

+ + +
+ +
+

Top Center

+ + +
+ +
+

Top Right

+ + +
+
+ + +

Left and Right Positioned

+
+
+

Right Top

+ + +
+
+

Left Top

+ + +
+ +
+

Right Center

+ + +
+
+

Left Center

+ + +
+ +
+

Right Bottom

+ + +
+
+

Left Bottom

+ + +
+
+
+ + + + + + diff --git a/test/visual/dropdown/offsets.html b/test/visual/dropdown/offsets.html index 5f847019d6..25ec2a8e08 100644 --- a/test/visual/dropdown/offsets.html +++ b/test/visual/dropdown/offsets.html @@ -9,34 +9,114 @@
-

Dropdown: Positioning Content

+

Dropdown: Explicit Positioning Content - with offsets

-

These dropdowns test various positioning and position offsets

+

These dropdowns test various positioning and alignments WITH OFFSETS. + Valid positions are left/right/top/bottom. Valid alignments are + left/right/top/bottom/center. Left align means left sides should line up. + Right align means right sides should line up. Center align means centers should line up. + Positive Offsets should always be applied in a direction to create + space between the anchor and the dropdown. +

-

This dropdown should be offset by 10 down and 30 to the right

- - -

This dropdown has position left and alignment center, and should go off the screen left because it has allow-overlap true

- - -

This dropdown has position left and should go left if there is room and otherwise below

- - -

This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button

- - @@ -92,14 +92,14 @@

Left and Right Positioned

Right Center

Left Center

From 2a947a1462e86eb7c7053f48bee3e5c096388124 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 31 May 2017 23:53:18 +0530 Subject: [PATCH 18/25] Dropdown: Add explicit positioning docs! --- docs/pages/dropdown.md | 182 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md index cd18d1fac2..8dd405f845 100644 --- a/docs/pages/dropdown.md +++ b/docs/pages/dropdown.md @@ -92,3 +92,185 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o + +--- + +## Explicit Positioning + +Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to do the same. + +These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. + +#### Top and Bottom positioned + +```html + + + + + + + + + + + + + + + + + + + + + + + +``` + +
+
+

Bottom Left

+ + +
+ +
+

Bottom Center

+ + +
+ +
+

Bottom Right

+ + +
+ +
+

Top Left

+ + +
+ +
+

Top Center

+ + +
+ +
+

Top Right

+ + +
+
+ +
+ +#### Left and Right Positioned + +```html + + + + + + + + + + + + + + + + + + + + + + + +``` + +
+
+

Right Top

+ + +
+
+

Left Top

+ + +
+ +
+

Right Center

+ + +
+
+

Left Center

+ + +
+ +
+

Right Bottom

+ + +
+
+

Left Bottom

+ + +
+
+ \ No newline at end of file From 8c947a7b6a339317df14e4ccc8c0de2ce5feb367 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 31 May 2017 23:58:48 +0530 Subject: [PATCH 19/25] Docs update! --- docs/pages/dropdown.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md index 8dd405f845..54f8728c8f 100644 --- a/docs/pages/dropdown.md +++ b/docs/pages/dropdown.md @@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o ## Explicit Positioning -Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to do the same. +Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions. These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. @@ -273,4 +273,3 @@ These dropdowns test various positioning and alignments. Valid positions are lef - \ No newline at end of file From 8d566a2279e9247c510982b944ed9684ea4e0cdb Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 31 May 2017 23:59:17 +0530 Subject: [PATCH 20/25] More docs update --- docs/pages/dropdown.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md index 54f8728c8f..8dab5ce9c6 100644 --- a/docs/pages/dropdown.md +++ b/docs/pages/dropdown.md @@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o ## Explicit Positioning -Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions. +Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box. These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. From b05a847ecee2006ca03b0f01ae2d6c1f7510a7f2 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 1 Jun 2017 00:00:53 +0530 Subject: [PATCH 21/25] More grammar corrections! --- docs/pages/dropdown.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md index 8dab5ce9c6..7f57f68d99 100644 --- a/docs/pages/dropdown.md +++ b/docs/pages/dropdown.md @@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o ## Explicit Positioning -Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box. +Wouldn't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box. These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. From b9c5d12e82c8f25c4d51d503c88f9f2550fdbc69 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 1 Jun 2017 00:12:19 +0530 Subject: [PATCH 22/25] Highlight new feature in 6.4! --- docs/pages/dropdown.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md index 7f57f68d99..d026b17f09 100644 --- a/docs/pages/dropdown.md +++ b/docs/pages/dropdown.md @@ -97,6 +97,10 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o ## Explicit Positioning +
+

New in v6.4: Heads up! This explicit positioning model is a new feature in v6.4.

+
+ Wouldn't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box. These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. From be677c5270e599b4592bb889ae8f39b9902d78fc Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 1 Jun 2017 01:11:29 +0530 Subject: [PATCH 23/25] Add tooltip docs for explicit positioning! --- docs/pages/tooltip.md | 143 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/docs/pages/tooltip.md b/docs/pages/tooltip.md index 05185fffb7..2bd8298cb9 100644 --- a/docs/pages/tooltip.md +++ b/docs/pages/tooltip.md @@ -95,3 +95,146 @@ home by force; but I was too well assured of the old negro's disposition, to hop index of his fortune." Upon the whole, I was sadly vexed and puzzled, but, at length, I concluded to make a virtue of necessity -to dig with a good will, and thus the sooner to convince the visionary, by ocular demonstration, of the fallacy of the opinions he entertained.

``` + +--- + +## Explicit Positioning + +
+

New in v6.4: Heads up! This explicit positioning model is a new feature in v6.4.

+
+ +Now with tooltips you can define both positions for the tip. These tooltips have a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box. + +These dropdowns sets various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up. + +#### Top and Bottom positioned + +```html + + + + + + + + + + + +``` + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ +#### Left and Right Positioned + +```html + + + + + + + + + + + +``` + +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
From 46bd936a28a5226410cc1dd0b892ece4edcda059 Mon Sep 17 00:00:00 2001 From: Kevin Ball Date: Wed, 31 May 2017 13:47:29 -0700 Subject: [PATCH 24/25] Fix pip positioning for aligned tooltips --- js/foundation.tooltip.js | 1 + scss/components/_tooltip.scss | 83 +++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/js/foundation.tooltip.js b/js/foundation.tooltip.js index 2da0157723..82b2cfd136 100644 --- a/js/foundation.tooltip.js +++ b/js/foundation.tooltip.js @@ -132,6 +132,7 @@ class Tooltip extends Positionable { this.template.css('visibility', 'hidden').show(); this._setPosition(); this.template.removeClass('top bottom left right').addClass(this.position) + this.template.removeClass('align-top align-bottom align-left align-right align-center').addClass('align-' + this.alignment); /** * Fires to close all other open tooltips on the page diff --git a/scss/components/_tooltip.scss b/scss/components/_tooltip.scss index f363d3f876..ef1de4f644 100644 --- a/scss/components/_tooltip.scss +++ b/scss/components/_tooltip.scss @@ -69,34 +69,79 @@ $tooltip-radius: $global-radius !default; color: $tooltip-color; &::before { - @include css-triangle($tooltip-pip-width, $tooltip-background-color, up); position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); } - &.top::before { - @include css-triangle($tooltip-pip-width, $tooltip-background-color, down); - top: 100%; - bottom: auto; + &.bottom { + &::before { + @include css-triangle($tooltip-pip-width, $tooltip-background-color, up); + bottom: 100%; + } + + &.align-center::before { + left: 50%; + transform: translateX(-50%); + } } - &.left::before { - @include css-triangle($tooltip-pip-width, $tooltip-background-color, right); - top: 50%; - bottom: auto; - left: 100%; - transform: translateY(-50%); + &.top { + &::before { + @include css-triangle($tooltip-pip-width, $tooltip-background-color, down); + top: 100%; + bottom: auto; + } + + &.align-center::before { + left: 50%; + transform: translateX(-50%); + } + } + + &.left { + &::before { + @include css-triangle($tooltip-pip-width, $tooltip-background-color, right); + left: 100%; + } + + &.align-center::before { + bottom: auto; + top: 50%; + transform: translateY(-50%); + } + } + + &.right { + &::before { + @include css-triangle($tooltip-pip-width, $tooltip-background-color, left); + right: 100%; + left: auto; + } + + &.align-center::before { + bottom: auto; + top: 50%; + transform: translateY(-50%); + } } - &.right::before { - @include css-triangle($tooltip-pip-width, $tooltip-background-color, left); - top: 50%; - right: 100%; + &.align-top::before { bottom: auto; + top: 10%; + } + + &.align-bottom::before { + bottom: 10%; + top: auto; + } + + &.align-left::before { + left: 10%; + right: auto; + } + + &.align-right::before { left: auto; - transform: translateY(-50%); + right: 10%; } } From d13a6a094688ae95fdb99ebb6e0d00923eb696d8 Mon Sep 17 00:00:00 2001 From: Kevin Ball Date: Wed, 31 May 2017 15:34:15 -0700 Subject: [PATCH 25/25] Update dropdown automated tests to work with new positioning --- test/javascript/components/dropdown.js | 31 +++++++------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/test/javascript/components/dropdown.js b/test/javascript/components/dropdown.js index ee0da373eb..6a7e029fc5 100644 --- a/test/javascript/components/dropdown.js +++ b/test/javascript/components/dropdown.js @@ -56,40 +56,25 @@ describe('Dropdown', function() { }); }); - describe('getPositionClass()', function() { - it('has no orientation', function() { + describe('inferred positioning', function() { + it('default orientation should be bottom', function() { $dropdownController = $(getDropdownController()).appendTo('body'); $dropdownContainer = $(getDropdownContainer()).appendTo('body'); plugin = new Foundation.Dropdown($dropdownContainer, {}); - plugin.getPositionClass().trim().should.equal(''); + plugin.open() + plugin.position.should.equal('bottom'); }); - it('has vertical position', function() { - $dropdownController = $(getDropdownController()).appendTo('body'); - $dropdownContainer = $(getDropdownContainer('custom-style-before bottom custom-style-after')) - .appendTo('body'); - plugin = new Foundation.Dropdown($dropdownContainer, {}); - - plugin.getPositionClass().trim().should.equal('bottom'); - }); - - it('has horizontal position', function() { + it('gets right alignment from float-right', function() { $dropdownController = $(getDropdownController('custom-style-before float-right custom-style-after')) .appendTo('body'); $dropdownContainer = $(getDropdownContainer()).appendTo('body'); plugin = new Foundation.Dropdown($dropdownContainer, {}); - plugin.getPositionClass().trim().should.equal('right'); - }); - - it('has horizontal position and only one class', function() { - $dropdownController = $(getDropdownController('float-right')) - .appendTo('body'); - $dropdownContainer = $(getDropdownContainer()).appendTo('body'); - plugin = new Foundation.Dropdown($dropdownContainer, {}); - - plugin.getPositionClass().trim().should.equal('right'); + plugin.open() + plugin.position.should.equal('bottom') + plugin.alignment.should.equal('right') }); }); });