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

HelpButton: VR adjusments #3899

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 49 additions & 44 deletions packages/gestalt/src/HelpButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComponentProps, ReactElement, Ref, useId, useRef, useState } from 'react';
import { ComponentProps, ReactElement, useId, useRef, useState } from 'react';
import Box from './Box';
import { useColorScheme } from './contexts/ColorSchemeProvider';
import { useDefaultLabelContext } from './contexts/DefaultLabelProvider';
Expand All @@ -12,31 +12,25 @@ import InternalPopover from './Popover/InternalPopover';
import TapArea from './TapArea';
import Text from './Text';
import Tooltip from './Tooltip';
import useInExperiment from './useInExperiment';
import { CompositeZIndex, FixedZIndex, Indexable } from './zIndex';

type LinkType = {
accessibilityLabel?: string;
accessibilityLabel?: ComponentProps<typeof Link>['accessibilityLabel'];
externalLinkIcon?:
| 'none'
| 'default'
| {
color: ComponentProps<typeof Icon>['color'];
size: ComponentProps<typeof Text>['size'];
};
href: string;
onClick?: (arg1: {
event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
dangerouslyDisableOnNavigation: () => void;
}) => void;
ref?: Ref<'a'>;
href: ComponentProps<typeof Link>['href'];
onClick?: ComponentProps<typeof Link>['onClick'];
ref?: ComponentProps<typeof Link>['ref'];
text: string;
target?: null | 'self' | 'blank';
target?: ComponentProps<typeof Link>['target'];
};

type OnTapType = (arg1: {
event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
}) => void;

type Props = {
/**
* Supply a short, descriptive label screen readers. Follow the pattern `Click to learn more about [description]`. See [Accessibility](https://gestalt.pinterest.systems/web/helpbutton#Accessibility) section for guidance.
Expand Down Expand Up @@ -66,7 +60,9 @@ type Props = {
/**
* Callback fired when HelpIcon is clicked (pressed and released) with a mouse or keyboard.
*/
onClick?: OnTapType;
onClick?: (arg1: {
event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
}) => void;
/**
* Informational content that's displayed when the user clicks on HelpButton.
*/
Expand All @@ -90,18 +86,25 @@ export default function HelpButton({
text,
zIndex,
}: Props) {
const tapAreaRef = useRef<null | HTMLAnchorElement | HTMLDivElement>(null);
const tapAreaRef = useRef<null | HTMLDivElement>(null);
const textRef = useRef<null | HTMLElement>(null);

const [open, setOpen] = useState(false);
const [hovered, setHovered] = useState(false);
const [focused, setFocused] = useState(false);

// Define where the focused content stays
const [innerModalFocus, setInnerModalFocus] = useState(false);

const isInVRExperiment = useInExperiment({
webExperimentName: 'web_gestalt_visualRefresh',
mwebExperimentName: 'web_gestalt_visualRefresh',
});
const { colorSchemeName } = useColorScheme();
const popoverId = useId();
const { tooltipMessage } = useDefaultLabelContext('HelpButton');

const handlePopoverKeyDown = ({ event }: { event: React.KeyboardEvent<HTMLElement> }) => {
const handlePopoverKeyDown: ComponentProps<typeof InternalPopover>['onKeyDown'] = ({ event }) => {
// Avoid others KeyDown events to listen this call
if (innerModalFocus) event.stopPropagation();

Expand All @@ -114,6 +117,7 @@ export default function HelpButton({
`a[href="${link?.href ?? ''}"]`, // Link query
].join(' ,'),
);

const lastElement = elementsInnerPopover[elementsInnerPopover.length - 1];
if (innerModalFocus && event.keyCode === TAB && lastElement === event.target) {
event.preventDefault();
Expand All @@ -127,7 +131,7 @@ export default function HelpButton({
}
};

const handleTapAreaKeyDown = ({ event }: { event: React.KeyboardEvent<HTMLDivElement> }) => {
const handleTapAreaKeyDown: ComponentProps<typeof TapArea>['onKeyDown'] = ({ event }) => {
if (event.keyCode === TAB && open) {
event.preventDefault();
textRef.current?.focus();
Expand All @@ -137,38 +141,28 @@ export default function HelpButton({
event.stopPropagation();
};

const toggleView = () => {
setOpen((currVal) => !currVal);
};
const toggleView = () => setOpen((currVal) => !currVal);

const onHandleTap = (
...args: ReadonlyArray<{
event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
}>
) => {
toggleView();
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
onClick?.(...args);
};
let bgIconColor: ComponentProps<typeof Box>['color'] = isInVRExperiment ? 'default' : 'tertiary';
let iconColor: ComponentProps<typeof Icon>['color'] = isInVRExperiment ? 'subtle' : 'inverse';

const bgIconColor = open || hovered || focused ? 'selected' : 'tertiary';
if (!isInVRExperiment && (open || hovered || focused)) {
bgIconColor = 'selected';
}

const tooltipZIndex = zIndex ?? new FixedZIndex(1);

const zIndexWrapper = new CompositeZIndex([tooltipZIndex]);
if (isInVRExperiment && (open || hovered || focused)) {
iconColor = 'default';
}

// Overriding color of `Text` components
const isDarkMode = colorSchemeName === 'darkMode';
const textColorOverrideStyles = isDarkMode
? styles.textColorOverrideLight
: styles.textColorOverrideDark;

const textElement =
typeof text === 'string' ? (
<Text align="start">{text}</Text>
) : (
<span className={textColorOverrideStyles}>{text}</span>
);
const tooltipZIndex = zIndex ?? new FixedZIndex(1);

const zIndexWrapper = new CompositeZIndex([tooltipZIndex]);

const popoverElement = (
<InternalPopover
Expand All @@ -192,13 +186,16 @@ export default function HelpButton({
* `tabIndex={0}` - It's used to make the text element as a focusable element
*/}
<Box ref={textRef} id={`helpButtonText-${popoverId}`} tabIndex={0}>
{textElement}
{typeof text === 'string' ? (
<Text align="start">{text}</Text>
) : (
<span className={textColorOverrideStyles}>{text}</span>
)}
</Box>
{link && link?.href && (
<Box display="block" marginTop={3} width="100%">
<Text>
<Link
// @ts-expect-error - TS2322 - Type 'Ref<"a"> | undefined' is not assignable to type 'LegacyRef<HTMLAnchorElement> | undefined'.
ref={link.ref}
accessibilityLabel={link.accessibilityLabel}
externalLinkIcon={link.externalLinkIcon}
Expand Down Expand Up @@ -228,18 +225,21 @@ export default function HelpButton({
zIndex={tooltipZIndex}
>
<TapArea
// @ts-expect-error - TS2322 - Type 'MutableRefObject<HTMLDivElement | HTMLAnchorElement | null>' is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'.
ref={tapAreaRef}
accessibilityControls={popoverId}
accessibilityExpanded={open}
accessibilityLabel={accessibilityLabel}
fullWidth={false}
innerFocusColor={isInVRExperiment ? 'default' : undefined}
onBlur={() => setFocused(false)}
onFocus={() => setFocused(true)}
onKeyDown={handleTapAreaKeyDown}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onTap={onHandleTap}
onTap={({ event }) => {
toggleView();
onClick?.({ event });
}}
role="button"
rounding="circle"
>
Expand All @@ -252,7 +252,12 @@ export default function HelpButton({
rounding="circle"
width={16}
>
<Icon accessibilityLabel="" color="inverse" icon="question-mark" size={8} />
<Icon
accessibilityLabel=""
color={iconColor}
icon="question-mark"
size={isInVRExperiment ? 16 : 8}
/>
</Box>
</TapArea>
</Tooltip>
Expand Down
Loading