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: button toggle: Allow consumer to control pressed state #5143

Merged
merged 6 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions components/button/button-toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,28 @@ class ButtonToggle extends LitElement {
elem.focus();
}

_clickCancelled(e) {
e.stopPropagation();
const customClick = new CustomEvent('click', {
cancelable: true
});
e.target.dispatchEvent(customClick);
return customClick.defaultPrevented;
}

async _handleClick(pressed) {
this.pressed = pressed;
await this.updateComplete;
this.focus();
}

_handleNotPressedClick() {
_handleNotPressedClick(e) {
if (this._clickCancelled(e)) return;
this._handleClick(true);
}

_handlePressedClick() {
_handlePressedClick(e) {
if (this._clickCancelled(e)) return;
this._handleClick(false);
}

Expand Down
24 changes: 24 additions & 0 deletions components/button/demo/button-toggle.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,30 @@ <h2>Toggle Button (disabled)</h2>
</template>
</d2l-demo-snippet>

<h2>Toggle Button (consumer manages state)</h2>

<d2l-demo-snippet>
<template>
<d2l-button-toggle id="toggle-button-icon-consumer-manage-state">
<d2l-button-icon slot="not-pressed" icon="tier1:pin-hollow" text="Unpinned, click to pin." id="button-icon-not-pressed"></d2l-button-icon>
<d2l-button-icon slot="pressed" icon="tier1:pin-filled" text="Pinned, click to unpin." id="button-icon-pressed"></d2l-button-icon>
</d2l-button-toggle>
<script>
const buttonToggle = document.querySelector('#toggle-button-icon-consumer-manage-state');
buttonToggle.addEventListener('d2l-button-toggle-change', e => console.log(e));

document.querySelector('#button-icon-not-pressed').addEventListener('click', (e) => {
e.preventDefault();
buttonToggle.pressed = true;
});
document.querySelector('#button-icon-pressed').addEventListener('click', (e) => {
e.preventDefault();
buttonToggle.pressed = false;
});
</script>
</template>
</d2l-demo-snippet>

</d2l-demo-page>

</body>
Expand Down
74 changes: 68 additions & 6 deletions components/button/test/button-toggle.vdiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import { clickElem, expect, fixture, focusElem, hoverElem, html, sendKeysElem }

describe('button-toggle', () => {

const buttonIconTemplate = html`<d2l-button-toggle><d2l-button-icon slot="not-pressed" icon="tier1:pin-hollow" text="Unpinned, click to pin."></d2l-button-icon><d2l-button-icon slot="pressed" icon="tier1:pin-filled" text="Pinned, click to unpin."></d2l-button-icon></d2l-button-toggle>`;
const getActiveButton = elem => {
if (elem.pressed) return elem.querySelector('[slot="pressed"]');
else return elem.querySelector('[slot="not-pressed"]');
};

[
{ category: 'button-icon', template: html`<d2l-button-toggle><d2l-button-icon slot="not-pressed" icon="tier1:pin-hollow" text="Unpinned, click to pin."></d2l-button-icon><d2l-button-icon slot="pressed" icon="tier1:pin-filled" text="Pinned, click to unpin."></d2l-button-icon></d2l-button-toggle>` },
{ category: 'button-icon', template: buttonIconTemplate },
{ category: 'button-icon-pressed', template: html`<d2l-button-toggle pressed><d2l-button-icon slot="not-pressed" icon="tier1:pin-hollow" text="Unpinned, click to pin."></d2l-button-icon><d2l-button-icon slot="pressed" icon="tier1:pin-filled" text="Pinned, click to unpin."></d2l-button-icon></d2l-button-toggle>` },
{ category: 'button-subtle', template: html`<d2l-button-toggle><d2l-button-subtle slot="not-pressed" icon="tier1:lock-unlock" text="Unlocked" description="Click to lock."></d2l-button-subtle><d2l-button-subtle slot="pressed" icon="tier1:lock-locked" text="Locked" description="Click to unlock."></d2l-button-subtle></d2l-button-toggle>` },
{ category: 'button-subtle-pressed', template: html`<d2l-button-toggle pressed><d2l-button-subtle slot="not-pressed" icon="tier1:lock-unlock" text="Unlocked" description="Click to lock."></d2l-button-subtle><d2l-button-subtle slot="pressed" icon="tier1:lock-locked" text="Locked" description="Click to unlock."></d2l-button-subtle></d2l-button-toggle>` },
{ category: 'button-subtle-disabled', template: html`<d2l-button-toggle><d2l-button-subtle slot="not-pressed" disabled icon="tier1:lock-unlock" text="Unlocked" description="Click to lock."></d2l-button-subtle><d2l-button-subtle slot="pressed" disabled icon="tier1:lock-locked" text="Locked" description="Click to unlock."></d2l-button-subtle></d2l-button-toggle>` }
].forEach(({ category, template }) => {

const getActiveButton = elem => {
if (elem.pressed) return elem.querySelector('[slot="pressed"]');
else return elem.querySelector('[slot="not-pressed"]');
};

describe(category, () => {
[
{ name: 'normal' },
Expand All @@ -36,4 +37,65 @@ describe('button-toggle', () => {

});

describe('consumer manages state', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These feel like they might be better suited to normal unit tests that just assert that the value of elem.pressed is what you're expecting.


it('normal', async() => {
const elem = await fixture(buttonIconTemplate);
const buttonIcons = elem.querySelectorAll('d2l-button-icon');
buttonIcons[0].addEventListener('click', (e) => {
e.preventDefault();
});
buttonIcons[1].addEventListener('click', (e) => {
e.preventDefault();
});
await expect(elem).to.be.golden();
});

it('click with no state management', async() => {
const elem = await fixture(buttonIconTemplate);
const buttonIcons = elem.querySelectorAll('d2l-button-icon');
buttonIcons[0].addEventListener('click', (e) => {
e.preventDefault();
});
buttonIcons[1].addEventListener('click', (e) => {
e.preventDefault();
});
clickElem(getActiveButton(elem));
await expect(elem).to.be.golden();
});

it('click once with state management', async() => {
const elem = await fixture(buttonIconTemplate);
const buttonIcons = elem.querySelectorAll('d2l-button-icon');
buttonIcons[0].addEventListener('click', (e) => {
e.preventDefault();
elem.pressed = true;
});
buttonIcons[1].addEventListener('click', (e) => {
e.preventDefault();
elem.pressed = false;
});

clickElem(getActiveButton(elem));
await expect(elem).to.be.golden();
});

it('click twice with state management', async() => {
const elem = await fixture(buttonIconTemplate);
const buttonIcons = elem.querySelectorAll('d2l-button-icon');
buttonIcons[0].addEventListener('click', (e) => {
e.preventDefault();
elem.pressed = true;
});
buttonIcons[1].addEventListener('click', (e) => {
e.preventDefault();
elem.pressed = false;
});

clickElem(getActiveButton(elem));
clickElem(getActiveButton(elem));
await expect(elem).to.be.golden();
});
});

});
Loading