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

feat(input): prefix and suffix slots #1644

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
195 changes: 195 additions & 0 deletions .storybook/stories/forms/forms-input-group.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright (c) 2016-2024 Broadcom. All Rights Reserved.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { ChangeDetectorRef, Component, Input } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ClrFormLayout, ClrFormsModule, ClrLayoutModule } from '@clr/angular';
import { argsToTemplate, moduleMetadata, StoryObj } from '@storybook/angular';

import { CommonModules } from '../../helpers/common';

@Component({
selector: 'forms-input-group',
template: `
<form clrForm [formGroup]="form" [clrLayout]="clrLayout">
<clr-input-container>
<label>With icon on right side</label>
<input type="text" clrInput name="text" formControlName="text" />
<cds-icon shape="calendar" clrInputPrefix></cds-icon>
<clr-control-helper>Helper Subtext</clr-control-helper>
<clr-control-error>State Subtext</clr-control-error>
<clr-control-success>State Subtext</clr-control-success>
</clr-input-container>
<clr-input-container>
<label>With icon on left side</label>
<input type="text" clrInput name="text" formControlName="text" />
<cds-icon shape="calendar" clrInputSuffix></cds-icon>
<clr-control-helper>Helper Subtext</clr-control-helper>
<clr-control-error>State Subtext</clr-control-error>
<clr-control-success>State Subtext</clr-control-success>
</clr-input-container>
<clr-input-container>
<label>With icon on both sides</label>
<input type="text" clrInput name="text" formControlName="text" />
<cds-icon shape="calendar" clrInputSuffix></cds-icon>
<cds-icon shape="calendar" clrInputPrefix></cds-icon>
<clr-control-helper>Helper Subtext</clr-control-helper>
<clr-control-error>State Subtext</clr-control-error>
<clr-control-success>State Subtext</clr-control-success>
</clr-input-container>
<clr-input-container>
<label>With text prefix + suffix</label>
<input type="text" clrInput name="text" formControlName="text" />
<div clrInputPrefix>$</div>
<div clrInputSuffix>.00</div>
<clr-control-helper>Helper Subtext</clr-control-helper>
<clr-control-error>State Subtext</clr-control-error>
<clr-control-success>State Subtext</clr-control-success>
</clr-input-container>
<clr-input-container>
<label>Basic input with a select</label>
<input type="text" clrInput name="text" formControlName="text" />
<div clrInputPrefix>
<select>
<option>http://</option>
<option>https://</option>
<option>git://</option>
</select>
</div>
<clr-control-helper>Helper Subtext</clr-control-helper>
<clr-control-error>State Subtext</clr-control-error>
<clr-control-success>State Subtext</clr-control-success>
</clr-input-container>
</form>
`,
})
class FormsStoryComponent {
_isDisabled = false;
_isSuccess = false;
_isError = false;

form = new FormGroup({
text: new FormControl(),
});

@Input() clrLayout = ClrFormLayout.HORIZONTAL;

constructor(private changeDetectorRef: ChangeDetectorRef) {}

@Input()
get isDisabled() {
return this._isDisabled;
}
set isDisabled(value: boolean) {
this._isDisabled = value;
this.setControlsState();
}

@Input()
get isError() {
return this._isError;
}
set isError(value: boolean) {
this._isError = value;
this.setControlsState();
}

@Input()
get isSuccess() {
return this._isSuccess;
}
set isSuccess(value: boolean) {
this._isSuccess = value;
this.setControlsState();
}

setControlsState() {
this.form.enable();
Object.keys(this.form.controls).forEach(control => {
if (this._isDisabled) {
this.form.get(control)?.disable();
} else {
if (this._isError && !this._isSuccess) {
this.form.get(control).setErrors({ required: true });
this.form.get(control).markAsTouched();
this.form.get(control).markAsDirty();
} else if (this._isSuccess) {
this.form.get(control).setErrors(null);
this.form.get(control).markAsTouched();
}
}
});
this.form.updateValueAndValidity();
this.changeDetectorRef.detectChanges();
}
}

export default {
title: 'Forms/Input Group',
component: FormsStoryComponent,
decorators: [
moduleMetadata({
declarations: [FormsStoryComponent],
imports: [...CommonModules, ClrLayoutModule, ClrFormsModule],
}),
],
argTypes: {
getProviderFromContainer: { control: { disable: true }, table: { disable: true } },
triggerValidation: { control: { disable: true }, table: { disable: true } },
clrLayout: { control: 'radio', options: Object.values(ClrFormLayout).filter(value => typeof value === 'string') },
},
args: {
clrLayout: ClrFormLayout.HORIZONTAL,
isDisabled: false,
isError: false,
isSuccess: false,
},
render: (args: FormsStoryComponent) => ({
props: {
...args,
},
template: `
<forms-input-group ${argsToTemplate(args)}></forms-input-group>
`,
}),
};

type Story = StoryObj<FormsStoryComponent>;

export const InputStates: Story = {};

export const VerticalInputStates: Story = {
args: { clrLayout: ClrFormLayout.VERTICAL },
};

export const CompactInputStates: Story = {
args: { clrLayout: ClrFormLayout.COMPACT },
};

export const DisabledStates: Story = {
args: { isDisabled: true },
};

export const ErrorStates: Story = {
args: { isError: true },
};
export const VerticalErrorStates: Story = {
args: { isError: true, clrLayout: ClrFormLayout.VERTICAL },
};
export const CompactErrorStates: Story = {
args: { isError: true, clrLayout: ClrFormLayout.COMPACT },
};

export const SuccessStates: Story = {
args: { isSuccess: true },
};
export const VerticalSuccessStates: Story = {
args: { isSuccess: true, clrLayout: ClrFormLayout.VERTICAL },
};
export const CompactSuccessStates: Story = {
args: { isSuccess: true, clrLayout: ClrFormLayout.COMPACT },
};
2 changes: 1 addition & 1 deletion projects/angular/clarity.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2635,7 +2635,7 @@ export class ClrInput extends WrappedFormControl<ClrInputContainer> {
// @public (undocumented)
export class ClrInputContainer extends ClrAbstractContainer {
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<ClrInputContainer, "clr-input-container", never, {}, {}, never, ["label", "[clrInput]", "clr-control-helper", "clr-control-error", "clr-control-success"], false, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<ClrInputContainer, "clr-input-container", never, {}, {}, never, ["label", "[clrInputPrefix]", "[clrInput]", "[clrInputSuffix]", "clr-control-helper", "clr-control-error", "clr-control-success"], false, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<ClrInputContainer, never>;
}
Expand Down
6 changes: 5 additions & 1 deletion projects/angular/src/forms/input/input-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import { NgControlService } from '../common/providers/ng-control.service';
<label *ngIf="!label && addGrid()"></label>
<div class="clr-control-container" [ngClass]="controlClass()">
<div class="clr-input-wrapper">
<ng-content select="[clrInput]"></ng-content>
<div class="clr-input-group">
<ng-content select="[clrInputPrefix]"></ng-content>
<ng-content select="[clrInput]"></ng-content>
<ng-content select="[clrInputSuffix]"></ng-content>
</div>
<cds-icon
*ngIf="showInvalid"
class="clr-validate-icon"
Expand Down
10 changes: 1 addition & 9 deletions projects/angular/src/forms/styles/_input-group.clarity.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@include forms-mixins.input-border-bottom-animation();
position: relative;
max-width: 100%;
height: tokens.$cds-global-space-9;
max-height: forms-variables.$clr-forms-input-wrapper-height;

&.clr-focus {
width: auto;
Expand Down Expand Up @@ -45,14 +45,6 @@

.clr-input-group-addon {
color: forms-variables.$clr-forms-subtext-color;

&:first-child {
padding: 0 0 0 #{tokens.$cds-global-space-11};
}

&:last-child {
padding: 0 #{tokens.$cds-global-space-11} 0 0;
}
}

.clr-input-group-icon-action {
Expand Down
4 changes: 2 additions & 2 deletions projects/angular/src/forms/styles/_input.clarity.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
}

.clr-input {
@include forms-mixins.custom-inputs-reset(calc(4 * #{forms-variables.$clr-forms-baseline}));
@include forms-mixins.custom-inputs-reset(forms-variables.$clr-forms-input-wrapper-height);
@include forms-mixins.form-fields-appearance();

padding: forms-variables.$clr-forms-input-padding;
max-height: calc(4 * #{forms-variables.$clr-forms-baseline});
max-height: forms-variables.$clr-forms-input-wrapper-height;
@include mixins.generate-typography-token('SECONDARY-13-RG-EXP');

&[readonly] {
Expand Down
6 changes: 4 additions & 2 deletions projects/angular/src/forms/styles/_mixins.forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
transition: background-size 0.2s ease;

&:focus,
&.clr-focus {
&.clr-focus,
&:focus-within {
border-bottom-color: $border-color;
background-size: 100% 100%;
}
Expand Down Expand Up @@ -83,7 +84,8 @@
) {
color: $text-color;
border-bottom: #{tokens.$cds-alias-object-border-width-100} solid #{$border-color};
display: inline-block;
display: inline-flex;
align-items: center;
}

@mixin disabled-form-fields() {
Expand Down
2 changes: 2 additions & 0 deletions projects/angular/src/forms/styles/_select.clarity.scss
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
.clr-error select {
&:not([multiple]) {
border-bottom-color: forms-variables.$clr-forms-invalid-color;
@include forms-mixins.input-border-bottom-animation(forms-variables.$clr-forms-invalid-color);
}
&[multiple] {
border-color: forms-variables.$clr-forms-invalid-color;
Expand All @@ -148,6 +149,7 @@
.clr-success select {
&:not([multiple]) {
border-bottom-color: forms-variables.$clr-forms-valid-color;
@include forms-mixins.input-border-bottom-animation(forms-variables.$clr-forms-valid-color);
}
&[multiple] {
border-color: forms-variables.$clr-forms-valid-color;
Expand Down
1 change: 0 additions & 1 deletion projects/angular/src/layout/_login.clarity.scss
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@
& > .clr-input-group {
max-width: 100%;
width: 100%;
padding-right: mixins.baselinePx(10);

& > .clr-input {
width: calc(100% - tokens.$cds-global-space-9);
Expand Down
Loading