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

Link Component #5502

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open

Link Component #5502

wants to merge 31 commits into from

Conversation

Zystix
Copy link
Contributor

@Zystix Zystix commented Feb 4, 2025

Why

Creating a new Link component.

https://cultureamp.atlassian.net/browse/KZN-2593

What

Adds the Link component, storybook tests, sticker sheets, API spec and usage guidelines.

Copy link

changeset-bot bot commented Feb 4, 2025

🦋 Changeset detected

Latest commit: 2bd25f2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@kaizen/components Minor
@docs/storybook Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Zystix Zystix marked this pull request as ready for review February 4, 2025 22:52
@Zystix Zystix requested a review from a team as a code owner February 4, 2025 22:52
Copy link
Contributor

github-actions bot commented Feb 4, 2025

✨ Here is your branch preview! ✨

Last updated for commit 2bd25f2: Removed useReversedColors

@Zystix Zystix marked this pull request as draft February 4, 2025 23:12
@Zystix Zystix marked this pull request as ready for review February 5, 2025 23:36
return (
<FrontendServices {...config}>
{/* application code */}
<RacRouterProvider navigate={(href, opts) => router.push(href, undefined, opts)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this provider part of next-services so it works for the consuming teams out of the box and without extra config?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jakubriedl did you mean before this release goes out?

I certainly think that makes sense as a future / subsequent goal, but don't think it should block this specific release since the component is agnostic to the routing solution and standard hrefs will work out of the box.

Potentially a good opportunity for @Zystix once this is done (noting we only have him till the end of Feb), but will also raise with the team to see when we can prioritize the work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah totally agree this shouldn't be blocker. But I'd add it there at the same time as it's just small change and write the documentation in the expects it to be there. It'll save you a lot of explaining to teams what they have to do, just say in the docs "have up-to-date next-services" and you can skip this whole section. Easier for you, and for the consumer as well

packages/components/src/Link/Link.module.css Outdated Show resolved Hide resolved
packages/components/src/Link/Link.module.css Show resolved Hide resolved
packages/components/src/Link/Link.module.css Show resolved Hide resolved
packages/components/src/Link/Link.module.css Show resolved Hide resolved
--text-icon-gap: var(--spacing-2);
}

[class^='small'] .isInline > *,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for the attribute selector use here? Can it just be .small

Copy link
Contributor Author

@Zystix Zystix Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the hashing of classes it can't "see" the .small class of the component its wrapped in when inline. It does feel like a cable ties solution though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The css module will apply the correct hash as part of the postcss plugin so you can just reference it as a normal class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't appear to work when we tried it? Does it look for the hash for link.module.css file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note is that the .[size] .isInline > * selector is never applied due to your condition of not applying the styles when isInline is false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the desired functionality, this is how we're achieving grabbing the parent Text's size class when the Link isInline. Is there anything you'd change here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah for all the selectors there you can drop the comma separated class e.g.

[class*='extra-small'] .isInline > *,
.extra-small <-- don't need this 

Just do

[class*='extra-small'] .isInline > * {
  ...
}

Copy link
Contributor

@ryanseddon ryanseddon Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll also add that the class^='small' will never actually match (at least not in storybook) as the class begins with a _ so it never finds a class starting with small

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a bit of code smell relying on a parent component class to make changes, can we do this within code and know the size being passed down?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can inherit the text size, the issue I think lies more in the icon sizing and spacing between the text and icon. We initially thought we might be able to leverage em to calculate a ratio based on the size but map to a cleanly between each font size.

Or were you thinking more like JS to look at the parent node and get the computed style to determine a size based off that?

packages/components/src/Link/_docs/Link.doc.stories.tsx Outdated Show resolved Hide resolved
className={mergeClassNames(
styles.link,
isDisabled && styles.isDisabled,
!isInline && styles[size],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the condition I'm referring to, your class selector is never applied due to the fact that the size classes aren't added when the component has isInline set to true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the desired functionality, we still allow the size to be passed cos we couldn't get the discriminated union to behave. Would be good to get that sorted though

@ryanseddon
Copy link
Contributor

ryanseddon commented Feb 10, 2025

Something else to consider (I don't know if both a relevant anymore) but you might want to look at seeing if adding rel="noreferrer" is something we want on any Link elements that go off to an external url.

Seems having target="_blank" now automatically does the rel="noopener" behaviour.

https://developer.chrome.com/docs/lighthouse/best-practices/external-anchors-use-rel-noopener


export const LinkOpensInNewTab: Story = {
render: (props) => (
<Link {...props} target="_blank" icon={<ExternalLinkIcon role="presentation" />} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also recommend we use the end icon position with this one

@@ -0,0 +1,184 @@
import React from 'react'
import { type Meta, type StoryObj } from '@storybook/react'
import { ExternalLinkIcon, FlagOffIcon } from '~components/Icon'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to replace with these usages with the RC icon Icon component - this is why the external link icons look larger than they should be

--link-font-size: var(--typography-paragraph-extra-small-font-size);
--link-line-height: var(--typography-paragraph-extra-small-line-height);
--icon-font-size: 0.875rem;
--text-icon-gap: var(--spacing-2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work the way you expect it as the variable is defined within the selector range so the --text-icon-gap is always the fallback 0.5rem

Here you can see the variable is not defined in the scope of where it's used
Screenshot 2025-02-11 at 11 37 10 am

Here it's defined (it just happens to be the same value as the fallback)
Screenshot 2025-02-11 at 11 37 24 am

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact all of these variables do not have any effect as they are scoped to this selector and the variables are used up the selector scope

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-02-11 at 1 21 40 PM

It appears to work as intended in the stickersheet? Thats the body size

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, doesn't work when isInline, I'm pretty sure it used to so I might have broken something when cleaning up.

export const WithText: Story = {
render: (props) => (
<>
<Text variant="extra-small">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we hook up the storybook controls to this value so we can adjust it within the story and see how it changes? At the moment the controls don't do anything

Screenshot 2025-02-11 at 11 42 27 am


### Icons and positioning

The `icon` property abstracts the need to handle positioning and sizing of icons within the `Link` component. The `iconPosition` prop allows for the icon to be positioned to the left or right of the `children`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a JSX element, it might be worth calling out that the intended component to be used here is the new Icon component and link out the API specification for it :)

--text-icon-gap: var(--spacing-2);
}

[class^='small'] .isInline > *,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can inherit the text size, the issue I think lies more in the icon sizing and spacing between the text and icon. We initially thought we might be able to leverage em to calculate a ratio based on the size but map to a cleanly between each font size.

Or were you thinking more like JS to look at the parent node and get the computed style to determine a size based off that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants