Skip to content

Commit

Permalink
Add Input Field + Label Components (#11)
Browse files Browse the repository at this point in the history
Co-authored-by: Felix T.J. Dietrich <[email protected]>
Co-authored-by: Felix T.J. Dietrich <[email protected]>
  • Loading branch information
3 people authored Aug 6, 2024
1 parent 7fec3c2 commit 7717137
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 0 deletions.
13 changes: 13 additions & 0 deletions webapp/src/app/ui/input/input.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<input
[class]="computedClass()"
[type]="type()"
[placeholder]="placeholder()"
[disabled]="disabled()"
[value]="value()"
[id]="id()"
(input)="onInput($event)"
>




51 changes: 51 additions & 0 deletions webapp/src/app/ui/input/input.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, computed, input, output } from '@angular/core';
import type { ClassValue } from 'clsx';
import { VariantProps } from 'class-variance-authority';
import { cn } from 'app/utils';
import { cva } from 'app/storybook.helper';

const [inputVariants, args, argTypes] = cva(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
{
variants: {
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 px-2 py-1',
lg: 'h-11 px-4 py-3',
},
},
defaultVariants: {
size: 'default',
},
},
);

export { args, argTypes };

interface InputVariants extends VariantProps<typeof inputVariants> {}

@Component({
selector: 'app-input',
standalone: true,
templateUrl: './input.component.html',
})
export class AppInputComponent {
class = input<ClassValue>('');
type = input<string>('text');
placeholder = input<string>('');
size = input<InputVariants['size']>('default');
disabled = input<boolean>(false);
value = input<string>('');
id = input<string>('');

valueChange = output<string>();

onInput(event: Event) {
const inputValue = (event.target as HTMLInputElement).value;
this.valueChange.emit(inputValue);
}

computedClass = computed(() =>
cn(inputVariants({ size: this.size() }), this.class()),
);
}
86 changes: 86 additions & 0 deletions webapp/src/app/ui/input/input.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
argsToTemplate,
moduleMetadata,
type Meta,
type StoryObj,
} from '@storybook/angular';
import { AppInputComponent, args, argTypes } from './input.component';
import { action } from '@storybook/addon-actions';
import { AppButtonComponent } from '@app/ui/button/button/button.component';
import { AppLabelComponent } from '@app/ui/label/label.component';

const meta: Meta<AppInputComponent> = {
title: 'UI/Input',
component: AppInputComponent,
tags: ['autodocs'],
args: {
...args,
value: '',
disabled: false,
size: 'default',
},
argTypes: {
...argTypes,
disabled: {
control: 'boolean',
},
onInput: {
action: 'onInput',
},
},
decorators: [
moduleMetadata({
imports: [AppButtonComponent, AppLabelComponent],
}),
],
};

export default meta;
type Story = StoryObj<AppInputComponent>;

export const Default: Story = {
render: (args) => ({
props: args,
template: `<app-input ${argsToTemplate(args)} placeholder="Enter text here"/>`,
}),
};

export const Disabled: Story = {
args: {
disabled: true,
},
render: (args) => ({
props: args,
template: `<app-input ${argsToTemplate(args)} placeholder="Disabled input"/>`,
}),
};

export const WithLabel: Story = {
render: (args) => ({
props: args,
template: `
<div class="grid w-full max-w-sm items-center gap-1.5">
<app-label [for]="input-field" size="sm">Label</app-label>
<app-input ${argsToTemplate(args)} [id]="input-field" placeholder="Enter text here" class="grow"></app-input>
</div>
`,
}),
};

export const WithButton: Story = {
render: (args) => ({
props: {
args,
userInput: '',
onButtonClick(value: string) {
action('Button Clicked')(`Input Value: ${value}`);
},
},
template: `
<div class="flex gap-2 flex-row">
<app-input ${argsToTemplate(args)} [size]="args.size" [(value)]="userInput" placeholder="Enter text here" class="grow"/>
<app-button (onClick)="onButtonClick(userInput)" size="${args.size ?? 'default'}" [disabled]="!userInput">Submit</app-button>
</div>
`,
}),
};
6 changes: 6 additions & 0 deletions webapp/src/app/ui/label/label.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<label
[class]="computedClass()"
[for]="for()"
>
<ng-content/>
</label>
25 changes: 25 additions & 0 deletions webapp/src/app/ui/label/label.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Component, computed, input } from '@angular/core';
import type { ClassValue } from 'clsx';
import { VariantProps } from 'class-variance-authority';
import { cn } from 'app/utils';
import { cva } from 'app/storybook.helper';

const [labelVariants, args, argTypes] = cva('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70');

export { args, argTypes };

interface LabelVariants extends VariantProps<typeof labelVariants> {}

@Component({
selector: 'app-label',
standalone: true,
templateUrl: './label.component.html',
})
export class AppLabelComponent {
class = input<ClassValue>('');
for = input<string>('');

computedClass = computed(() =>
cn(labelVariants({}), this.class()),
);
}
25 changes: 25 additions & 0 deletions webapp/src/app/ui/label/label.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { argsToTemplate, type Meta, type StoryObj } from '@storybook/angular';
import { AppLabelComponent, args, argTypes } from './label.component';

const meta: Meta<AppLabelComponent> = {
title: 'UI/Label',
component: AppLabelComponent,
tags: ['autodocs'],
args: {
...args,
for: 'example-input',
},
argTypes: {
...argTypes,
},
};

export default meta;
type Story = StoryObj<AppLabelComponent>;

export const Default: Story = {
render: (args) => ({
props: args,
template: `<app-label ${argsToTemplate(args)}>Label</app-label>`,
}),
};

0 comments on commit 7717137

Please sign in to comment.