@internetarchive/ia-menu-slider
Advanced tools
Comparing version 0.1.0-alpha.1b6f614 to 0.1.0
@@ -27,3 +27,8 @@ { | ||
], | ||
"events": [], | ||
"events": [ | ||
{ | ||
"name": "ItemNavMenuClosed", | ||
"description": "Fires whenever the menu drawer is closed" | ||
} | ||
], | ||
"slots": [], | ||
@@ -45,3 +50,9 @@ "cssProperties": [ | ||
"type": "Color" | ||
}, { | ||
}, | ||
{ | ||
"name": "--subpanelRightBorderColor", | ||
"description": "Accent color for menu", | ||
"type": "Color" | ||
}, | ||
{ | ||
"name": "--animationTiming", | ||
@@ -60,10 +71,2 @@ "description": "Duration of menu animation", | ||
"type": "Display" | ||
}, { | ||
"name": "--closeMenuIconWidth", | ||
"description": "Close menu icon width", | ||
"type": "Length" | ||
}, { | ||
"name": "--closeMenuIconHeight", | ||
"description": "Close menu icon height", | ||
"type": "Length" | ||
} | ||
@@ -70,0 +73,0 @@ ] |
{ | ||
"name": "@internetarchive/ia-menu-slider", | ||
"version": "0.1.0-alpha.1b6f614", | ||
"version": "0.1.0", | ||
"description": "Menu slider used in ia-topnav", | ||
@@ -24,3 +24,3 @@ "author": "ia-menu-slider", | ||
"dependencies": { | ||
"@internetarchive/ia-icons": "1.1.2", | ||
"@internetarchive/icon-collapse-sidebar": "^1.1.0", | ||
"lit-element": "^2.2.1", | ||
@@ -30,3 +30,4 @@ "lit-html": "^1.1.2" | ||
"devDependencies": { | ||
"@internetarchive/ia-sharing-options": "0.0.0-alpha.f838473", | ||
"@internetarchive/ia-icons": "0.3.0", | ||
"@internetarchive/icon-volumes": "^1.1.0", | ||
"@open-wc/demoing-storybook": "^2.0.0", | ||
@@ -33,0 +34,0 @@ "@open-wc/eslint-config": "^2.0.0", |
@@ -36,4 +36,3 @@ [![Build Status](https://travis-ci.com/internetarchive/iaux-donation-form.svg?branch=master)](https://travis-ci.com/internetarchive/iaux-menu-slider) | ||
followable: true, // Whether to follow the URL supplied in the href property. Optional. | ||
href: '#', // If followable is true, URL followed when menu button clicked | ||
renderCloseAction: true // Toggle for rendering the close submenu button. Optional. | ||
href: '#' // If followable is true, URL followed when menu button clicked | ||
} | ||
@@ -56,4 +55,2 @@ ``` | ||
--activeButtonBg: #282828; | ||
--closeMenuIconWidth: 18px; | ||
--closeMenuIconHeight: 18px; | ||
} | ||
@@ -60,0 +57,0 @@ ``` |
import { nothing } from 'lit-html'; | ||
import { LitElement, html } from 'lit-element'; | ||
import '@internetarchive/ia-icons/ia-icon'; | ||
import menuSliderCSS from './styles/menu-slider.js'; | ||
import '@internetarchive/icon-collapse-sidebar/icon-collapse-sidebar.js'; | ||
import './menu-button.js'; | ||
const sliderEvents = { | ||
closeDrawer: 'ItemNavMenuClosed', | ||
}; | ||
export class IAMenuSlider extends LitElement { | ||
@@ -28,2 +31,6 @@ static get styles() { | ||
/** | ||
* Event handler, captures state of selected menu | ||
* @param { CustomEvent } event | ||
*/ | ||
setSelectedMenu({ detail }) { | ||
@@ -34,12 +41,33 @@ const { id } = detail; | ||
/** | ||
* closes menu drawer | ||
*/ | ||
closeMenu() { | ||
this.selectedMenu = ''; | ||
this.open = false; | ||
const { closeDrawer } = sliderEvents; | ||
const drawerClosed = new CustomEvent(closeDrawer, { | ||
detail: this.selectedMenuDetails, | ||
}); | ||
this.dispatchEvent(drawerClosed); | ||
} | ||
handleCloseClick(e) { | ||
e.preventDefault(); | ||
this.closeMenu(); | ||
get selectedMenuDetails() { | ||
return this.menus.find(menu => menu.id === this.selectedMenu); | ||
} | ||
get selectedMenuComponent() { | ||
const menuItem = this.selectedMenuDetails; | ||
return menuItem && menuItem.component ? menuItem.component : html``; | ||
} | ||
/* render */ | ||
get sliderDetailsClass() { | ||
return this.open ? 'open' : 'closed'; | ||
} | ||
get selectedMenuClass() { | ||
return this.selectedMenu ? 'open' : 'closed'; | ||
} | ||
get menuItems() { | ||
@@ -63,29 +91,23 @@ return this.menus.map(menu => ( | ||
get selectedMenuComponent() { | ||
const menuItem = this.menus.find(menu => menu.id === this.selectedMenu); | ||
return menuItem && menuItem.component ? menuItem.component : html``; | ||
} | ||
get renderMenuHeader() { | ||
const { title = '', menuDetails = '', actionButton } = this.selectedMenuDetails || {}; | ||
const actionSection = actionButton | ||
? html`<div class="custom-action">${actionButton}</div>` | ||
: nothing; | ||
get sliderDetailsClass() { | ||
return this.open ? 'open' : 'closed'; | ||
return html` | ||
<header> | ||
<div class="details"> | ||
<h3>${title}</h3> | ||
<span class="extra-details">${menuDetails}</span> | ||
</div> | ||
${actionSection} | ||
<button class="close" aria-label="Close this menu" @click=${this.closeMenu}> | ||
<ia-icon-collapse-sidebar></ia-icon-collapse-sidebar> | ||
</button> | ||
</header> | ||
`; | ||
} | ||
get selectedMenuClass() { | ||
return this.selectedMenu ? 'open' : 'closed'; | ||
} | ||
get closeAction() { | ||
const menuItem = this.menus.find(menu => menu.id === this.selectedMenu); | ||
if (!menuItem) { return nothing; } | ||
return menuItem.renderCloseAction !== false | ||
? html`<a | ||
class="close" | ||
href="#" | ||
@click=${this.handleCloseClick} | ||
> | ||
<ia-icon icon="collapseSidebar"></ia-icon> | ||
</a>` | ||
: nothing; | ||
} | ||
/** @inheritdoc */ | ||
render() { | ||
@@ -97,7 +119,5 @@ return html` | ||
</ul> | ||
<div class="content ${this.selectedMenuClass}" @menuTypeSelected=${this.setSelectedMenu} @closeMenu=${this.closeMenu}> | ||
${this.closeAction} | ||
<div class="submenu"> | ||
${this.selectedMenuComponent} | ||
</div> | ||
<div class="content ${this.selectedMenuClass}" @menuTypeSelected=${this.setSelectedMenu}> | ||
${this.renderMenuHeader} | ||
${this.selectedMenuComponent} | ||
</div> | ||
@@ -104,0 +124,0 @@ </div> |
@@ -1,2 +0,1 @@ | ||
import { ifDefined } from 'lit-html/directives/if-defined'; | ||
import { html, LitElement } from 'lit-element'; | ||
@@ -66,3 +65,3 @@ import menuButtonCSS from './styles/menu-button.js'; | ||
<a | ||
href="${ifDefined(this.href)}" | ||
href="${this.href}" | ||
class="menu-item ${this.buttonClass}" | ||
@@ -69,0 +68,0 @@ @click=${this.followable ? undefined : this.onClick} |
@@ -7,3 +7,2 @@ import { css } from 'lit-element'; | ||
text-decoration: none; | ||
cursor: pointer; | ||
} | ||
@@ -10,0 +9,0 @@ |
@@ -6,66 +6,86 @@ import { css } from 'lit-element'; | ||
export default css` | ||
.menu { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
bottom: 0; | ||
width: var(--menuWidth); | ||
padding: .5rem; | ||
box-sizing: border-box; | ||
font-size: 1.4rem; | ||
color: var(--primaryTextColor); | ||
background: var(--menuSliderBg); | ||
transform: translateX(calc(var(--menuWidth) * -1)); | ||
transition: transform var(--animationTiming) ease-in-out; | ||
} | ||
.menu:before { | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
left: 0; | ||
z-index: 2; | ||
width: ${menuButtonWidth}; | ||
content: ""; | ||
background: var(--menuSliderBg); | ||
} | ||
.menu { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
bottom: 0; | ||
width: var(--menuWidth); | ||
padding: .5rem; | ||
box-sizing: border-box; | ||
font-size: 1.4rem; | ||
color: var(--primaryTextColor); | ||
background: var(--menuSliderBg); | ||
transform: translateX(calc(var(--menuWidth) * -1)); | ||
transition: transform var(--animationTiming) ease-in-out; | ||
} | ||
.menu:before { | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
left: 0; | ||
z-index: 2; | ||
width: ${menuButtonWidth}; | ||
content: ""; | ||
background: var(--menuSliderBg); | ||
} | ||
.content { | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: ${menuButtonWidth}; | ||
z-index: 1; | ||
transform: translateX(calc(var(--menuWidth) * -1)); | ||
transition: transform var(--animationTiming) ease-in-out; | ||
background: var(--activeButtonBg); | ||
} | ||
header { | ||
margin: .2rem 0 .5rem 0; | ||
} | ||
.open { | ||
transform: translateX(0); | ||
} | ||
header * { | ||
margin: 0; | ||
display: inline-block; | ||
} | ||
header button { | ||
outline: none; | ||
cursor: pointer; | ||
} | ||
ul { | ||
padding: 0; | ||
margin: 0; | ||
list-style: none; | ||
background: var(--menuSliderBg); | ||
} | ||
header .details { | ||
width: 80%; | ||
} | ||
.close { | ||
position: absolute; | ||
top: 15px; | ||
right: 10px; | ||
z-index: 2; | ||
} | ||
header .custom-action > *, | ||
header .close { | ||
padding: 0; | ||
background-color: transparent; | ||
border: 0; | ||
width: var(--iconWidth); | ||
height: var(--iconHeight); | ||
} | ||
.close ia-icon { | ||
--iconWidth: var(--closeMenuIconWidth); | ||
--iconHeight: var(--closeMenuIconHeight); | ||
} | ||
header .custom-action, | ||
header .close { | ||
position: absolute; | ||
} | ||
header .close { | ||
right: .5rem; | ||
} | ||
.submenu { | ||
position: relative; | ||
z-index: 1; | ||
} | ||
.content { | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: ${menuButtonWidth}; | ||
z-index: 1; | ||
transform: translateX(calc(var(--menuWidth) * -1)); | ||
transition: transform var(--animationTiming) ease-in-out; | ||
background: var(--activeButtonBg); | ||
border-right: .2rem solid; | ||
border-color: var(--subpanelRightBorderColor); | ||
padding: .5rem 0 .5rem .5rem; | ||
} | ||
.open { | ||
transform: translateX(0); | ||
} | ||
ul { | ||
padding: 0; | ||
margin: 0; | ||
list-style: none; | ||
background: var(--menuSliderBg); | ||
} | ||
`; |
@@ -24,3 +24,2 @@ import { html, fixture, expect } from '@open-wc/testing'; | ||
`, | ||
renderCloseAction: false, | ||
}, { | ||
@@ -144,25 +143,77 @@ icon: html`<i></i>`, | ||
it('does not render close submenu action when optional prop is false', async () => { | ||
const el = await fixture(container(menus)); | ||
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])); | ||
el.selectedMenu = menus[1].id; | ||
// open menu | ||
el | ||
.shadowRoot | ||
.querySelectorAll('menu-button')[0] | ||
.shadowRoot | ||
.querySelector('a') | ||
.click(); | ||
await el.updateComplete; | ||
const closeAction = el.shadowRoot.querySelector('.close'); | ||
const menuHeader = el.shadowRoot.querySelector('.content header'); | ||
expect(closeAction).to.be.null; | ||
}); | ||
// 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); | ||
it('closes menus and deselects submenu when close action clicked', async () => { | ||
const el = await fixture(container(menus)); | ||
const description = menuHeader.querySelector('.extra-details'); | ||
expect(description).to.not.be.undefined; | ||
expect(description.innerText).to.equal(menu.menuDetails); | ||
}); | ||
el.selectedMenu = menus[0].id; | ||
el.open = true; | ||
await el.updateComplete; | ||
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; | ||
}); | ||
el.shadowRoot.querySelector('.close').click(); | ||
await el.updateComplete; | ||
// 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; | ||
}); | ||
expect(el.open).to.equal(false); | ||
expect(el.selectedMenu).to.equal(''); | ||
// 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; | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
62896
18
683
11
98
+ Added@internetarchive/icon-collapse-sidebar@1.3.4(transitive)
+ Added@lit-labs/ssr-dom-shim@1.3.0(transitive)
+ Added@lit/reactive-element@1.6.3(transitive)
+ Added@types/trusted-types@2.0.7(transitive)
+ Addedlit@2.8.0(transitive)
+ Addedlit-element@3.3.3(transitive)
+ Addedlit-html@2.8.0(transitive)
- Removed@internetarchive/ia-icons@1.1.2