@internetarchive/ia-menu-slider
Advanced tools
Comparing version 0.1.10 to 0.2.0-alpha1
{ | ||
"name": "@internetarchive/ia-menu-slider", | ||
"version": "0.1.10", | ||
"version": "0.2.0-alpha1", | ||
"description": "Menu slider used in ia-topnav", | ||
@@ -5,0 +5,0 @@ "author": "ia-menu-slider", |
@@ -8,3 +8,3 @@ import { nothing } from 'lit-html'; | ||
const sliderEvents = { | ||
closeDrawer: 'menuSliderClosed', | ||
closeDrawer: 'ItemNavMenuClosed', | ||
}; | ||
@@ -21,4 +21,4 @@ export class IAMenuSlider extends LitElement { | ||
selectedMenu: { type: String }, | ||
selectedMenuAction: { type: Object }, | ||
animateMenuOpen: { type: Boolean }, | ||
manuallyHandleClose: { type: Boolean }, | ||
}; | ||
@@ -33,6 +33,14 @@ } | ||
this.selectedMenu = ''; | ||
this.selectedMenuAction = nothing; | ||
this.animateMenuOpen = false; | ||
this.manuallyHandleClose = false; | ||
} | ||
updated() { | ||
const { actionButton } = this.selectedMenuDetails || {}; | ||
const actionButtonHasChanged = actionButton !== this.selectedMenuAction; | ||
if (actionButtonHasChanged) { | ||
this.selectedMenuAction = actionButton || nothing; | ||
} | ||
} | ||
/** | ||
@@ -45,2 +53,4 @@ * Event handler, captures state of selected menu | ||
this.selectedMenu = this.selectedMenu === id ? '' : id; | ||
const { actionButton } = this.selectedMenuDetails || {}; | ||
this.selectedMenuAction = actionButton || nothing; | ||
} | ||
@@ -52,5 +62,3 @@ | ||
closeMenu() { | ||
if (!this.manuallyHandleClose) { | ||
this.open = false; | ||
} | ||
this.open = false; | ||
const { closeDrawer } = sliderEvents; | ||
@@ -64,3 +72,4 @@ const drawerClosed = new CustomEvent(closeDrawer, { | ||
get selectedMenuDetails() { | ||
return this.menus.find(menu => menu.id === this.selectedMenu); | ||
const selectedMenu = this.menus.find(menu => menu.id === this.selectedMenu); | ||
return selectedMenu; | ||
} | ||
@@ -77,3 +86,3 @@ | ||
const animate = this.animateMenuOpen ? 'animate' : ''; | ||
const state = this.open ? 'open' : ''; | ||
const state = this.open ? 'open' : 'closed'; | ||
return `${animate} ${state}`; | ||
@@ -83,3 +92,3 @@ } | ||
get selectedMenuClass() { | ||
return this.selectedMenu ? 'open' : ''; | ||
return this.selectedMenu ? 'open' : 'closed'; | ||
} | ||
@@ -107,7 +116,4 @@ | ||
get renderMenuHeader() { | ||
const { label = '', menuDetails = '', actionButton } = this.selectedMenuDetails || {}; | ||
const actionSection = actionButton | ||
? html`<div class="custom-action">${actionButton}</div>` | ||
: nothing; | ||
const headerClass = actionButton ? 'with-secondary-action' : ''; | ||
const { label = '', menuDetails = '' } = this.selectedMenuDetails || {}; | ||
const headerClass = this.selectedMenuAction ? 'with-secondary-action' : ''; | ||
@@ -120,3 +126,3 @@ return html` | ||
</div> | ||
${actionSection} | ||
${this.selectedMenuAction} | ||
${this.closeButton} | ||
@@ -138,3 +144,2 @@ </header> | ||
return html` | ||
<div class="main"> | ||
<div class="menu ${this.sliderDetailsClass}"> | ||
@@ -147,12 +152,9 @@ ${this.closeButton} | ||
${this.renderMenuHeader} | ||
<section> | ||
<div class="selected-menu"> | ||
${this.selectedMenuComponent} | ||
</div> | ||
<section class="selected-menu"> | ||
${this.selectedMenuComponent} | ||
</section> | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
} | ||
} |
import { css } from 'lit-element'; | ||
const menuButtonWidth = css`42px`; | ||
const sliderWidth = css`var(--menuWidth, 320px)`; | ||
const transitionTiming = css`var(--animationTiming, 200ms)`; | ||
export default css` | ||
.main { | ||
overflow: hidden; | ||
position: relative; | ||
width: 100%; | ||
height: 100%; | ||
} | ||
.animate { | ||
transition: transform ${transitionTiming} ease-out; | ||
} | ||
.menu { | ||
@@ -25,4 +12,4 @@ position: absolute; | ||
bottom: 0; | ||
width: ${sliderWidth}; | ||
padding: .5rem .5rem 0 0; | ||
width: 100%; | ||
padding: .5rem .5rem .5rem 0; | ||
box-sizing: border-box; | ||
@@ -32,5 +19,5 @@ font-size: 1.4rem; | ||
background: var(--menuSliderBg); | ||
transform: translateX(calc(${sliderWidth} * -1)); | ||
transform: translateX(calc(100% * -1)); | ||
transition: transform var(--animationTiming) ease-out; | ||
} | ||
.menu:before { | ||
@@ -47,6 +34,2 @@ position: absolute; | ||
.menu > button.close { | ||
right: 0.7rem; | ||
} | ||
button { | ||
@@ -58,3 +41,3 @@ outline: none; | ||
header { | ||
margin: 0 0 .5rem 0; | ||
margin: .2rem 0 .5rem 0; | ||
} | ||
@@ -108,8 +91,8 @@ | ||
z-index: 1; | ||
transform: translateX(calc(${sliderWidth} * -1)); | ||
transition: transform ${transitionTiming} ease-out; | ||
transform: translateX(calc(100% * -1)); | ||
transition: transform var(--animationTiming) ease-out; | ||
background: var(--activeButtonBg); | ||
border-right: .2rem solid; | ||
border-color: var(--subpanelRightBorderColor); | ||
padding: .5rem 0 0 .5rem; | ||
padding: .5rem 0 .5rem .5rem; | ||
} | ||
@@ -131,15 +114,8 @@ | ||
.content section { | ||
height: 100%; | ||
position: relative; | ||
width: 100%; | ||
} | ||
.content .selected-menu { | ||
overflow: auto; | ||
position: absolute; | ||
top: 0; | ||
width: 100%; | ||
padding-bottom: 2rem; | ||
height: inherit; | ||
width: 98%; | ||
bottom: 0; | ||
top: 4rem; | ||
} | ||
@@ -149,4 +125,3 @@ | ||
display: block; | ||
padding-bottom: 3rem; | ||
} | ||
`; |
@@ -149,127 +149,93 @@ import { html, fixture, expect } from '@open-wc/testing'; | ||
}); | ||
}); | ||
describe('Menu list', async () => { | ||
const el = await fixture(container(menus)); | ||
const menuList = el | ||
.shadowRoot | ||
.querySelectorAll('menu-button'); | ||
describe('Menu list', async () => { | ||
const el = await fixture(container(menus)); | ||
const menuList = el | ||
.shadowRoot | ||
.querySelectorAll('menu-button'); | ||
it('shows menu details when available', () => { | ||
const firstMenuItem = menuList[0].shadowRoot.querySelector('.menu-details'); | ||
expect(firstMenuItem.innerText).to.equal(menus[0].menuDetails); | ||
it('shows menu details when available', () => { | ||
const firstMenuItem = menuList[0].shadowRoot.querySelector('.menu-details'); | ||
expect(firstMenuItem.innerText).to.equal(menus[0].menuDetails); | ||
const secondMenuItem = menuList[1].shadowRoot.querySelector('.menu-details'); | ||
expect(secondMenuItem.innerText).to.be.empty; | ||
const secondMenuItem = menuList[1].shadowRoot.querySelector('.menu-details'); | ||
expect(secondMenuItem.innerText).to.be.empty; | ||
}); | ||
}); | ||
}); | ||
describe('Header section', async () => { | ||
let extraButtonClicked = false; | ||
const extraActionClickHandler = () => { | ||
extraButtonClicked = true; | ||
}; | ||
const menu = { | ||
icon: html`<ia-icon icon="audio"></ia-icon>`, | ||
title: 'Audio Menu', | ||
menuDetails: '(100,000,000 tracks)', | ||
actionButton: html` | ||
<button | ||
style='color: white;' | ||
@click=${extraActionClickHandler} | ||
>Foo</button> | ||
`, | ||
label: 'Audio', | ||
id: 'audio', | ||
component: html` | ||
<h1>Menu 2</h1> | ||
`, | ||
}; | ||
const el = await fixture(container([menu])); | ||
describe('Header section', async () => { | ||
let extraButtonClicked = false; | ||
const extraActionClickHandler = () => { | ||
extraButtonClicked = true; | ||
}; | ||
const menu = { | ||
icon: html`<ia-icon icon="audio"></ia-icon>`, | ||
title: 'Audio Menu', | ||
menuDetails: '(100,000,000 tracks)', | ||
actionButton: html` | ||
<button | ||
style='color: white;' | ||
@click=${extraActionClickHandler} | ||
>Foo</button> | ||
`, | ||
label: 'Audio', | ||
id: 'audio', | ||
component: html` | ||
<h1>Menu 2</h1> | ||
`, | ||
}; | ||
const el = await fixture(container([menu])); | ||
// open menu | ||
el | ||
.shadowRoot | ||
.querySelectorAll('menu-button')[0] | ||
.shadowRoot | ||
.querySelector('button') | ||
.click(); | ||
// open menu | ||
el | ||
.shadowRoot | ||
.querySelectorAll('menu-button')[0] | ||
.shadowRoot | ||
.querySelector('a') | ||
.click(); | ||
await el.updateComplete; | ||
const menuHeader = el.shadowRoot.querySelector('.content header'); | ||
// display | ||
it('creates a header section', () => { | ||
expect(menuHeader).to.not.be.undefined; | ||
}); | ||
it('adds header title and description', () => { | ||
const title = menuHeader.querySelector('h3'); | ||
expect(title).to.not.be.undefined; | ||
expect(title.innerText).to.equal(menu.label); | ||
const description = menuHeader.querySelector('.extra-details'); | ||
expect(description).to.not.be.undefined; | ||
expect(description.innerText).to.equal(menu.menuDetails); | ||
}); | ||
it('has a close button by default', async () => { | ||
const closeButton = menuHeader.querySelector('.close'); | ||
expect(closeButton).to.not.be.undefined; | ||
}); | ||
it('has an option for a menu specific action button', () => { | ||
const actionButton = menuHeader.querySelector('.custom-action'); | ||
expect(actionButton).to.not.be.undefined; | ||
}); | ||
// custom action | ||
it('can click on the custom action', async () => { | ||
const actionButton = menuHeader.querySelector('.custom-action > *'); | ||
expect(extraButtonClicked).to.be.false; // has not been clicked | ||
const actionClick = new MouseEvent('click'); | ||
actionButton.dispatchEvent(actionClick); | ||
await el.updateComplete; | ||
expect(extraButtonClicked).to.be.true; | ||
}); | ||
const menuHeader = el.shadowRoot.querySelector('.content header'); | ||
// close action | ||
it('emits a custom event when the drawer closes', async () => { | ||
const closeButton = menuHeader.querySelector('button.close'); | ||
closeButton.dispatchEvent(new MouseEvent('click')); | ||
await el.updateComplete; | ||
expect(el.open).to.be.false; | ||
}); | ||
// display | ||
it('creates a header section', () => { | ||
expect(menuHeader).to.not.be.undefined; | ||
}); | ||
it('adds header title and description', () => { | ||
const title = menuHeader.querySelector('h3'); | ||
expect(title).to.not.be.undefined; | ||
expect(title.innerText).to.equal(menu.title); | ||
describe('Animation toggles for primary open/close state', async () => { | ||
const thisMenu = await fixture(container(menus)); | ||
const description = menuHeader.querySelector('.extra-details'); | ||
expect(description).to.not.be.undefined; | ||
expect(description.innerText).to.equal(menu.menuDetails); | ||
}); | ||
it('does not automatically animate menu', () => { | ||
expect(thisMenu.animateMenuOpen).to.be.false; | ||
it('has a close button by default', async () => { | ||
const closeButton = menuHeader.querySelector('.close'); | ||
expect(closeButton).to.not.be.undefined; | ||
}); | ||
it('has an option for a menu specific action button', () => { | ||
const actionButton = menuHeader.querySelector('.custom-action'); | ||
expect(actionButton).to.not.be.undefined; | ||
}); | ||
it('uses `animate` class to power transition when turned on', async () => { | ||
thisMenu.animateMenuOpen = true; | ||
// custom action | ||
it('can click on the custom action', async () => { | ||
const actionButton = menuHeader.querySelector('.custom-action > *'); | ||
expect(extraButtonClicked).to.be.false; // has not been clicked | ||
const actionClick = new MouseEvent('click'); | ||
actionButton.dispatchEvent(actionClick); | ||
await el.updateComplete; | ||
expect(thisMenu.shadowRoot.querySelector('.menu.animate').classList.contains('animate')).to.be.true; | ||
expect(extraButtonClicked).to.be.true; | ||
}); | ||
}); | ||
describe('Menu close behavior', async () => { | ||
const thisMenu = await fixture(container(menus)); | ||
it('has property: `manuallyHandleClose`', () => { | ||
expect(thisMenu.manuallyHandleClose).to.exist; | ||
// close action | ||
it('emits a custom event when the drawer closes', async () => { | ||
const closeButton = menuHeader.querySelector('button.close'); | ||
closeButton.dispatchEvent(new MouseEvent('click')); | ||
await el.updateComplete; | ||
expect(el.open).to.be.false; | ||
}); | ||
thisMenu.manuallyHandleClose = true; | ||
thisMenu.open = true; | ||
const closebutton = thisMenu.shadowRoot.querySelector('button.close'); | ||
closebutton.click(); | ||
await el.updateComplete; | ||
closebutton; | ||
it('does not manually close menu when property is set: `manuallyHandleClose`', () => { | ||
expect(thisMenu.manuallyHandleClose).to.be.true; | ||
expect(thisMenu.open).to.be.true; | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
65787
745