Skip to content

Commit

Permalink
refactor: extract radio-button and radio-group logic to mixins (#6680)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Oct 20, 2023
1 parent 34652cd commit c1d2e1f
Show file tree
Hide file tree
Showing 10 changed files with 635 additions and 536 deletions.
1 change: 1 addition & 0 deletions packages/radio-group/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"polymer"
],
"dependencies": {
"@open-wc/dedupe-mixin": "^1.3.0",
"@polymer/polymer": "^3.0.0",
"@vaadin/a11y-base": "24.3.0-alpha5",
"@vaadin/component-base": "24.3.0-alpha5",
Expand Down
50 changes: 50 additions & 0 deletions packages/radio-group/src/vaadin-radio-button-mixin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @license
* Copyright (c) 2017 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { Constructor } from '@open-wc/dedupe-mixin';
import type { ActiveMixinClass } from '@vaadin/a11y-base/src/active-mixin.js';
import type { DelegateFocusMixinClass } from '@vaadin/a11y-base/src/delegate-focus-mixin.js';
import type { DisabledMixinClass } from '@vaadin/a11y-base/src/disabled-mixin.js';
import type { FocusMixinClass } from '@vaadin/a11y-base/src/focus-mixin.js';
import type { KeyboardMixinClass } from '@vaadin/a11y-base/src/keyboard-mixin.js';
import type { DelegateStateMixinClass } from '@vaadin/component-base/src/delegate-state-mixin.js';
import type { CheckedMixinClass } from '@vaadin/field-base/src/checked-mixin.js';
import type { InputMixinClass } from '@vaadin/field-base/src/input-mixin.js';
import type { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';

/**
* Fired when the `checked` property changes.
*/
export type RadioButtonCheckedChangedEvent = CustomEvent<{ value: boolean }>;

export interface RadioButtonCustomEventMap {
'checked-changed': RadioButtonCheckedChangedEvent;
}

export interface RadioButtonEventMap extends HTMLElementEventMap, RadioButtonCustomEventMap {}

/**
* A mixin providing common radio-button functionality.
*/
export declare function RadioButtonMixin<T extends Constructor<HTMLElement>>(
base: T,
): Constructor<ActiveMixinClass> &
Constructor<CheckedMixinClass> &
Constructor<DelegateFocusMixinClass> &
Constructor<DelegateStateMixinClass> &
Constructor<DisabledMixinClass> &
Constructor<FocusMixinClass> &
Constructor<InputMixinClass> &
Constructor<KeyboardMixinClass> &
Constructor<LabelMixinClass> &
Constructor<RadioButtonMixinClass> &
T;

export declare class RadioButtonMixinClass {
/**
* The name of the radio button.
*/
name: string;
}
67 changes: 67 additions & 0 deletions packages/radio-group/src/vaadin-radio-button-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @license
* Copyright (c) 2017 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ActiveMixin } from '@vaadin/a11y-base/src/active-mixin.js';
import { DelegateFocusMixin } from '@vaadin/a11y-base/src/delegate-focus-mixin.js';
import { CheckedMixin } from '@vaadin/field-base/src/checked-mixin.js';
import { InputController } from '@vaadin/field-base/src/input-controller.js';
import { LabelMixin } from '@vaadin/field-base/src/label-mixin.js';
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';

/**
* A mixin providing common radio-button functionality.
*
* @polymerMixin
* @mixes ActiveMixin
* @mixes CheckedMixin
* @mixes DelegateFocusMixin
* @mixes LabelMixin
*/
export const RadioButtonMixin = (superclass) =>
class RadioButtonMixinClass extends LabelMixin(CheckedMixin(DelegateFocusMixin(ActiveMixin(superclass)))) {
static get properties() {
return {
/**
* The name of the radio button.
*
* @type {string}
*/
name: {
type: String,
value: '',
},
};
}

/** @override */
static get delegateAttrs() {
return [...super.delegateAttrs, 'name'];
}

constructor() {
super();

this._setType('radio');

// Set the string "on" as the default value for the radio button following the HTML specification:
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on
this.value = 'on';
}

/** @protected */
ready() {
super.ready();

this.addController(
new InputController(this, (input) => {
this._setInputElement(input);
this._setFocusElement(input);
this.stateTarget = input;
this.ariaTarget = input;
}),
);
this.addController(new LabelledInputController(this.inputElement, this._labelController));
}
};
25 changes: 3 additions & 22 deletions packages/radio-group/src/vaadin-radio-button.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,12 @@
* Copyright (c) 2017 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ActiveMixin } from '@vaadin/a11y-base/src/active-mixin.js';
import { DelegateFocusMixin } from '@vaadin/a11y-base/src/delegate-focus-mixin.js';
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { CheckedMixin } from '@vaadin/field-base/src/checked-mixin.js';
import { LabelMixin } from '@vaadin/field-base/src/label-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { type RadioButtonEventMap, RadioButtonMixin } from './vaadin-radio-button-mixin.js';

/**
* Fired when the `checked` property changes.
*/
export type RadioButtonCheckedChangedEvent = CustomEvent<{ value: boolean }>;

export interface RadioButtonCustomEventMap {
'checked-changed': RadioButtonCheckedChangedEvent;
}

export interface RadioButtonEventMap extends HTMLElementEventMap, RadioButtonCustomEventMap {}
export * from './vaadin-radio-button-mixin.js';

/**
* `<vaadin-radio-button>` is a web component representing a choice in a radio group.
Expand Down Expand Up @@ -57,14 +45,7 @@ export interface RadioButtonEventMap extends HTMLElementEventMap, RadioButtonCus
*
* @fires {CustomEvent} checked-changed - Fired when the `checked` property changes.
*/
declare class RadioButton extends LabelMixin(
CheckedMixin(DelegateFocusMixin(ActiveMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement)))))),
) {
/**
* The name of the radio button.
*/
name: string;

declare class RadioButton extends RadioButtonMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement)))) {
addEventListener<K extends keyof RadioButtonEventMap>(
type: K,
listener: (this: RadioButton, ev: RadioButtonEventMap[K]) => void,
Expand Down
60 changes: 3 additions & 57 deletions packages/radio-group/src/vaadin-radio-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { ActiveMixin } from '@vaadin/a11y-base/src/active-mixin.js';
import { DelegateFocusMixin } from '@vaadin/a11y-base/src/delegate-focus-mixin.js';
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { CheckedMixin } from '@vaadin/field-base/src/checked-mixin.js';
import { InputController } from '@vaadin/field-base/src/input-controller.js';
import { LabelMixin } from '@vaadin/field-base/src/label-mixin.js';
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { RadioButtonMixin } from './vaadin-radio-button-mixin.js';
import { radioButtonStyles } from './vaadin-radio-button-styles.js';

registerStyles('vaadin-radio-button', radioButtonStyles, { moduleId: 'vaadin-radio-button-styles' });
Expand Down Expand Up @@ -55,16 +50,11 @@ registerStyles('vaadin-radio-button', radioButtonStyles, { moduleId: 'vaadin-rad
*
* @customElement
* @extends HTMLElement
* @mixes ControllerMixin
* @mixes ThemableMixin
* @mixes ElementMixin
* @mixes ActiveMixin
* @mixes CheckedMixin
* @mixes LabelMixin
* @mixes RadioButtonMixin
*/
class RadioButton extends LabelMixin(
CheckedMixin(DelegateFocusMixin(ActiveMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))))),
) {
class RadioButton extends RadioButtonMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))) {
static get is() {
return 'vaadin-radio-button';
}
Expand All @@ -78,50 +68,6 @@ class RadioButton extends LabelMixin(
</div>
`;
}

static get properties() {
return {
/**
* The name of the radio button.
*
* @type {string}
*/
name: {
type: String,
value: '',
},
};
}

/** @override */
static get delegateAttrs() {
return [...super.delegateAttrs, 'name'];
}

constructor() {
super();

this._setType('radio');

// Set the string "on" as the default value for the radio button following the HTML specification:
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on
this.value = 'on';
}

/** @protected */
ready() {
super.ready();

this.addController(
new InputController(this, (input) => {
this._setInputElement(input);
this._setFocusElement(input);
this.stateTarget = input;
this.ariaTarget = input;
}),
);
this.addController(new LabelledInputController(this.inputElement, this._labelController));
}
}

defineCustomElement(RadioButton);
Expand Down
68 changes: 68 additions & 0 deletions packages/radio-group/src/vaadin-radio-group-mixin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @license
* Copyright (c) 2017 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { Constructor } from '@open-wc/dedupe-mixin';
import type { DisabledMixinClass } from '@vaadin/a11y-base/src/disabled-mixin.js';
import type { FocusMixinClass } from '@vaadin/a11y-base/src/focus-mixin.js';
import type { KeyboardMixinClass } from '@vaadin/a11y-base/src/keyboard-mixin.js';
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
import type { FieldMixinClass } from '@vaadin/field-base/src/field-mixin.js';
import type { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';
import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';

/**
* Fired when the `invalid` property changes.
*/
export type RadioGroupInvalidChangedEvent = CustomEvent<{ value: boolean }>;

/**
* Fired when the `value` property changes.
*/
export type RadioGroupValueChangedEvent = CustomEvent<{ value: string }>;

/**
* Fired whenever the field is validated.
*/
export type RadioGroupValidatedEvent = CustomEvent<{ valid: boolean }>;

export interface RadioGroupCustomEventMap {
'invalid-changed': RadioGroupInvalidChangedEvent;

'value-changed': RadioGroupValueChangedEvent;

validated: RadioGroupValidatedEvent;
}

export interface RadioGroupEventMap extends HTMLElementEventMap, RadioGroupCustomEventMap {}

/**
* A mixin providing common radio-group functionality.
*/
export declare function RadioGroupMixin<T extends Constructor<HTMLElement>>(
base: T,
): Constructor<ControllerMixinClass> &
Constructor<DisabledMixinClass> &
Constructor<FieldMixinClass> &
Constructor<FocusMixinClass> &
Constructor<KeyboardMixinClass> &
Constructor<LabelMixinClass> &
Constructor<RadioGroupMixinClass> &
Constructor<ValidateMixinClass> &
T;

export declare class RadioGroupMixinClass {
/**
* The value of the radio group.
*/
value: string | null | undefined;

/**
* When present, the user cannot modify the value of the radio group.
* The property works similarly to the `disabled` property.
* While the `disabled` property disables all the radio buttons inside the group,
* the `readonly` property disables only unchecked ones.
*/
readonly: boolean;
}
Loading

0 comments on commit c1d2e1f

Please sign in to comment.