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

fix: display beta tag with css #14478

Merged
merged 9 commits into from
Jan 27, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,3 @@
.active {
border-bottom: 2px solid var(--fds-semantic-surface-neutral-default);
}

.betaTag {
min-height: min-content;
padding: 0 var(--fds-spacing-1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const HeaderButtonListItem = ({ menuItem }: HeaderButtonListItemProps): ReactEle
<StudioPageHeader.HeaderLink
color='dark'
variant={variant}
isBeta={menuItem.isBeta}
renderLink={(props) => (
<NavLink to={menuItem.link} {...props}>
<span
Expand All @@ -45,7 +46,7 @@ const HeaderButtonListItem = ({ menuItem }: HeaderButtonListItemProps): ReactEle
>
{menuItem.name}
</span>
{menuItem.isBeta && <StudioBetaTag className={classes.betaTag} />}
{menuItem.isBeta && <StudioBetaTag />}
</NavLink>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,8 @@ type Story = StoryFn<typeof StudioBetaTag>;
const meta: Meta = {
title: 'Components/StudioBetaTag',
component: StudioBetaTag,
argTypes: {
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
},
};

export const Preview: Story = (args): React.ReactElement => <StudioBetaTag {...args} />;

Preview.args = {
size: 'sm',
};
export const Preview: Story = (): React.ReactElement => <StudioBetaTag />;

export default meta;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import type { RenderResult } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { StudioBetaTag, type StudioBetaTagProps } from './StudioBetaTag';
import { defaultAriaLabel, StudioBetaTag, StudioBetaTagProps } from './StudioBetaTag';

Check failure on line 4 in frontend/libs/studio-components/src/components/StudioBetaTag/StudioBetaTag.test.tsx

View workflow job for this annotation

GitHub Actions / Typechecking and linting

Imports "StudioBetaTagProps" are only used as type
standeren marked this conversation as resolved.
Show resolved Hide resolved
import { testRootClassNameAppending } from '../../test-utils/testRootClassNameAppending';

describe('StudioBetaTag', () => {
Expand All @@ -9,11 +10,27 @@
expect(screen.getByText('Beta')).toBeInTheDocument();
});

it('should render with isBeta className', () => {
renderStudioBetaTag();
expect(screen.getByText('Beta')).toHaveClass('isBeta');
});

it('should render with a default aria label', () => {
renderStudioBetaTag();
expect(screen.getByText('Beta')).toHaveAttribute('aria-label', defaultAriaLabel);
});

it('should render with a custom aria label', () => {
const customAriaLabel = 'customAriaLabel';
renderStudioBetaTag({ 'aria-label': customAriaLabel });
expect(screen.getByText('Beta')).toHaveAttribute('aria-label', customAriaLabel);
});

it('Appends given classname to the component', () => {
testRootClassNameAppending((className) => renderStudioBetaTag({ className }));
});
});

const renderStudioBetaTag = (props: Partial<StudioBetaTagProps> = {}) => {
const renderStudioBetaTag = (props: Partial<StudioBetaTagProps> = {}): RenderResult => {
return render(<StudioBetaTag {...props} />);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import React from 'react';
import { StudioTag, type StudioTagProps } from '../StudioTag';
import commonClasses from '../StudioPageHeader/common.module.css';
import cn from 'classnames';

export type StudioBetaTagProps = Omit<StudioTagProps, 'color' | 'children'>;
export type StudioBetaTagProps = {
className?: string;
'aria-label'?: string;
};

export const defaultAriaLabel = 'Beta feature';

export const StudioBetaTag = ({ size = 'sm', ...rest }: StudioBetaTagProps) => {
export const StudioBetaTag = ({
className: givenClass,
'aria-label': ariaLabel = defaultAriaLabel,
}: StudioBetaTagProps): React.ReactElement => {
const className = cn(commonClasses['isBeta'], givenClass);
return (
<StudioTag color='info' size={size} {...rest}>
<span className={className} aria-label={ariaLabel}>
Beta
</StudioTag>
</span>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import type { RenderResult } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import type { StudioPageHeaderHeaderLinkProps } from './StudioPageHeaderHeaderLink';
import { StudioPageHeaderHeaderLink } from './StudioPageHeaderHeaderLink';
import { defaultAriaDescription, StudioPageHeaderHeaderLink } from './StudioPageHeaderHeaderLink';
Fixed Show fixed Hide fixed
standeren marked this conversation as resolved.
Show resolved Hide resolved

// Test data:
const linkText: string = 'Text';
Expand All @@ -24,6 +24,28 @@ describe('StudioPageHeaderHeaderLink', () => {
expect(screen.getByRole('link', { name: linkText })).toBeInTheDocument();
});

it('Renders the link with betaContainer when isBeta is true', () => {
renderStudioPageHeaderHeaderLink({ isBeta: true });
expect(screen.getByRole('link', { name: linkText })).toHaveClass('betaContainer');
});

it('Renders with default aria-description by default when isBeta is true', () => {
renderStudioPageHeaderHeaderLink({ isBeta: true });
expect(screen.getByRole('link', { name: linkText })).toHaveAttribute(
'aria-description',
defaultAriaDescription,
);
});

it('Renders with custom aria-description when provided', () => {
const customAriaDescription = 'customAriaDescription';
renderStudioPageHeaderHeaderLink({ isBeta: true, 'aria-description': customAriaDescription });
expect(screen.getByRole('link', { name: linkText })).toHaveAttribute(
'aria-description',
customAriaDescription,
);
});

it('Passes the colour and variant classes to the link', () => {
renderStudioPageHeaderHeaderLink();
const link = screen.getByRole('link');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,32 @@ export type StudioPageHeaderHeaderLinkProps = {
color: StudioPageHeaderColor;
variant: StudioPageHeaderVariant;
renderLink: (props: HTMLAttributes<HTMLAnchorElement>) => ReactElement;
isBeta?: boolean;
'aria-description'?: string;
} & HTMLAttributes<HTMLAnchorElement>;

export const defaultAriaDescription = 'This feature is in beta';

export function StudioPageHeaderHeaderLink({
color,
variant,
className: givenClass,
renderLink,
isBeta,
'aria-description': ariaDescription = defaultAriaDescription,
}: StudioPageHeaderHeaderLinkProps): ReactElement {
const className = cn(
commonClasses.linkOrButton,
commonClasses[variant],
commonClasses[color],
isBeta && commonClasses['betaContainer'],
givenClass,
linkClasses.link,
);
const props: HTMLAttributes<HTMLAnchorElement> = { className };
const props: HTMLAttributes<HTMLAnchorElement> = {
className,
'aria-description': isBeta ? ariaDescription : undefined,
};
return renderLink(props);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,17 @@
.linkOrButton.preview.light:hover {
background-color: var(--studio-button-preview-light);
}

.betaContainer {
display: flex;
align-items: center;
gap: var(--fds-spacing-1);
}

.isBeta {
background-color: var(--fds-semantic-surface-info-subtle);
color: var(--fds-semantic-text-neutral-default);
border-radius: var(--fds-border_radius-large);
padding: var(--fds-spacing-1);
font-size: var(--fds-sizing-3);
}
Loading