Skip to content

Commit

Permalink
refactor/radio-group and add card layout (#531)
Browse files Browse the repository at this point in the history
* refactor(radio-group): change brn-radio-group to directive and hlm-radio-group to component

* expose brn-radio-group inputs/outputs
* apply dir and tabindex attribute for radio-group
* change styles for radio-group
* apply reverse style for rtl direction

* docs(radio-group): update default imports/skeleton

* docs(radio-group): add card layout example

* Todos: style issue with short text for first card

* fix(radio-group): lint issues

* fix(radio-group): update import and template for e2e

* fix(radio-group): remove tabindex="0" from group to forward the focus on tab to the first radio item
  • Loading branch information
marcjulian authored Dec 23, 2024
1 parent 3164754 commit c15c095
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { provideIcons } from '@ng-icons/core';
import { lucideApple, lucideCreditCard } from '@ng-icons/lucide';
import { BrnRadioComponent } from '@spartan-ng/brain/radio-group';
import { HlmIconComponent } from '@spartan-ng/ui-icon-helm';
import { HlmRadioDirective, HlmRadioGroupComponent } from '@spartan-ng/ui-radiogroup-helm';

@Component({
selector: 'spartan-radio-card-preview',
standalone: true,
providers: [provideIcons({ lucideCreditCard, lucideApple })],
imports: [FormsModule, BrnRadioComponent, HlmRadioDirective, HlmRadioGroupComponent, HlmIconComponent],
template: `
<hlm-radio-group class="grid grid-cols-3 gap-4" [(ngModel)]="payment">
<brn-radio hlm value="card" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<hlm-icon name="lucideCreditCard" class="mb-3" />
<!-- FIXME style issue with short text -->
Card
</div>
</brn-radio>
<brn-radio hlm value="paypal" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="mb-3 size-6">
<title>PayPal</title>
<path
d="M7.016 19.198h-4.2a.562.562 0 0 1-.555-.65L5.093.584A.692.692 0 0 1 5.776 0h7.222c3.417 0 5.904 2.488 5.846 5.5-.006.25-.027.5-.066.747A6.794 6.794 0 0 1 12.071 12H8.743a.69.69 0 0 0-.682.583l-.325 2.056-.013.083-.692 4.39-.015.087zM19.79 6.142c-.01.087-.01.175-.023.261a7.76 7.76 0 0 1-7.695 6.598H9.007l-.283 1.795-.013.083-.692 4.39-.134.843-.014.088H6.86l-.497 3.15a.562.562 0 0 0 .555.65h3.612c.34 0 .63-.249.683-.585l.952-6.031a.692.692 0 0 1 .683-.584h2.126a6.793 6.793 0 0 0 6.707-5.752c.306-1.95-.466-3.744-1.89-4.906z"
/>
</svg>
PayPal
</div>
</brn-radio>
<brn-radio hlm value="apple" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="mb-3 size-6">
<title>Apple</title>
<path
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
/>
</svg>
Apple
</div>
</brn-radio>
</hlm-radio-group>
`,
})
export class RadioGroupCardComponent {
public payment = 'card';
}

export const cardCode = `
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { provideIcons } from '@ng-icons/core';
import { lucideApple, lucideCreditCard } from '@ng-icons/lucide';
import { BrnRadioComponent } from '@spartan-ng/brain/radio-group';
import { HlmIconComponent } from '@spartan-ng/ui-icon-helm';
import { HlmRadioDirective, HlmRadioGroupComponent } from '@spartan-ng/ui-radiogroup-helm';
@Component({
selector: 'spartan-radio-card-preview',
standalone: true,
providers: [provideIcons({ lucideCreditCard, lucideApple })],
imports: [FormsModule, BrnRadioComponent, HlmRadioDirective, HlmRadioGroupComponent, HlmIconComponent],
template: \`
<hlm-radio-group class="grid grid-cols-3 gap-4" [(ngModel)]="payment">
<brn-radio hlm value="card" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<hlm-icon name="lucideCreditCard" class="mb-3" />
Card
</div>
</brn-radio>
<brn-radio hlm value="paypal" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="mb-3 size-6">
<title>PayPal</title>
<path
d="M7.016 19.198h-4.2a.562.562 0 0 1-.555-.65L5.093.584A.692.692 0 0 1 5.776 0h7.222c3.417 0 5.904 2.488 5.846 5.5-.006.25-.027.5-.066.747A6.794 6.794 0 0 1 12.071 12H8.743a.69.69 0 0 0-.682.583l-.325 2.056-.013.083-.692 4.39-.015.087zM19.79 6.142c-.01.087-.01.175-.023.261a7.76 7.76 0 0 1-7.695 6.598H9.007l-.283 1.795-.013.083-.692 4.39-.134.843-.014.088H6.86l-.497 3.15a.562.562 0 0 0 .555.65h3.612c.34 0 .63-.249.683-.585l.952-6.031a.692.692 0 0 1 .683-.584h2.126a6.793 6.793 0 0 0 6.707-5.752c.306-1.95-.466-3.744-1.89-4.906z"
/>
</svg>
PayPal
</div>
</brn-radio>
<brn-radio hlm value="apple" class="group space-x-0">
<div
class="border-muted bg-popover hover:bg-accent hover:text-accent-foreground group-data-[checked=true]:border-primary flex flex-col items-center justify-between rounded-md border-2 p-4"
>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="mb-3 size-6">
<title>Apple</title>
<path
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
/>
</svg>
Apple
</div>
</brn-radio>
</hlm-radio-group>
\`,
})
export class RadioGroupCardComponent {
public payment = 'card';
}
`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RouteMeta } from '@analogjs/router';
import { Component } from '@angular/core';
import { hlmH4 } from '@spartan-ng/ui-typography-helm';
import { CodePreviewDirective } from '../../../../shared/code/code-preview.directive';
import { CodeComponent } from '../../../../shared/code/code.component';
import { MainSectionDirective } from '../../../../shared/layout/main-section.directive';
Expand All @@ -11,7 +12,8 @@ import { SectionSubHeadingComponent } from '../../../../shared/layout/section-su
import { TabsCliComponent } from '../../../../shared/layout/tabs-cli.component';
import { TabsComponent } from '../../../../shared/layout/tabs.component';
import { metaWith } from '../../../../shared/meta/meta.util';
import { RadioGroupPreviewComponent, defaultCode, defaultImports, defaultSkeleton } from './radio-group.preview';
import { cardCode, RadioGroupCardComponent } from './radio-group--card.example';
import { defaultCode, defaultImports, defaultSkeleton, RadioGroupPreviewComponent } from './radio-group.preview';

export const routeMeta: RouteMeta = {
data: { breadcrumb: 'Radio Group' },
Expand All @@ -36,6 +38,8 @@ export const routeMeta: RouteMeta = {
PageBottomNavComponent,
PageBottomNavLinkComponent,
RadioGroupPreviewComponent,
RadioGroupPreviewComponent,
RadioGroupCardComponent,
],
template: `
<section spartanMainSection>
Expand Down Expand Up @@ -64,6 +68,15 @@ export const routeMeta: RouteMeta = {
<spartan-code [code]="defaultSkeleton" />
</div>
<spartan-section-sub-heading id="examples">Examples</spartan-section-sub-heading>
<h3 id="examples__default" class="${hlmH4} mb-2 mt-6">Card Layout</h3>
<spartan-tabs firstTab="Preview" secondTab="Code">
<div spartanCodePreview firstTab>
<spartan-radio-card-preview />
</div>
<spartan-code secondTab [code]="cardCode" />
</spartan-tabs>
<spartan-page-bottom-nav>
<spartan-page-bottom-nav-link href="scroll-area" label="Scroll Area" />
<spartan-page-bottom-nav-link direction="previous" href="progress" label="Progress" />
Expand All @@ -76,4 +89,6 @@ export default class LabelPageComponent {
protected readonly defaultCode = defaultCode;
protected readonly defaultSkeleton = defaultSkeleton;
protected readonly defaultImports = defaultImports;

protected readonly cardCode = cardCode;
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrnRadioComponent, BrnRadioGroupComponent } from '@spartan-ng/brain/radio-group';
import { HlmRadioDirective, HlmRadioGroupDirective, HlmRadioIndicatorComponent } from '@spartan-ng/ui-radiogroup-helm';
import { BrnRadioComponent } from '@spartan-ng/brain/radio-group';
import { HlmRadioDirective, HlmRadioGroupComponent, HlmRadioIndicatorComponent } from '@spartan-ng/ui-radiogroup-helm';
import { HlmSmallDirective } from '@spartan-ng/ui-typography-helm';

@Component({
selector: 'spartan-radio-group-preview',
standalone: true,
imports: [
FormsModule,
BrnRadioGroupComponent,
BrnRadioComponent,
HlmRadioIndicatorComponent,
HlmRadioDirective,
HlmRadioGroupDirective,
HlmRadioGroupComponent,
HlmSmallDirective,
],
template: `
<small hlmSmall class="font-semibold">Choose a version</small>
<brn-radio-group class="mb-4 space-y-1 font-mono text-sm font-medium" hlm [(ngModel)]="version">
<hlm-radio-group class="font-mono text-sm font-medium" [(ngModel)]="version">
<brn-radio hlm value="16.1.4">
<hlm-radio-indicator indicator />
v16.1.4
Expand All @@ -35,7 +34,7 @@ import { HlmSmallDirective } from '@spartan-ng/ui-typography-helm';
<hlm-radio-indicator indicator />
v15.2.0
</brn-radio>
</brn-radio-group>
</hlm-radio-group>
`,
})
export class RadioGroupPreviewComponent {
Expand All @@ -45,42 +44,41 @@ export class RadioGroupPreviewComponent {
export const defaultCode = `
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrnRadioComponent, BrnRadioGroupComponent } from '@spartan-ng/brain/radio-group';
import { HlmRadioDirective, HlmRadioGroupDirective, HlmRadioIndicatorComponent } from '@spartan-ng/ui-radiogroup-helm';
import { BrnRadioComponent } from '@spartan-ng/brain/radio-group';
import { HlmRadioDirective, HlmRadioGroupComponent, HlmRadioIndicatorComponent } from '@spartan-ng/ui-radiogroup-helm';
import { HlmSmallDirective } from '@spartan-ng/ui-typography-helm';
@Component({
selector: 'spartan-radio-group-preview',
standalone: true,
imports: [
FormsModule,
BrnRadioGroupComponent,
BrnRadioComponent,
HlmRadioIndicatorComponent,
HlmRadioDirective,
HlmRadioGroupDirective,
HlmSmallDirective,
BrnRadioComponent,
HlmRadioIndicatorComponent,
HlmRadioDirective,
HlmRadioGroupComponent,
HlmSmallDirective,
],
template: \`
<small hlmSmall class="font-semibold">Choose a version</small>
<brn-radio-group class="mb-4 font-mono text-sm font-medium space-y-1" hlm [(ngModel)]="version">
<brn-radio hlm value="16.1.4">
<hlm-radio-indicator indicator />
v16.1.4
</brn-radio>
<brn-radio hlm value="16.0.0">
<hlm-radio-indicator indicator />
v16.0.0
</brn-radio>
<brn-radio hlm value="15.8.0">
<hlm-radio-indicator indicator />
v15.8.0
</brn-radio>
<brn-radio disabled hlm value="15.2.0">
<hlm-radio-indicator indicator />
v15.2.0
</brn-radio>
</brn-radio-group>
<hlm-radio-group class="font-mono text-sm font-medium" [(ngModel)]="version">
<brn-radio hlm value="16.1.4">
<hlm-radio-indicator indicator />
v16.1.4
</brn-radio>
<brn-radio hlm value="16.0.0">
<hlm-radio-indicator indicator />
v16.0.0
</brn-radio>
<brn-radio hlm value="15.8.0">
<hlm-radio-indicator indicator />
v15.8.0
</brn-radio>
<brn-radio disabled hlm value="15.2.0">
<hlm-radio-indicator indicator />
v15.2.0
</brn-radio>
</hlm-radio-group>
\`,
})
export class RadioGroupPreviewComponent {
Expand All @@ -89,18 +87,18 @@ export class RadioGroupPreviewComponent {
`;

export const defaultImports = `
import { BrnRadioComponent, BrnRadioGroupComponent } from '@spartan-ng/brain/radio-group';
import { BrnRadioComponent } from '@spartan-ng/brain/radio-group';
import {
HlmRadioDirective,
HlmRadioGroupDirective,
HlmRadioGroupComponent,
HlmRadioIndicatorComponent,
} from '@spartan-ng/ui-radiogroup-helm';
`;
export const defaultSkeleton = `
<brn-radio-group hlm>
<hlm-radio-group>
<brn-radio hlm value="16.1.4">
<hlm-radio-indicator indicator />
v16.1.4
</brn-radio>
</brn-radio-group>
</hlm-radio-group>
`;
6 changes: 3 additions & 3 deletions libs/brain/radio-group/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { NgModule } from '@angular/core';

import { BrnRadioGroupComponent } from './lib/brn-radio-group.component';
import { BrnRadioGroupDirective } from './lib/brn-radio-group.directive';
import { BrnRadioComponent } from './lib/brn-radio.component';

export * from './lib/brn-radio-group.component';
export * from './lib/brn-radio-group.directive';
export * from './lib/brn-radio.component';

export const BrnRadioGroupImports = [BrnRadioGroupComponent, BrnRadioComponent] as const;
export const BrnRadioGroupImports = [BrnRadioGroupDirective, BrnRadioComponent] as const;

@NgModule({
imports: [...BrnRadioGroupImports],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import { BooleanInput } from '@angular/cdk/coercion';
import {
booleanAttribute,
Component,
computed,
contentChildren,
Directive,
forwardRef,
input,
model,
Expand All @@ -18,28 +18,28 @@ import { BrnRadioChange, BrnRadioComponent } from './brn-radio.component';

export const BRN_RADIO_GROUP_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => BrnRadioGroupComponent),
useExisting: forwardRef(() => BrnRadioGroupDirective),
multi: true,
};

@Component({
selector: 'brn-radio-group',
@Directive({
selector: '[brnRadioGroup]',
standalone: true,
providers: [BRN_RADIO_GROUP_CONTROL_VALUE_ACCESSOR, provideBrnRadioGroupToken(BrnRadioGroupComponent)],
providers: [BRN_RADIO_GROUP_CONTROL_VALUE_ACCESSOR, provideBrnRadioGroupToken(BrnRadioGroupDirective)],
host: {
role: 'radiogroup',
'[dir]': 'direction()',
'(focusout)': 'onTouched()',
},
template: '<ng-content />',
})
export class BrnRadioGroupComponent<T = unknown> implements ControlValueAccessor {
export class BrnRadioGroupDirective<T = unknown> implements ControlValueAccessor {
private static _nextUniqueId = 0;

protected onChange: ChangeFn<T> = () => {};

protected onTouched: TouchFn = () => {};

public readonly name = input(`brn-radio-group-${BrnRadioGroupComponent._nextUniqueId++}`);
public readonly name = input(`brn-radio-group-${BrnRadioGroupDirective._nextUniqueId++}`);

/**
* The value of the selected radio button.
Expand Down
12 changes: 6 additions & 6 deletions libs/brain/radio-group/src/lib/brn-radio-group.token.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ExistingProvider, inject, InjectionToken, Type } from '@angular/core';
import type { BrnRadioGroupComponent } from './brn-radio-group.component';
import type { BrnRadioGroupDirective } from './brn-radio-group.directive';

const BrnRadioGroupToken = new InjectionToken<BrnRadioGroupComponent<unknown>>('BrnRadioGroupToken');
const BrnRadioGroupToken = new InjectionToken<BrnRadioGroupDirective<unknown>>('BrnRadioGroupToken');

export function provideBrnRadioGroupToken<T>(component: Type<BrnRadioGroupComponent<T>>): ExistingProvider {
return { provide: BrnRadioGroupToken, useExisting: component };
export function provideBrnRadioGroupToken<T>(directive: Type<BrnRadioGroupDirective<T>>): ExistingProvider {
return { provide: BrnRadioGroupToken, useExisting: directive };
}

export function injectBrnRadioGroup<T = unknown>(): BrnRadioGroupComponent<T> {
return inject(BrnRadioGroupToken) as BrnRadioGroupComponent<T>;
export function injectBrnRadioGroup<T = unknown>(): BrnRadioGroupDirective<T> {
return inject(BrnRadioGroupToken) as BrnRadioGroupDirective<T>;
}
1 change: 1 addition & 0 deletions libs/ui/radio-group/helm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {},
"peerDependencies": {
"@angular/core": ">=18.0.0",
"@spartan-ng/brain": "0.0.1-alpha.357",
"@spartan-ng/ui-core": "0.0.1-alpha.357",
"clsx": "^2.1.1"
},
Expand Down
Loading

0 comments on commit c15c095

Please sign in to comment.