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

feat(popover): improve usability - FRONT-3841 #2739

Merged
merged 10 commits into from
Feb 1, 2023
4 changes: 2 additions & 2 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"path": "dist/packages/ec/scripts/ecl-ec.js",
"webpack": false,
"limit": "40 KB"
"limit": "41 KB"
},
{
"path": "dist/packages/ec/styles/ecl-ec.css",
Expand All @@ -17,7 +17,7 @@
{
"path": "dist/packages/eu/scripts/ecl-eu.js",
"webpack": false,
"limit": "40 KB"
"limit": "41 KB"
},
{
"path": "dist/packages/eu/styles/ecl-eu.css",
Expand Down
5 changes: 3 additions & 2 deletions src/implementations/vanilla/components/popover/_popover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ $_item-height: map.get(theme.$line-height-prolonged, 'm') +
transform: none;

&::before {
left: 25%;
left: var(--ecl-popover-position);
}
}

Expand All @@ -116,6 +116,7 @@ $_item-height: map.get(theme.$line-height-prolonged, 'm') +
transform: none;

&::before {
left: 75%;
left: auto;
right: var(--ecl-popover-position);
}
}
97 changes: 92 additions & 5 deletions src/implementations/vanilla/components/popover/popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { queryOne } from '@ecl/dom-utils';
* @param {Object} options
* @param {String} options.toggleSelector Selector for toggling element
* @param {Boolean} options.attachClickListener Whether or not to bind click events on toggle
* @param {Boolean} options.attachKeyListener Whether or not to bind keyboard events
*/
export class Popover {
/**
Expand All @@ -27,6 +28,7 @@ export class Popover {
{
toggleSelector = '[data-ecl-popover-toggle]',
attachClickListener = true,
attachKeyListener = true,
} = {}
) {
// Check element
Expand All @@ -41,13 +43,19 @@ export class Popover {
// Options
this.toggleSelector = toggleSelector;
this.attachClickListener = attachClickListener;
this.attachKeyListener = attachKeyListener;

// Private variables
this.toggle = null;
this.target = null;

// Bind `this` for use in callbacks
this.openPopover = this.openPopover.bind(this);
this.closePopover = this.closePopover.bind(this);
this.positionPopover = this.positionPopover.bind(this);
this.handleClickOnToggle = this.handleClickOnToggle.bind(this);
this.handleKeyboardGlobal = this.handleKeyboardGlobal.bind(this);
this.handleClickGlobal = this.handleClickGlobal.bind(this);
}

/**
Expand All @@ -56,6 +64,14 @@ export class Popover {
init() {
this.toggle = queryOne(this.toggleSelector, this.element);

// Bind global events
if (this.attachKeyListener) {
document.addEventListener('keyup', this.handleKeyboardGlobal);
}
if (this.attachClickListener) {
document.addEventListener('click', this.handleClickGlobal);
}

// Get target element
this.target = document.querySelector(
`#${this.toggle.getAttribute('aria-controls')}`
Expand Down Expand Up @@ -84,6 +100,14 @@ export class Popover {
if (this.attachClickListener && this.toggle) {
this.toggle.removeEventListener('click', this.handleClickOnToggle);
}

if (this.attachKeyListener) {
document.removeEventListener('keyup', this.handleKeyboardGlobal);
}
if (this.attachClickListener) {
document.removeEventListener('click', this.handleClickGlobal);
}

if (this.element) {
this.element.removeAttribute('data-ecl-auto-initialized');
}
Expand All @@ -101,13 +125,35 @@ export class Popover {
const isExpanded = this.toggle.getAttribute('aria-expanded') === 'true';

// Toggle the popover
this.toggle.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
if (isExpanded) {
this.target.hidden = true;
} else {
this.target.hidden = false;
this.closePopover();
return;
}

this.openPopover();
this.positionPopover();
}

/**
* Open the popover.
*/
openPopover() {
this.toggle.setAttribute('aria-expanded', 'true');
this.target.hidden = false;
}

/**
* Close the popover.
*/
closePopover() {
this.toggle.setAttribute('aria-expanded', 'false');
this.target.hidden = true;
}

/**
* Manage popover position.
*/
positionPopover() {
// Check available space
this.element.classList.remove('ecl-popover--top');
this.element.classList.remove('ecl-popover--push-left');
Expand All @@ -125,13 +171,54 @@ export class Popover {

if (popoverRect.left < 0) {
this.element.classList.add('ecl-popover--push-left');

// Adapt arrow position
this.target.style.setProperty(
'--ecl-popover-position',
`${toggleRect.width / 2}px`
);
}

if (popoverRect.right > screenWidth) {
this.element.classList.add('ecl-popover--push-right');

// Adapt arrow position
this.target.style.setProperty(
'--ecl-popover-position',
`calc(${toggleRect.width / 2}px - 0.5rem)`
);
}
}

/**
* Handles global keyboard events, triggered outside of the popover.
*
* @param {Event} e
*/
handleKeyboardGlobal(e) {
if (!this.target) return;

// Detect press on Escape
if (e.key === 'Escape' || e.key === 'Esc') {
this.closePopover();
}
}

return this;
/**
* Handles global click events, triggered outside of the popover.
*
* @param {Event} e
*/
handleClickGlobal(e) {
if (!this.target) return;

// Check if the popover is open
if (this.toggle.getAttribute('aria-expanded') === 'true') {
// Check if the click occured on the popover
if (!this.target.contains(e.target) && !this.toggle.contains(e.target)) {
this.closePopover();
}
}
}
}

Expand Down