mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-08 19:18:02 +01:00
[PM-2195] Adjust radio groups margin (#5410)
* Adjust radio groups margin * Move hint margin to input field * Tweak field spacing * Add story for hint, and fix hint display * Fix label ssue * Revert input margin * Re-add margin to hint * Change font weight * Fix required placement * Add support for required * Change margin of radio group * Remove unnecessary div * Fix long inputs cutting off * Fix radio story * Remove newline --------- Co-authored-by: Will Martin <contact@willmartian.com>
This commit is contained in:
parent
651593bcd2
commit
50493ab6f7
@ -25,6 +25,7 @@ export class CheckboxComponent implements BitFormControlAbstraction {
|
|||||||
"tw-w-3.5",
|
"tw-w-3.5",
|
||||||
"tw-mr-1.5",
|
"tw-mr-1.5",
|
||||||
"tw-bottom-[-1px]", // Fix checkbox looking off-center
|
"tw-bottom-[-1px]", // Fix checkbox looking off-center
|
||||||
|
"tw-flex-none", // Flexbox fix for bit-form-control
|
||||||
|
|
||||||
"before:tw-content-['']",
|
"before:tw-content-['']",
|
||||||
"before:tw-block",
|
"before:tw-block",
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule, FormBuilder, Validators } from "@angular/forms";
|
import {
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormBuilder,
|
||||||
|
Validators,
|
||||||
|
FormGroup,
|
||||||
|
FormControl,
|
||||||
|
} from "@angular/forms";
|
||||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/src/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/src/platform/abstractions/i18n.service";
|
||||||
@ -15,7 +22,8 @@ const template = `
|
|||||||
<input type="checkbox" bitCheckbox formControlName="checkbox">
|
<input type="checkbox" bitCheckbox formControlName="checkbox">
|
||||||
<bit-label>Click me</bit-label>
|
<bit-label>Click me</bit-label>
|
||||||
</bit-form-control>
|
</bit-form-control>
|
||||||
</form>`;
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-example",
|
selector: "app-example",
|
||||||
@ -89,6 +97,40 @@ export const Default: Story = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Hint: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: {
|
||||||
|
formObj: new FormGroup({
|
||||||
|
checkbox: new FormControl(false),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="formObj">
|
||||||
|
<bit-form-control>
|
||||||
|
<input type="checkbox" bitCheckbox formControlName="checkbox">
|
||||||
|
<bit-label>Really long value that never ends.</bit-label>
|
||||||
|
<bit-hint>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur iaculis consequat enim vitae elementum.
|
||||||
|
Ut non odio est. Duis eu nisi ultrices, porttitor lorem eget, ornare libero. Fusce ex ante, consequat ac
|
||||||
|
sem et, euismod placerat tellus.
|
||||||
|
</bit-hint>
|
||||||
|
</bit-form-control>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
source: {
|
||||||
|
code: template,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
checked: false,
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const Custom: Story = {
|
export const Custom: Story = {
|
||||||
render: (args) => ({
|
render: (args) => ({
|
||||||
props: args,
|
props: args,
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<label [class]="labelClasses">
|
<label [class]="labelClasses">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
<span [class]="labelContentClasses">
|
<span [class]="labelContentClasses">
|
||||||
|
<span>
|
||||||
<ng-content select="bit-label"></ng-content>
|
<ng-content select="bit-label"></ng-content>
|
||||||
<span *ngIf="required" class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span>
|
<span *ngIf="required" class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
|
||||||
<div>
|
|
||||||
<ng-content select="bit-hint" *ngIf="!hasError"></ng-content>
|
<ng-content select="bit-hint" *ngIf="!hasError"></ng-content>
|
||||||
</div>
|
</span>
|
||||||
|
</label>
|
||||||
<div *ngIf="hasError" class="tw-mt-1 tw-text-danger">
|
<div *ngIf="hasError" class="tw-mt-1 tw-text-danger">
|
||||||
<i class="bwi bwi-error"></i> {{ displayError }}
|
<i class="bwi bwi-error"></i> {{ displayError }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,22 +20,36 @@ export class FormControlComponent {
|
|||||||
this._inline = coerceBooleanProperty(value);
|
this._inline = coerceBooleanProperty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _disableMargin = false;
|
||||||
|
@Input() set disableMargin(value: boolean | "") {
|
||||||
|
this._disableMargin = coerceBooleanProperty(value);
|
||||||
|
}
|
||||||
|
get disableMargin() {
|
||||||
|
return this._disableMargin;
|
||||||
|
}
|
||||||
|
|
||||||
@ContentChild(BitFormControlAbstraction) protected formControl: BitFormControlAbstraction;
|
@ContentChild(BitFormControlAbstraction) protected formControl: BitFormControlAbstraction;
|
||||||
|
|
||||||
@HostBinding("class") get classes() {
|
@HostBinding("class") get classes() {
|
||||||
return ["tw-mb-6"].concat(this.inline ? ["tw-inline-block", "tw-mr-4"] : ["tw-block"]);
|
return []
|
||||||
|
.concat(this.inline ? ["tw-inline-block", "tw-mr-4"] : ["tw-block"])
|
||||||
|
.concat(this.disableMargin ? [] : ["tw-mb-6"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private i18nService: I18nService) {}
|
constructor(private i18nService: I18nService) {}
|
||||||
|
|
||||||
protected get labelClasses() {
|
protected get labelClasses() {
|
||||||
return ["tw-transition", "tw-select-none", "tw-mb-0"].concat(
|
return [
|
||||||
this.formControl.disabled ? "tw-cursor-auto" : "tw-cursor-pointer"
|
"tw-transition",
|
||||||
);
|
"tw-select-none",
|
||||||
|
"tw-mb-0",
|
||||||
|
"tw-inline-flex",
|
||||||
|
"tw-items-baseline",
|
||||||
|
].concat(this.formControl.disabled ? "tw-cursor-auto" : "tw-cursor-pointer");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get labelContentClasses() {
|
protected get labelContentClasses() {
|
||||||
return ["tw-font-semibold"].concat(
|
return ["tw-inline-flex", "tw-flex-col", "tw-font-semibold"].concat(
|
||||||
this.formControl.disabled ? "tw-text-muted" : "tw-text-main"
|
this.formControl.disabled ? "tw-text-muted" : "tw-text-main"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ let nextId = 0;
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: "bit-hint",
|
selector: "bit-hint",
|
||||||
host: {
|
host: {
|
||||||
class: "tw-text-muted tw-inline-block tw-mt-1",
|
class: "tw-text-muted tw-font-normal tw-inline-block tw-mt-1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class BitHintComponent {
|
export class BitHintComponent {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<bit-form-control inline>
|
<bit-form-control [inline]="!block" disableMargin>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bitRadio
|
bitRadio
|
||||||
[id]="inputId"
|
[id]="inputId"
|
||||||
[name]="name"
|
|
||||||
[disabled]="groupDisabled || disabled"
|
[disabled]="groupDisabled || disabled"
|
||||||
[value]="value"
|
[value]="value"
|
||||||
[checked]="selected"
|
[checked]="selected"
|
||||||
(change)="onInputChange()"
|
(change)="onInputChange()"
|
||||||
(blur)="onBlur()"
|
(blur)="onBlur()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ng-content select="bit-label" ngProjectAs="bit-label"></ng-content>
|
<ng-content select="bit-label" ngProjectAs="bit-label"></ng-content>
|
||||||
<ng-content select="bit-hint" ngProjectAs="bit-hint"></ng-content>
|
<ng-content select="bit-hint" ngProjectAs="bit-hint"></ng-content>
|
||||||
</bit-form-control>
|
</bit-form-control>
|
||||||
|
@ -10,6 +10,10 @@ let nextId = 0;
|
|||||||
})
|
})
|
||||||
export class RadioButtonComponent {
|
export class RadioButtonComponent {
|
||||||
@HostBinding("attr.id") @Input() id = `bit-radio-button-${nextId++}`;
|
@HostBinding("attr.id") @Input() id = `bit-radio-button-${nextId++}`;
|
||||||
|
@HostBinding("class") get classList() {
|
||||||
|
return [this.block ? "tw-block" : "tw-inline-block", "tw-mb-2"];
|
||||||
|
}
|
||||||
|
|
||||||
@Input() value: unknown;
|
@Input() value: unknown;
|
||||||
@Input() disabled = false;
|
@Input() disabled = false;
|
||||||
|
|
||||||
@ -31,6 +35,10 @@ export class RadioButtonComponent {
|
|||||||
return this.groupComponent.disabled;
|
return this.groupComponent.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get block() {
|
||||||
|
return this.groupComponent.block;
|
||||||
|
}
|
||||||
|
|
||||||
protected onInputChange() {
|
protected onInputChange() {
|
||||||
this.groupComponent.onInputChange(this.value);
|
this.groupComponent.onInputChange(this.value);
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { FormControlModule } from "../form-control";
|
import { FormControlModule } from "../form-control";
|
||||||
|
import { SharedModule } from "../shared";
|
||||||
|
|
||||||
import { RadioButtonComponent } from "./radio-button.component";
|
import { RadioButtonComponent } from "./radio-button.component";
|
||||||
import { RadioGroupComponent } from "./radio-group.component";
|
import { RadioGroupComponent } from "./radio-group.component";
|
||||||
import { RadioInputComponent } from "./radio-input.component";
|
import { RadioInputComponent } from "./radio-input.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, FormControlModule],
|
imports: [CommonModule, SharedModule, FormControlModule],
|
||||||
declarations: [RadioInputComponent, RadioButtonComponent, RadioGroupComponent],
|
declarations: [RadioInputComponent, RadioButtonComponent, RadioGroupComponent],
|
||||||
exports: [FormControlModule, RadioInputComponent, RadioButtonComponent, RadioGroupComponent],
|
exports: [FormControlModule, RadioInputComponent, RadioButtonComponent, RadioGroupComponent],
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms";
|
||||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ export const Inline: Story = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Block: Story = {
|
export const InlineHint: Story = {
|
||||||
render: () => ({
|
render: () => ({
|
||||||
props: {
|
props: {
|
||||||
formObj: new FormGroup({
|
formObj: new FormGroup({
|
||||||
@ -79,17 +79,48 @@ export const Block: Story = {
|
|||||||
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
<bit-radio-group formControlName="radio" aria-label="Example radio group">
|
||||||
<bit-label>Group of radio buttons</bit-label>
|
<bit-label>Group of radio buttons</bit-label>
|
||||||
|
|
||||||
<bit-radio-button id="radio-first" class="tw-block" [value]="0">
|
<bit-radio-button id="radio-first" [value]="0">
|
||||||
|
<bit-label>First</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-second" [value]="1">
|
||||||
|
<bit-label>Second</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-third" [value]="2">
|
||||||
|
<bit-label>Third</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-hint>This is a hint for the radio group</bit-hint>
|
||||||
|
</bit-radio-group>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Block: Story = {
|
||||||
|
render: () => ({
|
||||||
|
props: {
|
||||||
|
formObj: new FormGroup({
|
||||||
|
radio: new FormControl(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="formObj">
|
||||||
|
<bit-radio-group formControlName="radio" aria-label="Example radio group" [block]="true">
|
||||||
|
<bit-label>Group of radio buttons</bit-label>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-first" [value]="0">
|
||||||
<bit-label>First</bit-label>
|
<bit-label>First</bit-label>
|
||||||
<bit-hint>This is a hint for the first option</bit-hint>
|
<bit-hint>This is a hint for the first option</bit-hint>
|
||||||
</bit-radio-button>
|
</bit-radio-button>
|
||||||
|
|
||||||
<bit-radio-button id="radio-second" class="tw-block" [value]="1">
|
<bit-radio-button id="radio-second" [value]="1">
|
||||||
<bit-label>Second</bit-label>
|
<bit-label>Second</bit-label>
|
||||||
<bit-hint>This is a hint for the second option</bit-hint>
|
<bit-hint>This is a hint for the second option</bit-hint>
|
||||||
</bit-radio-button>
|
</bit-radio-button>
|
||||||
|
|
||||||
<bit-radio-button id="radio-third" class="tw-block" [value]="2">
|
<bit-radio-button id="radio-third" [value]="2">
|
||||||
<bit-label>Third</bit-label>
|
<bit-label>Third</bit-label>
|
||||||
<bit-hint>This is a hint for the third option</bit-hint>
|
<bit-hint>This is a hint for the third option</bit-hint>
|
||||||
</bit-radio-button>
|
</bit-radio-button>
|
||||||
@ -98,3 +129,37 @@ export const Block: Story = {
|
|||||||
`,
|
`,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BlockHint: Story = {
|
||||||
|
render: () => ({
|
||||||
|
props: {
|
||||||
|
formObj: new FormGroup({
|
||||||
|
radio: new FormControl(0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="formObj">
|
||||||
|
<bit-radio-group formControlName="radio" aria-label="Example radio group" [block]="true">
|
||||||
|
<bit-label>Group of radio buttons</bit-label>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-first" [value]="0">
|
||||||
|
<bit-label>First</bit-label>
|
||||||
|
<bit-hint>This is a hint for the first option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-second" [value]="1">
|
||||||
|
<bit-label>Second</bit-label>
|
||||||
|
<bit-hint>This is a hint for the second option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-radio-button id="radio-third" [value]="2">
|
||||||
|
<bit-label>Third</bit-label>
|
||||||
|
<bit-hint>This is a hint for the third option</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
|
||||||
|
<bit-hint>This is a hint for the radio group</bit-hint>
|
||||||
|
</bit-radio-group>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend class="tw-mb-1 tw-block tw-text-sm tw-font-semibold tw-text-main">
|
<legend class="tw-mb-1 tw-block tw-text-sm tw-font-semibold tw-text-main">
|
||||||
<ng-content select="bit-label"></ng-content>
|
<ng-content select="bit-label"></ng-content>
|
||||||
|
<span *ngIf="required" class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span>
|
||||||
</legend>
|
</legend>
|
||||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -11,4 +12,9 @@
|
|||||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #content><ng-content></ng-content></ng-template>
|
<ng-template #content>
|
||||||
|
<div>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
<ng-content select="bit-hint" ngProjectAs="bit-hint"></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
|
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
|
||||||
import { ControlValueAccessor, NgControl } from "@angular/forms";
|
import { ControlValueAccessor, NgControl, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { BitLabel } from "../form-control/label.directive";
|
import { BitLabel } from "../form-control/label.directive";
|
||||||
|
|
||||||
@ -21,8 +21,11 @@ export class RadioGroupComponent implements ControlValueAccessor {
|
|||||||
this._name = value;
|
this._name = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input() block = false;
|
||||||
|
|
||||||
@HostBinding("attr.role") role = "radiogroup";
|
@HostBinding("attr.role") role = "radiogroup";
|
||||||
@HostBinding("attr.id") @Input() id = `bit-radio-group-${nextId++}`;
|
@HostBinding("attr.id") @Input() id = `bit-radio-group-${nextId++}`;
|
||||||
|
@HostBinding("class") classList = ["tw-block", "tw-mb-4"];
|
||||||
|
|
||||||
@ContentChild(BitLabel) protected label: BitLabel;
|
@ContentChild(BitLabel) protected label: BitLabel;
|
||||||
|
|
||||||
@ -32,6 +35,10 @@ export class RadioGroupComponent implements ControlValueAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get required() {
|
||||||
|
return this.ngControl?.control?.hasValidator(Validators.required) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
// ControlValueAccessor
|
// ControlValueAccessor
|
||||||
onChange: (value: unknown) => void;
|
onChange: (value: unknown) => void;
|
||||||
onTouched: () => void;
|
onTouched: () => void;
|
||||||
|
@ -29,6 +29,7 @@ export class RadioInputComponent implements BitFormControlAbstraction {
|
|||||||
"tw-h-3.5",
|
"tw-h-3.5",
|
||||||
"tw-mr-1.5",
|
"tw-mr-1.5",
|
||||||
"tw-bottom-[-1px]", // Fix checkbox looking off-center
|
"tw-bottom-[-1px]", // Fix checkbox looking off-center
|
||||||
|
"tw-flex-none", // Flexbox fix for bit-form-control
|
||||||
|
|
||||||
"hover:tw-border-2",
|
"hover:tw-border-2",
|
||||||
"[&>label:hover]:tw-border-2",
|
"[&>label:hover]:tw-border-2",
|
||||||
|
Loading…
Reference in New Issue
Block a user