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

Buttongroup: Handle selection reset for single and multiple selections #2875

Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const SINGLE_SELECTION_TYPE = 'single';
export const MULTIPLE_SELECTION_TYPE = 'multiple';
export const DEFAULT_SELECTION_TYPE = 'none';
export const SELECTION_ATTRIBUTE = 'selected';
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,28 @@ describe('modus-button-group', () => {
expect(buttonGroup.getAttribute('aria-label')).toBe('Group Label');
expect(buttonGroup.getAttribute('aria-disabled')).toBe('true');
});

it('should remove active state from buttons when selected attribute is removed or set to false', async () => {
const page = await newE2EPage();

await page.setContent(`
<modus-button-group selection-type="single">
<modus-button selected>Button 1</modus-button>
</modus-button-group>
`);
const modusButton = await page.find('modus-button');
const buttonElement = await page.find('modus-button >>> button');

modusButton.removeAttribute('selected');
await page.waitForChanges();
expect(buttonElement).not.toHaveClass('active');

modusButton.setAttribute('selected', 'true');
await page.waitForChanges();
expect(buttonElement).toHaveClass('active');

modusButton.setAttribute('selected', 'false');
await page.waitForChanges();
expect(buttonElement).not.toHaveClass('active');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ import { newSpecPage } from '@stencil/core/testing';
import { ModusButtonGroup } from './modus-button-group';

describe('modus-button-group', () => {
beforeAll(() => {
class MutationObserverMock {
observe() {}
disconnect() {}
takeRecords() {
return [];
}
}
global.MutationObserver = MutationObserverMock;
});

afterAll(() => {
delete global.MutationObserver;
});

it('renders', async () => {
const { root } = await newSpecPage({
components: [ModusButtonGroup],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
// eslint-disable-next-line
import { Component, h, Prop, Element, Event, EventEmitter, Host, Listen, Watch } from '@stencil/core';
import { ButtonGroupSelectionType } from './modus-button-group.models';
import { DEFAULT_SELECTION_TYPE, SINGLE_SELECTION_TYPE, MULTIPLE_SELECTION_TYPE } from './modus-button-group.constants';
import { Component, Element, Event, EventEmitter, h, Host, Listen, Prop, Watch } from '@stencil/core';
import { ButtonColor, ButtonSize, ButtonStyle, ButtonType } from '../modus-button/modus-button.models';
import {
DEFAULT_SELECTION_TYPE,
MULTIPLE_SELECTION_TYPE,
SELECTION_ATTRIBUTE,
SINGLE_SELECTION_TYPE,
} from './modus-button-group.constants';
import { ButtonGroupSelectionType } from './modus-button-group.models';

@Component({
tag: 'modus-button-group',
Expand All @@ -12,6 +17,7 @@ import { ButtonColor, ButtonSize, ButtonStyle, ButtonType } from '../modus-butto
export class ModusButtonGroup {
/** Array to store selected buttons */
selectedButtons: HTMLModusButtonElement[] = [];
private observer: MutationObserver | null = null;

@Element() host: HTMLElement;

Expand Down Expand Up @@ -65,6 +71,15 @@ export class ModusButtonGroup {
this.setupButtons();
}

componentDidLoad() {
this.observer = new MutationObserver(this.handleMutations.bind(this));
this.observer.observe(this.host, { subtree: true, attributes: true, attributeFilter: [SELECTION_ATTRIBUTE] });
}

disconnectedCallback() {
this.observer?.disconnect();
}

@Listen('slotchange')
handleSlotChange() {
this.setupButtons();
Expand All @@ -90,9 +105,31 @@ export class ModusButtonGroup {
this.buttonGroupClick.emit({ button: clickedButton, isSelected: this.selectedButtons.includes(clickedButton) });
}

handleMutations(mutationList: MutationRecord[]) {
for (const mutation of mutationList) {
if (mutation.type === 'attributes' && mutation.attributeName === SELECTION_ATTRIBUTE) {
this.setupButtons();
}
}
}

handleButtonSelection(button, isSelected) {
if (isSelected) {
button.setActive(true);
this.selectedButtons.push(button);
} else {
button.setActive(false);
if (this.selectedButtons.includes(button)) {
this.selectedButtons = this.selectedButtons.filter((selectedButton) => selectedButton !== button);
}
}
}

setupButtons(reset?: boolean) {
const buttons = this.host.querySelectorAll('modus-button');
this.renderButtons(buttons, reset);
customElements.whenDefined('modus-button').then(() => {
const buttons = this.host.querySelectorAll('modus-button');
this.renderButtons(buttons, reset);
});
}

renderButtons(buttons: NodeListOf<HTMLModusButtonElement>, reset: boolean) {
Expand All @@ -111,15 +148,13 @@ export class ModusButtonGroup {
button.color = this.color;
button.size = this.size;
button.type = buttonType;
if (button.hasAttribute('selected') && !foundSelected && this.selectionType == SINGLE_SELECTION_TYPE) {
button.setActive(true);
this.selectedButtons.push(button);
if (button.hasAttribute(SELECTION_ATTRIBUTE) && !foundSelected && this.selectionType == SINGLE_SELECTION_TYPE) {
this.handleButtonSelection(button, button.getAttribute(SELECTION_ATTRIBUTE) !== 'false');
foundSelected = true;
} else if (button.hasAttribute('selected') && this.selectionType == MULTIPLE_SELECTION_TYPE) {
button.setActive(true);
if (!this.selectedButtons.includes(button)) {
this.selectedButtons.push(button);
}
} else if (button.hasAttribute(SELECTION_ATTRIBUTE) && this.selectionType == MULTIPLE_SELECTION_TYPE) {
this.handleButtonSelection(button, button.getAttribute(SELECTION_ATTRIBUTE) !== 'false');
} else {
this.handleButtonSelection(button, false);
}
});
}
Expand Down
2 changes: 2 additions & 0 deletions stencil-workspace/src/components/modus-number-input/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
| Property | Attribute | Description | Type | Default |
| ------------- | ------------- | ------------------------------------------------------------- | --------------------- | ----------- |
| `ariaLabel` | `aria-label` | (optional) The input's aria-label. | `string` | `undefined` |
| `currency` | `currency` | (optional) The input's currency | `string` | `undefined` |
| `disabled` | `disabled` | (optional) Whether the input is disabled. | `boolean` | `undefined` |
| `errorText` | `error-text` | (optional) The input's error state text. | `string` | `undefined` |
| `helperText` | `helper-text` | (optional) The input's helper text displayed below the input. | `string` | `undefined` |
| `label` | `label` | (optional) The input's label. | `string` | `undefined` |
| `locale` | `locale` | (optional) The input's locale | `string` | `undefined` |
| `maxValue` | `max-value` | (optional) The input's maximum value. | `number` | `undefined` |
| `minValue` | `min-value` | (optional) The input's minimum value. | `number` | `undefined` |
| `placeholder` | `placeholder` | (optional) The input's placeholder text. | `string` | `undefined` |
Expand Down