Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

@lion/button

Package Overview
Dependencies
Maintainers
1
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/button - npm Package Compare versions

Comparing version 0.7.15 to 0.8.0

19

CHANGELOG.md
# Change Log
## 0.8.0
### Minor Changes
- 26b60593: Several button improvements
- remove click-area --> move styles to host::before
- reduce css so that extending styles makes sense. Merge .btn with host.
- reduce the template and remove the if else construction inside the template.
- hide focus styles if they're not needed, for example, when an element receives focus via the mouse.
- improve \_\_clickDelegationHandler. Use current slotted native button instead of create new one.
- fix vertical alignment when 2 buttons are inline and one has icon. Example included.
### Patch Changes
- f7ab5391: Set button host to inline-flex as a better default for when button content contains a before or after icon
- Updated dependencies [e92b98a4]
- @lion/core@0.13.1
## 0.7.15

@@ -4,0 +23,0 @@

11

package.json
{
"name": "@lion/button",
"version": "0.7.15",
"version": "0.8.0",
"description": "A button that is easily styleable and accessible in all contexts",

@@ -26,6 +26,7 @@ "license": "MIT",

"scripts": {
"debug": "cd ../../ && yarn debug --group button",
"debug:firefox": "cd ../../ && yarn debug:firefox --group button",
"debug:webkit": "cd ../../ && yarn debug:webkit --group button",
"prepublishOnly": "../../scripts/npm-prepublish.js",
"start": "cd ../../ && yarn dev-server --open packages/button/README.md",
"test": "cd ../../ && yarn test:browser --grep \"packages/button/test/**/*.test.js\"",
"test:watch": "cd ../../ && yarn test:browser:watch --grep \"packages/button/test/**/*.test.js\""
"test": "cd ../../ && yarn test:browser --group button"
},

@@ -36,3 +37,3 @@ "sideEffects": [

"dependencies": {
"@lion/core": "0.13.0"
"@lion/core": "0.13.1"
},

@@ -39,0 +40,0 @@ "keywords": [

@@ -19,3 +19,3 @@ [//]: # 'AUTO INSERT HEADER PREPUBLISH'

```js preview-story
export const main = () => html` <lion-button>Default</lion-button> `;
export const main = () => html`<lion-button>Default</lion-button>`;
```

@@ -64,3 +64,3 @@

```js preview-story
export const iconButton = () => html` <lion-button>${iconSvg(html)} Bug</lion-button> `;
export const iconButton = () => html`<lion-button>${iconSvg(html)}Bug</lion-button>`;
```

@@ -71,4 +71,3 @@

```js preview-story
export const iconOnly = () =>
html` <lion-button aria-label="Bug"> ${iconSvg(html)} </lion-button> `;
export const iconOnly = () => html`<lion-button aria-label="Bug">${iconSvg(html)}</lion-button>`;
```

@@ -79,5 +78,29 @@

```js preview-story
export const disabled = () => html` <lion-button disabled>Disabled</lion-button> `;
export const disabled = () => html`<lion-button disabled>Disabled</lion-button>`;
```
### Multiple buttons inline
```js preview-story
export const mainAndIconButton = () => html`
<lion-button>Default</lion-button>
<lion-button>${iconSvg(html)} Bug</lion-button>
`;
```
### Small button (minimum click area showed)
```js preview-story
export const smallButton = () => html` <style>
.small {
padding: 4px;
line-height: 1em;
}
.small::before {
border: 1px dashed #000;
}
</style>
<lion-button class="small">xs</lion-button>`;
```
### Usage with native form

@@ -97,3 +120,3 @@

ev.preventDefault();
console.log('submit handler');
console.log('submit handler', ev.target);
}}

@@ -105,3 +128,3 @@ >

<input id="lastNameId" name="lastName" />
<lion-button @click=${() => console.log('click handler')}>Submit</lion-button>
<lion-button @click=${ev => console.log('click handler', ev.target)}>Submit</lion-button>
</form>

@@ -108,0 +131,0 @@ `;

@@ -11,12 +11,2 @@ import {

// TODO: several improvements:
// [1] remove click-area
// [2] remove the native _button slot. We can detect and submit parent form without the slot.
// [3] reduce css so that extending styles makes sense. Merge .btn with host
// [4] reduce the template and remove the if else construction inside the template (an extra
// div by default to support IE is fine) => <div id="${this._buttonId}"><slot></slot></div>
// should be all needed
// [5] do we need the before and after templates? Could be added by subclasser
// [6] extract all functionality (except for form submission) into LionButtonMixin
const isKeyboardClickEvent = (/** @type {KeyboardEvent} */ e) => e.key === ' ' || e.key === 'Enter';

@@ -45,11 +35,6 @@ const isSpaceKeyboardClickEvent = (/** @type {KeyboardEvent} */ e) => e.key === ' ';

return html`
<div class="btn">
<div class="click-area"></div>
${this._beforeTemplate()}
${browserDetection.isIE11
? html`<div id="${this._buttonId}"><slot></slot></div>`
: html`<slot></slot>`}
${this._afterTemplate()}
<slot name="_button"></slot>
</div>
${this._beforeTemplate()}
<div class="button-content" id="${this._buttonId}"><slot></slot></div>
${this._afterTemplate()}
<slot name="_button"></slot>
`;

@@ -72,20 +57,35 @@ }

:host {
display: inline-block;
min-height: 40px; /* src = https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/ */
outline: 0;
background-color: transparent;
position: relative;
display: inline-flex;
box-sizing: border-box;
vertical-align: middle;
line-height: 24px;
background: #eee; /* minimal styling to make it recognizable as btn */
padding: 8px; /* padding to fix with min-height */
outline: none; /* focus style handled below */
cursor: default; /* /* we should always see the default arrow, never a caret */
}
.btn {
min-height: 24px;
:host::before {
content: '';
/* center vertically and horizontally */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* touch area (comes into play when button height goes below this one) */
/* src = https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/ */
min-height: 40px;
min-width: 40px;
}
.button-content {
display: flex;
align-items: center;
position: relative;
background: #eee; /* minimal styling to make it recognizable as btn */
padding: 8px; /* vertical padding to fix with host min-height */
outline: none; /* focus style handled below, else it follows boundaries of click-area */
justify-content: center;
}
:host .btn ::slotted(button) {
:host ::slotted(button) {
position: absolute;

@@ -104,13 +104,5 @@ top: 0;

.click-area {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: 0;
padding: 0;
}
:host(:focus:not([disabled])) .btn {
/* Show focus styles on keyboard focus. */
:host(:focus:not([disabled])),
:host(:focus-visible) {
/* if you extend, please overwrite */

@@ -120,3 +112,9 @@ outline: 2px solid #bde4ff;

:host(:hover) .btn {
/* Hide focus styles if they're not needed, for example,
when an element receives focus via the mouse. */
:host(:focus:not(:focus-visible)) {
outline: 0;
}
:host(:hover) {
/* if you extend, please overwrite */

@@ -126,4 +124,4 @@ background: #f4f6f7;

:host(:active) .btn, /* keep native :active to render quickly where possible */
:host([active]) .btn /* use custom [active] to fix IE11 */ {
:host(:active), /* keep native :active to render quickly where possible */
:host([active]) /* use custom [active] to fix IE11 */ {
/* if you extend, please overwrite */

@@ -133,6 +131,2 @@ background: gray;

:host([disabled]) {
pointer-events: none;
}
:host([hidden]) {

@@ -142,3 +136,4 @@ display: none;

:host([disabled]) .btn {
:host([disabled]) {
pointer-events: none;
/* if you extend, please overwrite */

@@ -224,10 +219,5 @@ background: lightgray;

__clickDelegationHandler(e) {
if ((this.type === 'submit' || this.type === 'reset') && e.target === this) {
if (this._form) {
const nativeButton = document.createElement('button');
nativeButton.type = this.type;
this._form.appendChild(nativeButton);
nativeButton.click();
this._form.removeChild(nativeButton);
}
if ((this.type === 'submit' || this.type === 'reset') && e.target === this && this._form) {
e.stopImmediatePropagation();
this._nativeButtonNode.click();
}

@@ -252,2 +242,3 @@ }

this.removeEventListener('keyup', this.__keyupHandler);
this.removeEventListener('click', this.__clickDelegationHandler);
}

@@ -254,0 +245,0 @@

import { browserDetection } from '@lion/core';
import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';
import { aTimeout, expect, fixture, html, oneEvent, unsafeStatic } from '@open-wc/testing';
import sinon from 'sinon';

@@ -209,3 +209,3 @@ import '@lion/core/src/differentKeyEventNamesShimIE.js';

expect(/** @type {ShadowRoot} */ (el.shadowRoot).querySelector(`#${wrapperId}`)).dom.to.equal(
`<div id="${wrapperId}"><slot></slot></div>`,
`<div class="button-content" id="${wrapperId}"><slot></slot></div>`,
);

@@ -401,2 +401,12 @@ browserDetectionStub.restore();

describe('click event', () => {
/**
* @param {HTMLButtonElement | LionButton} el
*/
async function prepareClickEvent(el) {
setTimeout(() => {
el.click();
});
return oneEvent(el, 'click');
}
it('is fired once', async () => {

@@ -417,12 +427,21 @@ const clickSpy = /** @type {EventListener} */ (sinon.spy());

it('is fired one inside a form', async () => {
const formClickSpy = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault()));
const el = /** @type {HTMLFormElement} */ (await fixture(
html`<form @click="${formClickSpy}">
<lion-button>foo</lion-button>
</form>`,
));
// @ts-ignore
el.querySelector('lion-button').click();
// trying to wait for other possible redispatched events
await aTimeout(0);
await aTimeout(0);
expect(formClickSpy).to.have.been.calledOnce;
});
describe('native button behavior', async () => {
/**
* @param {HTMLButtonElement | LionButton} el
*/
async function prepareClickEvent(el) {
setTimeout(() => {
el.click();
});
return oneEvent(el, 'click');
}
/** @type {Event} */

@@ -456,4 +475,6 @@ let nativeButtonEvent;

});
});
it('has host in the target property', async () => {
describe('event target', async () => {
it('is host by default', async () => {
const el = /** @type {LionButton} */ (await fixture('<lion-button>foo</lion-button>'));

@@ -463,4 +484,33 @@ const event = await prepareClickEvent(el);

});
const useCases = [
{ container: 'div', type: 'submit', targetHost: true },
{ container: 'div', type: 'reset', targetHost: true },
{ container: 'div', type: 'button', targetHost: true },
{ container: 'form', type: 'submit', targetHost: false },
{ container: 'form', type: 'reset', targetHost: false },
{ container: 'form', type: 'button', targetHost: true },
];
useCases.forEach(useCase => {
const { container, type, targetHost } = useCase;
const targetName = targetHost ? 'host' : 'native button';
it(`is ${targetName} with type ${type} and it is inside a ${container}`, async () => {
const clickSpy = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault()));
const el = /** @type {LionButton} */ (await fixture(
`<lion-button type="${type}">foo</lion-button>`,
));
const tag = unsafeStatic(container);
await fixture(html`<${tag} @click="${clickSpy}">${el}</${tag}>`);
const event = await prepareClickEvent(el);
if (targetHost) {
expect(event.target).to.equal(el);
} else {
expect(event.target).to.equal(el._nativeButtonNode);
}
});
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc