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

feat(IconButton): aria-describedby override #2894

Merged
merged 11 commits into from
Jun 26, 2024
5 changes: 4 additions & 1 deletion packages/gamut/src/Button/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export type IconButtonProps = ComponentProps<typeof IconButtonBase> &
IconComponentType & {
'aria-label'?: string;
tip: string;
tipProps?: Omit<ToolTipProps, 'info' | 'id' | 'children' | 'hasLabel'>;
tipProps?: Omit<
ToolTipProps,
'info' | 'id' | 'children' | 'hasRepetitiveLabel'
>;
};

export const IconButton = forwardRef<ButtonBaseElements, IconButtonProps>(
Expand Down
1 change: 0 additions & 1 deletion packages/gamut/src/Button/__tests__/IconButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { setupRtl } from 'component-test-setup';
import { IconButton } from '../IconButton';
import { IconButtonFloatingMock } from './mocks';


const label = 'Click';
const tip = 'Click this button';
const tipText = 'this button';
Expand Down
10 changes: 9 additions & 1 deletion packages/gamut/src/Tip/ToolTip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export type ToolTipProps = TipBaseProps &
* If your button has a label that is repeated in the first word of the tooltip, you can set this to `true` to avoid repetition. If your info tip is not a string, you cannot do this.
*/
hasRepetitiveLabel?: boolean;
/**
* If you would like to forgo the aria-describedby attribute set this to `true`. When using this prop, the `aria-label` should always be identical to the `tip`.
*/
hideAriaToolTip?: boolean;
};

export const ToolTip: React.FC<ToolTipProps> = ({
Expand All @@ -30,6 +34,7 @@ export const ToolTip: React.FC<ToolTipProps> = ({
placement = tipDefaultProps.placement,
id,
hasRepetitiveLabel,
hideAriaToolTip,
...rest
}) => {
const wrapperRef = useRef<HTMLDivElement>(null);
Expand All @@ -41,6 +46,7 @@ export const ToolTip: React.FC<ToolTipProps> = ({

const isFloating = placement === 'floating';
const Tip = loaded && isFloating ? FloatingTip : InlineTip;

const adjustedInfo = useMemo(() => {
return hasRepetitiveLabel && typeof info === 'string'
? info.split(' ').slice(1).join(' ')
Expand All @@ -49,7 +55,9 @@ export const ToolTip: React.FC<ToolTipProps> = ({

// this should only happen if the button has an aria-label that is the same is and ONLY the content of the tooltip

const shouldRenderAriaTip = adjustedInfo !== '';
const shouldRenderAriaTip = useMemo(() => {
return hideAriaToolTip ? false : adjustedInfo !== '';
}, [hideAriaToolTip, adjustedInfo]);

const tipProps = {
alignment,
Expand Down
12 changes: 12 additions & 0 deletions packages/gamut/src/Tip/__tests__/ToolTip.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,25 @@ describe('ToolTip', () => {
view.getByRole('button', { name: 'Click' });
expect(view.queryByRole('tooltip')).toBeNull();
});

it('calls onClick when clicked', () => {
const { view } = renderView({});

userEvent.click(view.getByRole('button'));

expect(onClick).toHaveBeenCalled();
});

it('hides ariaTooltip when there is hideAriaToolTip is true', () => {
const { view } = renderView({
'aria-label': ariaLabel,
hideAriaToolTip: true,
info: `${ariaLabel}`,
});

view.getByRole('button', { name: 'Click' });
expect(view.queryByRole('tooltip')).toBeNull();
});
});
});
describe('floating placement', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/gamut/src/Tip/__tests__/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ export const ToolTipMock: React.FC<
placement,
onClick,
hasRepetitiveLabel,
hideAriaToolTip,
}) => {
return (
<ToolTip
info={info}
placement={placement}
id="tip-id"
hasRepetitiveLabel={hasRepetitiveLabel}
hideAriaToolTip={hideAriaToolTip}
>
<FillButton
aria-label={ariaLabel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Writing words for a link or button? Above all else, make sure that the words alo

## Icon Button

You must pass a string for the `tip` prop to label your icon button. The `tipProps` prop is optional and can be used to customize the tooltip.
You must pass a string for the `tip` prop to label your icon button. The `tipProps` prop is optional and can be used to customize the tooltip - for more information on `tooltip`s + `IconButton`s see the <LinkTo component="ToolTip">ToolTip page</LinkTo>.

<Canvas>
<Story name="IconButton">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Links or buttons within InfoTips should be used sparingly and only when the info

## InfoTips and zIndex

Links or buttons within InfoTips should be used sparingly and only when the information is critical to the user's understanding of the content. If an infotip _absolutely requires_ a link or button, it needs to provide a programmatic focus by way of the `onClick` prop. The `onClick` prop accepts a function that calls the object `{isTipHidden}` and should focus when the tip is visible.
You can change the zIndex of your `InfoTip` with the zIndex property.

<Canvas>
<Story name="z-index">
Expand Down
12 changes: 11 additions & 1 deletion packages/styleguide/stories/Molecules/Tip/ToolTip.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ToolTip,
} from '@codecademy/gamut/src';
import {
ArrowRightIcon,
SmileyStarEyesIcon,
SparkleIcon,
ThumbsDownIcon,
Expand Down Expand Up @@ -62,15 +63,24 @@ When a Button is disabled with a tooltip, you must use the `aria-disabled` prop

When using an icon-only button, a tooltip displaying the label is required to provide context for the button's action. This is accessible directly through the `IconButton`'s required `tip` prop. You can adjust positioning and alignment of the tooltip by using the tipProps prop. An additional label that is different from the tip should also be provided.

If your `IconButton` doesn't require `aria-describedby` text, you can set the `hideAriaToolTip` tipProp to `true` to hide the `aria-describedby` text. When using the `hideAriaToolTip` prop, the `aria-label` should always be identical to the `tip` prop.

<Canvas>
<Story name="InfoButton Tooltip">
{() => (
<FlexBox center py={64} m={24}>
<FlexBox justifyContent="space-around" m={24} width="95%">
<IconButton
tip="Beautify your code"
icon={SparkleIcon}
tipProps={{ alignment: 'bottom-center' }}
/>
<IconButton
aria-label="Next Prompt"
tip="Next Prompt"
icon={ArrowRightIcon}
variant="secondary"
tipProps={{ alignment: 'bottom-center', hideAriaToolTip: true }}
/>
</FlexBox>
)}
</Story>
Expand Down
Loading