Skip to content

Commit

Permalink
AnchorNav size and variant updates (#859)
Browse files Browse the repository at this point in the history
  • Loading branch information
rezrah authored Dec 12, 2024
1 parent 8732b70 commit cd18615
Show file tree
Hide file tree
Showing 22 changed files with 163 additions and 17 deletions.
8 changes: 8 additions & 0 deletions .changeset/brown-meals-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@primer/react-brand': patch
---

`AnchorNav` component updates:

- `AnchorNav.Action` and `AnchorNav.SecondaryAction` now appear visually smaller by default. Use `size="medium"` if the previous, larger buttons are needed.
- `AnchorNav.Action` and `AnchorNav.SecondaryAction` each support a `variant` prop, allowing primary `Button` visuals to be optionally applied.
4 changes: 2 additions & 2 deletions apps/docs/content/components/AnchorNav.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ Additional props can be passed to the `<a>` element. [See MDN for a list of prop
| `id` | `string` | | Sets a custom id |
| `ref` | `React.RefObject` | | Forward a Ref to the underlying DOM node |

Additional props can be passed to the `<a>` element. [See MDN for a list of props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes) accepted by the `<anchor>` element.
Additional props can be passed to the `<a>` element. Refer to [Button](./Button/react.mdx) for more details or [MDN for a list of props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes) accepted by the `<anchor>` element.

<h3 id="AnchorNav-secondary-action">AnchorNav.SecondaryAction</h3>

Expand All @@ -229,4 +229,4 @@ Additional props can be passed to the `<a>` element. [See MDN for a list of prop
| `id` | `string` | | Sets a custom id |
| `ref` | `React.RefObject` | | Forward a Ref to the underlying DOM node |

Additional props can be passed to the `<a>` element. [See MDN for a list of props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes) accepted by the `<anchor>` element.
Additional props can be passed to the `<a>` element. Refer to [Button](./Button/react.mdx) for more details or [MDN for a list of props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes) accepted by the `<anchor>` element.
91 changes: 91 additions & 0 deletions packages/react/src/AnchorNav/AnchorNav.features.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,94 @@ HideUntilSticky.args = {
hideUntilSticky: true,
}
HideUntilSticky.storyName = 'Hide until sticky'

export const AnchorNavPrimaryActions = () => {
const data = ['link1', 'link2', 'link3']
return (
<>
<AnchorNav>
{data.map((link, index) => (
<AnchorNav.Link href={link} key={index}>
{link}
</AnchorNav.Link>
))}
<AnchorNav.Action variant="primary" href="#">
Get started
</AnchorNav.Action>
</AnchorNav>
<Stack
direction="vertical"
justifyContent="space-around"
gap="none"
style={{marginBottom: '100px'}}
padding="none"
>
{data.map(key => (
<RedlineBackground key={key}>
<Stack
key={key}
id={key}
direction="vertical"
style={{
padding: `${1000 / 2}px var(--base-size-24)`,
}}
>
<Heading>First action as primary</Heading>
<Text as="p">
AnchorNav is a component that allows users to navigate to different sections of a page.
</Text>
</Stack>
</RedlineBackground>
))}
</Stack>
</>
)
}
AnchorNavPrimaryActions.storyName = 'With optional primary CTA'

export const AnchorNavLargerActions = () => {
const data = ['link1', 'link2', 'link3']
return (
<>
<AnchorNav>
{data.map((link, index) => (
<AnchorNav.Link href={link} key={index}>
{link}
</AnchorNav.Link>
))}
<AnchorNav.Action size="medium" href="#">
Primary action
</AnchorNav.Action>
<AnchorNav.SecondaryAction size="medium" href="#">
Secondary action
</AnchorNav.SecondaryAction>
</AnchorNav>
<Stack
direction="vertical"
justifyContent="space-around"
gap="none"
style={{marginBottom: '100px'}}
padding="none"
>
{data.map(key => (
<RedlineBackground key={key}>
<Stack
key={key}
id={key}
direction="vertical"
style={{
padding: `${1000 / 2}px var(--base-size-24)`,
}}
>
<Heading>First action as primary</Heading>
<Text as="p">
AnchorNav is a component that allows users to navigate to different sections of a page.
</Text>
</Stack>
</RedlineBackground>
))}
</Stack>
</>
)
}
AnchorNavLargerActions.storyName = 'With larger CTAs'
22 changes: 14 additions & 8 deletions packages/react/src/AnchorNav/AnchorNav.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@

/* Up to large breakpoint */
@media screen and (max-width: 63.25rem) {
.AnchorNav:not(.AnchorNav--expanded) .AnchorNav-inner-container {
display: flex;
align-items: center;
justify-content: space-between;
}

.AnchorNav {
background-color: var(--brand-color-canvas-default);
}
Expand All @@ -108,6 +114,14 @@
}
}

@media screen and (min-width: 40rem) and (max-width: 63.25rem) {
.AnchorNav__actionsContainer:not(.AnchorNav__actionsContainer--no-offset) {
position: absolute;
top: var(--base-size-8);
right: 0;
}
}

/* Large breakpoint and up */
@media screen and (min-width: 63.25rem) {
.AnchorNav {
Expand Down Expand Up @@ -391,14 +405,6 @@
}
}

@media screen and (min-width: 40rem) and (max-width: 63.25rem) {
.AnchorNav__actionsContainer {
position: absolute;
top: 0;
right: 0;
}
}

@media screen and (min-width: 40rem) {
.AnchorNav__actionsContainer {
gap: var(--base-size-16);
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/AnchorNav/AnchorNav.module.css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ declare const styles: {
readonly "AnchorNav-link-container": string;
readonly "AnchorNav-inner-container": string;
readonly "AnchorNav-inner-container--expanded": string;
readonly "AnchorNav__actionsContainer": string;
readonly "AnchorNav__actionsContainer--no-offset": string;
readonly "AnchorNav-link": string;
readonly "fade-in": string;
readonly "AnchorNav-link--is-active": string;
Expand All @@ -17,7 +19,6 @@ declare const styles: {
readonly "AnchorNav-menu-button": string;
readonly "AnchorNav-menu-button-arrow": string;
readonly "AnchorNav-overlay--expanded": string;
readonly "AnchorNav__actionsContainer": string;
readonly "AnchorNav-action": string;
readonly "AnchorNav__actionsContainer--multiple": string;
};
Expand Down
34 changes: 28 additions & 6 deletions packages/react/src/AnchorNav/AnchorNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, {ReactElement, useCallback, useEffect, useMemo, useRef, useState}
import {ChevronDownIcon, ChevronUpIcon} from '@primer/octicons-react'
import {useId} from '@reach/auto-id'

import {Button, Text} from '../'
import {Button, ButtonBaseProps, Text} from '../'
import {useWindowSize} from '../hooks/useWindowSize'
import {useKeyboardEscape} from '../hooks/useKeyboardEscape'
import {useExpandedMenu} from './useExpandedMenu'
Expand Down Expand Up @@ -177,6 +177,18 @@ function _AnchorNav({children, enableDefaultBgColor = false, hideUntilSticky = f
return null
}).filter(Boolean)

const hasLargerSizeActions =
Action.some(action => {
if (React.isValidElement<AnchorNavActionProps>(action) && action.props.size) {
return action.props.size !== 'small'
}
}) ||
SecondaryAction.some(action => {
if (React.isValidElement<AnchorNavActionProps>(action) && action.props.size) {
return action.props.size !== 'small'
}
})

/* On page load, the rootMargin positions and/or thresholds of the IntersectionObserver
* may not be met depending on the position of the AnchorNav on the page.
* The following useEffect ensures that the first link always marked as the active link, until
Expand Down Expand Up @@ -241,6 +253,7 @@ function _AnchorNav({children, enableDefaultBgColor = false, hideUntilSticky = f
data-forward-focus="true"
className={clsx(
styles['AnchorNav__actionsContainer'],
hasLargerSizeActions && styles['AnchorNav__actionsContainer--no-offset'],
hasTwoActions && styles['AnchorNav__actionsContainer--multiple'],
)}
>
Expand Down Expand Up @@ -379,33 +392,42 @@ type AnchorNavActionProps = {
* Required path or location for the action button to link to.
*/
href: string
} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>
} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> &
ButtonBaseProps

function _AnchorNavAction({children, href, ...rest}: AnchorNavActionProps) {
function _AnchorNavAction({children, href, variant = 'secondary', size = 'small', ...rest}: AnchorNavActionProps) {
return (
<Button
as="a"
variant="secondary"
variant={variant}
className={clsx(styles['AnchorNav-action'])}
href={href}
hasArrow={false}
data-testid={testIds.action}
size={size}
{...rest}
>
{children}
</Button>
)
}

function _AnchorNavActionSecondary({children, href, ...rest}: AnchorNavActionProps) {
function _AnchorNavActionSecondary({
children,
href,
variant = 'secondary',
size = 'small',
...rest
}: AnchorNavActionProps) {
return (
<Button
as="a"
variant="secondary"
variant={variant}
className={clsx(styles['AnchorNav-action'])}
href={href}
hasArrow={false}
data-testid={testIds.secondaryAction}
size={size}
{...rest}
>
{children}
Expand Down
18 changes: 18 additions & 0 deletions packages/react/src/AnchorNav/AnchorNav.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,22 @@ test.describe('Visual Comparison: AnchorNav', () => {
await page.waitForTimeout(500)
expect(await page.screenshot({fullPage: true})).toMatchSnapshot()
})

test('AnchorNav / With optional primary CTA', async ({page}) => {
await page.goto(
'http://localhost:6006/iframe.html?args=&id=components-anchornav-features--anchor-nav-primary-actions&viewMode=story',
)

await page.waitForTimeout(500)
expect(await page.screenshot({fullPage: true})).toMatchSnapshot()
})

test('AnchorNav / With larger CTAs', async ({page}) => {
await page.goto(
'http://localhost:6006/iframe.html?args=&id=components-anchornav-features--anchor-nav-larger-actions&viewMode=story',
)

await page.waitForTimeout(500)
expect(await page.screenshot({fullPage: true})).toMatchSnapshot()
})
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit cd18615

Please sign in to comment.