From 156eabe77456a739dabf82e0dbff397f5905cbd2 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 21 Nov 2022 13:13:31 +0100 Subject: [PATCH] [CL-59] [Bug] Link buttons have different height depending on html tag used (#3954) * [CL-59] feat: add explicit relative line-height to button * [EC-59] feat: fix using pseudo element workaround * [EC-59] fix: inconsistent templates * [CL-59] feat: add inline example * [CL-59] fix: tweak horizontal padding --- libs/components/src/link/link.directive.ts | 92 +++++++++++++++------- libs/components/src/link/link.module.ts | 6 +- libs/components/src/link/link.stories.ts | 57 ++++++++++---- 3 files changed, 109 insertions(+), 46 deletions(-) diff --git a/libs/components/src/link/link.directive.ts b/libs/components/src/link/link.directive.ts index d4ae14058ec..0d80fb27f18 100644 --- a/libs/components/src/link/link.directive.ts +++ b/libs/components/src/link/link.directive.ts @@ -6,49 +6,85 @@ const linkStyles: Record = { primary: [ "!tw-text-primary-500", "hover:!tw-text-primary-500", - "focus-visible:tw-ring-primary-700", + "focus-visible:before:tw-ring-primary-700", "disabled:!tw-text-primary-500/60", ], secondary: [ "!tw-text-main", "hover:!tw-text-main", - "focus-visible:tw-ring-primary-700", + "focus-visible:before:tw-ring-primary-700", "disabled:!tw-text-muted/60", ], contrast: [ "!tw-text-contrast", "hover:!tw-text-contrast", - "focus-visible:tw-ring-text-contrast", + "focus-visible:before:tw-ring-text-contrast", "disabled:!tw-text-contrast/60", ], }; -@Directive({ - selector: "button[bitLink], a[bitLink]", -}) -export class LinkDirective { - @HostBinding("class") get classList() { - return [ - "tw-font-semibold", - "tw-py-0.5", - "tw-px-0", - "tw-bg-transparent", - "tw-border-0", - "tw-border-none", - "tw-rounded", - "tw-transition", - "hover:tw-underline", - "hover:tw-decoration-1", - "focus-visible:tw-outline-none", - "focus-visible:tw-underline", - "focus-visible:tw-decoration-1", - "focus-visible:tw-ring-2", - "focus-visible:tw-z-10", - "disabled:tw-no-underline", - "disabled:tw-cursor-not-allowed", - ].concat(linkStyles[this.linkType] ?? []); - } +const commonStyles = [ + "tw-leading-none", + "tw-p-0", + "tw-font-semibold", + "tw-bg-transparent", + "tw-border-0", + "tw-border-none", + "tw-rounded", + "tw-transition", + "hover:tw-underline", + "hover:tw-decoration-1", + "disabled:tw-no-underline", + "disabled:tw-cursor-not-allowed", + "focus-visible:tw-outline-none", + "focus-visible:tw-underline", + "focus-visible:tw-decoration-1", + // Workaround for html button tag not being able to be set to `display: inline` + // and at the same time not being able to use `tw-ring-offset` because of box-shadow issue. + // https://github.com/w3c/csswg-drafts/issues/3226 + // Add `tw-inline`, add `tw-py-0.5` and use regular `tw-ring` if issue is fixed. + // + // https://github.com/tailwindlabs/tailwindcss/issues/3595 + // Remove `before:` and use regular `tw-ring` when browser no longer has bug, or better: + // switch to `outline` with `outline-offset` when Safari supports border radius on outline. + // Using `box-shadow` to create outlines is a hack and as such `outline` should be preferred. + "tw-relative", + "before:tw-content-['']", + "before:tw-block", + "before:tw-absolute", + "before:-tw-inset-x-[0.1em]", + "before:tw-rounded-md", + "before:tw-transition", + "before:tw-ring-2", + "focus-visible:before:tw-ring-text-contrast", + "focus-visible:tw-z-10", +]; + +@Directive() +abstract class LinkDirective { @Input() linkType: LinkType = "primary"; } + +@Directive({ + selector: "a[bitLink]", +}) +export class AnchorLinkDirective extends LinkDirective { + @HostBinding("class") get classList() { + return ["before:-tw-inset-y-[0.125rem]"] + .concat(commonStyles) + .concat(linkStyles[this.linkType] ?? []); + } +} + +@Directive({ + selector: "button[bitLink]", +}) +export class ButtonLinkDirective extends LinkDirective { + @HostBinding("class") get classList() { + return ["before:-tw-inset-y-[0.25rem]"] + .concat(commonStyles) + .concat(linkStyles[this.linkType] ?? []); + } +} diff --git a/libs/components/src/link/link.module.ts b/libs/components/src/link/link.module.ts index 73e78edf785..b8b54d57c00 100644 --- a/libs/components/src/link/link.module.ts +++ b/libs/components/src/link/link.module.ts @@ -1,11 +1,11 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { LinkDirective } from "./link.directive"; +import { AnchorLinkDirective, ButtonLinkDirective } from "./link.directive"; @NgModule({ imports: [CommonModule], - exports: [LinkDirective], - declarations: [LinkDirective], + exports: [AnchorLinkDirective, ButtonLinkDirective], + declarations: [AnchorLinkDirective, ButtonLinkDirective], }) export class LinkModule {} diff --git a/libs/components/src/link/link.stories.ts b/libs/components/src/link/link.stories.ts index 5c7b11fe352..68567e2f755 100644 --- a/libs/components/src/link/link.stories.ts +++ b/libs/components/src/link/link.stories.ts @@ -1,10 +1,15 @@ -import { Meta, Story } from "@storybook/angular"; +import { Meta, moduleMetadata, Story } from "@storybook/angular"; -import { LinkDirective } from "./link.directive"; +import { AnchorLinkDirective, ButtonLinkDirective } from "./link.directive"; +import { LinkModule } from "./link.module"; export default { title: "Component Library/Link", - component: LinkDirective, + decorators: [ + moduleMetadata({ + imports: [LinkModule], + }), + ], argTypes: { linkType: { options: ["primary", "secondary", "contrast"], @@ -19,25 +24,33 @@ export default { }, } as Meta; -const ButtonTemplate: Story = (args: LinkDirective) => ({ +const ButtonTemplate: Story = (args: ButtonLinkDirective) => ({ props: args, template: `
- - - - +
+ +
+
+ +
+
+ +
+
+ +
`, }); -const AnchorTemplate: Story = (args: LinkDirective) => ({ +const AnchorTemplate: Story = (args: AnchorLinkDirective) => ({ props: args, template: `
@@ -73,6 +86,20 @@ Anchors.args = { linkType: "primary", }; +const InlineTemplate: Story = (args) => ({ + props: args, + template: ` + + On the internet pargraphs often contain inline links, but few know that can be used for similar purposes. + + `, +}); + +export const Inline = InlineTemplate.bind({}); +Inline.args = { + linkType: "primary", +}; + const DisabledTemplate: Story = (args) => ({ props: args, template: `