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

fix(radio): updated disabled state #186

Merged
merged 3 commits into from
Dec 11, 2024
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
Expand Up @@ -22,7 +22,7 @@ export class RdxRadioItemInputDirective {

readonly name = input<string>();
readonly value = computed(() => this.radioItem.value() || undefined);
readonly checked = computed(() => this.radioItem.checked || undefined);
readonly checked = computed(() => this.radioItem.checkedState() || undefined);
readonly required = input<boolean | undefined>(this.radioItem.required());
readonly disabled = input<boolean | undefined>(this.radioItem.disabled());
}
32 changes: 20 additions & 12 deletions packages/primitives/radio/src/radio-item.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { BooleanInput } from '@angular/cdk/coercion';
import { booleanAttribute, Directive, ElementRef, inject, InjectionToken, input, OnInit, signal } from '@angular/core';
import {
booleanAttribute,
computed,
Directive,
ElementRef,
inject,
InjectionToken,
input,
OnInit,
signal
} from '@angular/core';
import { RdxRovingFocusItemDirective } from '@radix-ng/primitives/roving-focus';
import { RDX_RADIO_GROUP } from './radio-tokens';

Expand All @@ -20,9 +30,10 @@ export function injectRadioItem(): RdxRadioItemDirective {
host: {
type: 'button',
role: 'radio',
'[attr.aria-checked]': 'checked',
'[attr.data-disabled]': 'disabled() ? "" : null',
'[attr.data-state]': 'checked ? "checked" : "unchecked"',
'[attr.aria-checked]': 'checkedState()',
'[attr.data-disabled]': 'disabledState() ? "" : null',
'[attr.data-state]': 'checkedState() ? "checked" : "unchecked"',
'[disabled]': 'disabledState()',
'(click)': 'onClick()',
'(keydown)': 'onKeyDown($event)',
'(keyup)': 'onKeyUp()',
Expand All @@ -41,6 +52,10 @@ export class RdxRadioItemDirective implements OnInit {

readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

protected readonly disabledState = computed(() => this.radioGroup.disableState() || this.disabled());

readonly checkedState = computed(() => this.radioGroup.value() === this.value());

private readonly ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
private readonly isArrowKeyPressedSignal = signal(false);

Expand All @@ -51,16 +66,9 @@ export class RdxRadioItemDirective implements OnInit {
}
}

/** @ignore */
get checked(): boolean {
if (this.radioGroup.value == undefined) return false;

return this.radioGroup.value() === this.value();
}

/** @ignore */
onClick() {
if (!this.disabled()) {
if (!this.disabledState()) {
this.radioGroup.select(this.value());
this.isArrowKeyPressedSignal.set(true);
}
Expand Down
25 changes: 15 additions & 10 deletions packages/primitives/radio/src/radio-root.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { booleanAttribute, Directive, EventEmitter, Input, model, Output } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { booleanAttribute, computed, Directive, input, Input, model, output, signal } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Orientation, RdxRovingFocusGroupDirective } from '@radix-ng/primitives/roving-focus';
import { RadioGroupDirective, RadioGroupProps, RDX_RADIO_GROUP } from './radio-tokens';
Expand All @@ -14,26 +15,30 @@ import { RadioGroupDirective, RadioGroupProps, RDX_RADIO_GROUP } from './radio-t
hostDirectives: [{ directive: RdxRovingFocusGroupDirective, inputs: ['dir', 'orientation', 'loop'] }],
host: {
role: 'radiogroup',
'[attr.aria-orientation]': 'orientation',
'[attr.data-disabled]': 'disabled ? "" : null',
'[attr.aria-orientation]': 'orientation()',
'[attr.aria-required]': 'required()',
'[attr.data-disabled]': 'disableState() ? "" : null',
'(keydown)': 'onKeydown()'
}
})
export class RdxRadioGroupDirective implements RadioGroupProps, RadioGroupDirective, ControlValueAccessor {
readonly value = model<string | undefined>();
readonly value = model<string | null>(null);

@Input({ transform: booleanAttribute }) disabled = false;
readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

@Input() defaultValue?: string;

@Input() required: boolean;
readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });

@Input() orientation: Orientation;
readonly orientation = input<Orientation>();

/**
* Event handler called when the value changes.
*/
@Output() readonly onValueChange = new EventEmitter<string>();
readonly onValueChange = output<string>();

private readonly disable = signal<boolean>(this.disabled());
readonly disableState = computed(() => this.disable() || this.disabled());
Copy link
Collaborator

Choose a reason for hiding this comment

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

why you create additional signal, why not use existing this.disabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


/**
* The callback function to call when the value of the radio group changes.
Expand Down Expand Up @@ -91,10 +96,10 @@ export class RdxRadioGroupDirective implements RadioGroupProps, RadioGroupDirect
* @ignore
*/
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
this.disable.set(isDisabled);
}

protected onKeydown(): void {
if (this.disabled) return;
if (this.disableState()) return;
}
}
10 changes: 6 additions & 4 deletions packages/primitives/radio/src/radio-tokens.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { InjectionToken, ModelSignal } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { InjectionToken, InputSignalWithTransform, ModelSignal, Signal } from '@angular/core';

export interface RadioGroupProps {
name?: string;
disabled?: boolean;
disabled?: InputSignalWithTransform<boolean, BooleanInput>;
defaultValue?: string;
value?: ModelSignal<string | undefined>;
value: ModelSignal<string | null>;
disableState: Signal<boolean>;
}

export interface RadioGroupDirective extends RadioGroupProps {
select(value: string | undefined): void;
select(value: string | null): void;

onTouched(): void;
}
Expand Down
47 changes: 47 additions & 0 deletions packages/primitives/radio/stories/radio.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ export default {
box-shadow: 0 0 0 2px black;
}

.RadioGroupItem:disabled {
background-color: var(--gray-4);
box-shadow: none;
cursor: not-allowed;
}

.RadioGroupItem:disabled:hover {
background-color: var(--gray-4);
}

.RadioGroupIndicator {
display: flex;
align-items: center;
Expand Down Expand Up @@ -136,3 +146,40 @@ export const RadioGroup: Story = {
template: `<radio-groups-forms-example></radio-groups-forms-example>`
})
};

export const DisabledGroup: Story = {
render: () => ({
template: html`
<div
class="RadioGroupRoot"
rdxRadioRoot
[value]="'comfortable'"
disabled
orientation="vertical"
aria-label="View density"
>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r1" rdxRadioItem value="default">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r1">Default</label>
</div>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r2" rdxRadioItem [required]="true" value="comfortable">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r2">Comfortable</label>
</div>
<div style="display: flex; align-items: center;">
<button class="RadioGroupItem" id="r3" rdxRadioItem value="compact">
<div class="RadioGroupIndicator" rdxRadioIndicator></div>
<input class="Input" rdxRadioItemInput feature="fully-hidden" />
</button>
<label class="Label" rdxLabel htmlFor="r3">Compact</label>
</div>
</div>
`
})
};
Loading