1
0
mirror of https://github.com/bitwarden/jslib synced 2026-01-06 10:33:15 +00:00

Component Library scaffolding (#625)

This commit is contained in:
Oscar Hinton
2022-03-08 11:50:34 +01:00
committed by GitHub
parent fa3a95fed0
commit 67a4fc8591
45 changed files with 62205 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
import { Meta, Story } from "@storybook/angular";
import { ButtonComponent } from "./button.component";
// More on default export: https://storybook.js.org/docs/angular/writing-stories/introduction#default-export
export default {
title: "Jslib/Button",
component: ButtonComponent,
args: {
buttonType: "primary",
},
// More on argTypes: https://storybook.js.org/docs/angular/api/argtypes
} as Meta;
// More on component templates: https://storybook.js.org/docs/angular/writing-stories/introduction#using-args
const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
props: args,
template: `<button bit-button [buttonType]="buttonType" [block]="block">Test</button>`,
});
export const Primary = Template.bind({});
// More on args: https://storybook.js.org/docs/angular/writing-stories/args
Primary.args = {
buttonType: "primary",
};
export const Secondary = Template.bind({});
Secondary.args = {
buttonType: "secondary",
};
export const Danger = Template.bind({});
Danger.args = {
buttonType: "danger",
};
const DisabledTemplate: Story = (args) => ({
props: args,
template: `
<button bit-button disabled buttonType="primary" class="tw-mr-2">Primary</button>
<button bit-button disabled buttonType="secondary" class="tw-mr-2">Secondary</button>
<button bit-button disabled buttonType="danger" class="tw-mr-2">Danger</button>
`,
});
export const Disabled = DisabledTemplate.bind({});
Disabled.args = {
size: "small",
};

View File

@@ -0,0 +1,57 @@
import { Component } from "@angular/core";
import { TestBed, waitForAsync } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { ButtonModule } from "./index";
describe("Button", () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [ButtonModule],
declarations: [TestApp],
});
TestBed.compileComponents();
})
);
it("should apply classes based on type", () => {
const fixture = TestBed.createComponent(TestApp);
const testAppComponent: TestApp = fixture.debugElement.componentInstance;
const buttonDebugElement = fixture.debugElement.query(By.css("button"));
const linkDebugElement = fixture.debugElement.query(By.css("a"));
testAppComponent.buttonType = "primary";
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains("tw-bg-primary-500")).toBe(true);
expect(linkDebugElement.nativeElement.classList.contains("tw-bg-primary-500")).toBe(true);
testAppComponent.buttonType = "secondary";
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains("tw-border-text-muted")).toBe(true);
expect(linkDebugElement.nativeElement.classList.contains("tw-border-text-muted")).toBe(true);
testAppComponent.buttonType = "danger";
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains("tw-border-danger-500")).toBe(true);
expect(linkDebugElement.nativeElement.classList.contains("tw-border-danger-500")).toBe(true);
testAppComponent.buttonType = null;
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains("tw-border-text-muted")).toBe(true);
expect(linkDebugElement.nativeElement.classList.contains("tw-border-text-muted")).toBe(true);
});
});
@Component({
selector: "test-app",
template: `
<button type="button" bit-button [buttonType]="buttonType">Button</button>
<a href="#" bit-button [buttonType]="buttonType"> Link </a>
`,
})
class TestApp {
buttonType: string;
}

View File

@@ -0,0 +1,77 @@
import { Input, HostBinding, OnChanges, Directive } from "@angular/core";
export type ButtonTypes = "primary" | "secondary" | "danger";
const buttonStyles: Record<ButtonTypes, string> = {
primary: [
"tw-border-primary-500",
"tw-bg-primary-500",
"!tw-text-contrast",
"hover:tw-bg-primary-700",
"hover:tw-border-primary-700",
"focus:tw-bg-primary-700",
"focus:tw-border-primary-700",
].join(" "),
secondary: [
"tw-bg-transparent",
"tw-border-text-muted",
"!tw-text-muted",
"hover:tw-bg-secondary-500",
"hover:tw-border-secondary-500",
"hover:tw-text-contrast",
"focus:tw-bg-secondary-500",
"focus:tw-border-secondary-500",
"focus:tw-text-contrast",
].join(" "),
danger: [
"tw-bg-transparent",
"tw-border-danger-500",
"!tw-text-danger",
"hover:tw-bg-danger-500",
"hover:tw-border-danger-500",
"hover:tw-text-contrast",
"focus:tw-bg-danger-500",
"focus:tw-border-danger-500",
"focus:tw-text-contrast",
].join(" "),
};
@Directive({
selector: "button[bit-button], a[bit-button]",
})
export class ButtonComponent implements OnChanges {
@HostBinding("class") @Input("class") classList = "";
@Input()
buttonType: ButtonTypes = "secondary";
@Input()
block = false;
ngOnChanges() {
this.classList = this.classes.join(" ");
}
get classes(): string[] {
return [
"tw-font-semibold",
"tw-py-1.5",
"tw-px-3",
"tw-rounded",
"tw-transition",
"tw-border",
"tw-border-solid",
"tw-text-center",
"hover:tw-no-underline",
"disabled:tw-bg-secondary-100",
"disabled:tw-border-secondary-100",
"disabled:!tw-text-main",
"focus:tw-outline-none",
"focus:tw-ring",
"focus:tw-ring-offset-2",
"focus:tw-ring-primary-700",
this.block ? "tw-w-full tw-block" : "",
buttonStyles[this.buttonType ?? "secondary"],
];
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { ButtonComponent } from "./button.component";
@NgModule({
imports: [CommonModule],
exports: [ButtonComponent],
declarations: [ButtonComponent],
})
export class ButtonModule {}

View File

@@ -0,0 +1,2 @@
export * from "./button.component";
export * from "./button.module";