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

IconButtonLink: support active state #3809

Merged
merged 6 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
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
22 changes: 22 additions & 0 deletions docs/examples/iconbuttonlink/active.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Flex, IconButtonLink } from 'gestalt';

export default function Example() {
const reactRouterPath = '/iconbuttonlink';

return (
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<IconButtonLink
accessibilityLabel="Visit the Gestalt documentation"
active={reactRouterPath === '/iconbuttonlink' ? 'page' : undefined}
href="https://gestalt.pinterest.systems/web/iconbuttonlink#Active-item"
icon="visit"
onClick={({ event, dangerouslyDisableOnNavigation }) => {
event.preventDefault();
dangerouslyDisableOnNavigation();
}}
target="blank"
/>
</Flex>
);
}
34 changes: 34 additions & 0 deletions docs/examples/iconbuttonlink/disabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Flex, IconButtonLink } from 'gestalt';

export default function Example() {
const reactRouterPath = '/iconbuttonlink';

return (
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<IconButtonLink
accessibilityLabel="Visit the Gestalt documentation"
disabled
href="https://gestalt.pinterest.systems/web/iconbuttonlink#Active-item"
icon="visit"
onClick={({ event, dangerouslyDisableOnNavigation }) => {
event.preventDefault();
dangerouslyDisableOnNavigation();
}}
target="blank"
/>
<IconButtonLink
accessibilityLabel="Visit the Gestalt documentation"
active={reactRouterPath === '/iconbuttonlink' ? 'page' : undefined}
disabled
href="https://gestalt.pinterest.systems/web/iconbuttonlink#Active-item"
icon="visit"
onClick={({ event, dangerouslyDisableOnNavigation }) => {
event.preventDefault();
dangerouslyDisableOnNavigation();
}}
target="blank"
/>
</Flex>
);
}
19 changes: 19 additions & 0 deletions docs/examples/iconbuttonlink/enabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { Flex, IconButtonLink } from 'gestalt';

export default function Example() {
return (
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<IconButtonLink
accessibilityLabel="Visit the Gestalt documentation"
href="https://gestalt.pinterest.systems/web/iconbuttonlink#Active-item"
icon="visit"
onClick={({ event, dangerouslyDisableOnNavigation }) => {
event.preventDefault();
dangerouslyDisableOnNavigation();
}}
target="blank"
/>
</Flex>
);
}
4 changes: 2 additions & 2 deletions docs/examples/iconbuttonlink/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export default function Example() {
return (
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<IconButtonLink
accessibilityLabel=""
href="#"
accessibilityLabel="Visit the Gestalt documentation"
href="https://gestalt.pinterest.systems"
icon="visit"
onClick={({ event, dangerouslyDisableOnNavigation }) => {
event.preventDefault();
Expand Down
22 changes: 11 additions & 11 deletions docs/pages/web/iconbutton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import defaultStateExample from 'docs/examples/iconbutton/defaultStateExample';
import disabledStateExample from 'docs/examples/iconbutton/disabledStateExample';
import disabledStateExample from 'docs/examples/iconbutton/disabled';
import defaultStateExample from 'docs/examples/iconbutton/enabled';
import focusOnDarkBackground from 'docs/examples/iconbutton/focusOnDarkBackground';
import selectedStateExample from 'docs/examples/iconbutton/selectedStateExample';
import { IconButton, Pog } from 'gestalt';
Expand Down Expand Up @@ -160,6 +160,15 @@ If IconButton is disabled, it's also unreachable from keyboard navigation.`}
}
/>
</MainSection.Subsection>
<MainSection.Subsection
description={`IconButton has a \`selected\` state to visually indicate that the element is selected, open, and/or active. If the selected state controls the display of a Popover-based component (open/closed), use \`accessibilityExpanded\` to inform screen reader users. See the [Accessibility](#Keyboard-interaction) guidelines to learn more.`}
title="Selected state"
>
<MainSection.Card
cardSize="lg"
sandpackExample={<SandpackExample code={selectedState} name="Selected state example" />}
/>
</MainSection.Subsection>
</AccessibilitySection>
<LocalizationSection name={generatedDocGen?.displayName} noDefaultLabelProvider />
<MainSection name="Variants">
Expand Down Expand Up @@ -330,15 +339,6 @@ Follow these guidelines for \`bgColor\`
title="Custom SVG icon"
/>
</MainSection.Subsection>
<MainSection.Subsection
description={`IconButton has a \`selected\` state to visually indicate that the element is selected, open, and/or active. If the selected state controls the display of a Popover-based component (open/closed), use \`accessibilityExpanded\` to inform screen reader users. See the [Accessibility](#Keyboard-interaction) guidelines to learn more.`}
title="Selected state"
>
<MainSection.Card
cardSize="lg"
sandpackExample={<SandpackExample code={selectedState} name="Selected state example" />}
/>
</MainSection.Subsection>
</MainSection>
<MainSection
description="When pairing IconButton with [Tooltip](/web/tooltip), refer to the Tooltip component for writing guidelines.
Expand Down
46 changes: 37 additions & 9 deletions docs/pages/web/iconbuttonlink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import focusOnDarkBackground from 'docs/examples/iconbuttonlink/focusOnDarkBackground';
import { BannerSlim, Icon } from 'gestalt';
import AccessibilitySection from '../../docs-components/AccessibilitySection';
import docGen, { DocGen, DocType, overrideTypes } from '../../docs-components/docgen';
import GeneratedPropTable from '../../docs-components/GeneratedPropTable';
import InternalDocumentationSection from '../../docs-components/InternalDocumentationSection';
Expand All @@ -9,6 +10,9 @@ import Page from '../../docs-components/Page';
import PageHeader from '../../docs-components/PageHeader';
import QualityChecklist from '../../docs-components/QualityChecklist';
import SandpackExample from '../../docs-components/SandpackExample';
import active from '../../examples/iconbuttonlink/active';
import disabled from '../../examples/iconbuttonlink/disabled';
import enabled from '../../examples/iconbuttonlink/enabled';
import localizationLabels from '../../examples/iconbuttonlink/localizationLabels';
import main from '../../examples/iconbuttonlink/main';

Expand All @@ -35,30 +39,43 @@ export default function DocsPage({ generatedDocGen }: DocType) {
>
<SandpackExample code={main} hideEditor name="Main IconButtonLink example" />
</PageHeader>

<GeneratedPropTable generatedDocGen={generatedDocGen} />

<MainSection
description="See [IconButton](/web/iconbutton) for usage guidelines."
name="Usage guidelines"
/>

<AccessibilitySection name={generatedDocGen?.displayName}>
<MainSection.Subsection
description={`IconButtonLink has an "active" state that visually identifies it. To set them to "active" set 'active="page"' (page redirect) or 'active="section"'. Use routing hooks from React.Router or other frameworks to identify the current route. For example, if the current pathname matches the IconButtonLink href, set IconButtonLink to "page". Use the example below as a reference.`}
title="Active state"
>
<MainSection.Card sandpackExample={<SandpackExample code={active} name="Active" />} />
</MainSection.Subsection>
</AccessibilitySection>

<LocalizationSection code={localizationLabels} name={generatedDocGen?.displayName} />

<MainSection
description="See [IconButton](/web/iconbutton) for more variants."
name="Variants"
>
<MainSection.Subsection
description={`IconButtonLink consumes external handlers from [GlobalEventsHandlerProvider](/web/utilities/globaleventshandlerprovider).
description={`IconButton's appearance can be modified by the following states:

Handlers:

- [onNavigation](/web/utilities/globaleventshandlerprovider#onNavigation:-custom-navigation): executed when IconButtonLink is clicked

See [GlobalEventsHandlerProvider](/web/utilities/globaleventshandlerprovider#onNavigation:-custom-navigation) for more information.
1. \`enabled\`
2. \`active\`
3. \`disabled\`
Disabled state, used to indicate that the button is not currently available for interaction.
`}
title="External handlers"
/>

title="States"
>
<SandpackExample code={enabled} name="Enabled state example" />
<SandpackExample code={active} name="Selected state example" />
<SandpackExample code={disabled} name="Disabled state example" />
</MainSection.Subsection>
<MainSection.Subsection title="Focus ring on dark backgrounds">
<MainSection.Card
cardSize="lg"
Expand All @@ -71,6 +88,17 @@ See [GlobalEventsHandlerProvider](/web/utilities/globaleventshandlerprovider#onN
}
/>
</MainSection.Subsection>
<MainSection.Subsection
description={`IconButtonLink consumes external handlers from [GlobalEventsHandlerProvider](/web/utilities/globaleventshandlerprovider).

Handlers:

- [onNavigation](/web/utilities/globaleventshandlerprovider#onNavigation:-custom-navigation): executed when IconButtonLink is clicked

See [GlobalEventsHandlerProvider](/web/utilities/globaleventshandlerprovider#onNavigation:-custom-navigation) for more information.
`}
title="External handlers"
/>
</MainSection>

<QualityChecklist component={generatedDocGen?.displayName} />
Expand Down
7 changes: 7 additions & 0 deletions packages/gestalt/src/IconButtonLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type Props = {
* Label for screen readers to announce IconButtonLink.
*/
accessibilityLabel: string;
/**
* When set to 'page' or 'section', it displays the item in "active" state. See the [active state](https://gestalt.pinterest.systems/web/iconbuttonlink#Active-state) guidelines to learn more.
*/
active?: 'page' | 'section';
/**
* Primary colors to apply to the IconButtonLink background.
*/
Expand Down Expand Up @@ -101,6 +105,7 @@ type Props = {

const IconButtonLinkWithForwardRef = forwardRef<HTMLAnchorElement, Props>(function IconButtonLink(
{
active,
href,
rel,
target,
Expand Down Expand Up @@ -148,6 +153,7 @@ const IconButtonLinkWithForwardRef = forwardRef<HTMLAnchorElement, Props>(functi
const buttonComponent = (
<InternalLink
ref={innerRef}
accessibilityCurrent={active}
accessibilityLabel={getAriaLabel({
target,
accessibilityLabel,
Expand Down Expand Up @@ -183,6 +189,7 @@ const IconButtonLinkWithForwardRef = forwardRef<HTMLAnchorElement, Props>(functi
icon={icon}
iconColor={iconColor}
padding={padding}
selected={active && ['page', 'section'].includes(active)}
size={size}
/>
<NewTabAccessibilityLabel target={target} />
Expand Down
12 changes: 10 additions & 2 deletions packages/gestalt/src/Link/InternalLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,17 @@ const InternalLinkWithForwardRef = forwardRef<HTMLAnchorElement, Props>(function
// @ts-expect-error TS2322 Types of property '"aria-selected"' are incompatible. Type '"section" | undefined' is not assignable to type 'Booleanish | undefined'
<a
ref={innerRef}
aria-current={accessibilityCurrent !== 'section' ? accessibilityCurrent : undefined}
aria-current={
accessibilityCurrent && accessibilityCurrent !== 'section'
? accessibilityCurrent
: undefined
}
aria-label={accessibilityLabel}
aria-selected={accessibilityCurrent === 'section' ? accessibilityCurrent : undefined}
aria-selected={
accessibilityCurrent && accessibilityCurrent === 'section'
? accessibilityCurrent
: undefined
}
className={isSearchGuide ? searchGuideClassNames : className}
data-test-id={dataTestId}
href={disabled ? undefined : href}
Expand Down
2 changes: 1 addition & 1 deletion packages/gestalt/src/SideNavigationTopItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const SideNavigationTopItemWithForwardRef = forwardRef<HTMLLIElement, Props>(
return (
<li ref={ref} className={classnames(styles.liItem)}>
<TapAreaLink
accessibilityCurrent={active === 'page' ? active : undefined}
accessibilityCurrent={active}
href={href}
onBlur={() => setFocused(false)}
onFocus={() => setFocused(true)}
Expand Down
Loading