Skip to content

Commit

Permalink
<UnderlineNav> menu now uses the Anchored Position on smaller scree…
Browse files Browse the repository at this point in the history
…n sizes to not clip, or go out of bounds. (#4234)

* fix & tests

* Create tricky-moose-design.md

* fixes with @broccolinisoup 's feedback

* type-only import BetterSystemStyleObject

---------

Co-authored-by: Armagan Ersoz <broccolinisoup@github.com>
  • Loading branch information
Rebstorm and broccolinisoup committed Mar 26, 2024
1 parent bb04e9b commit 8bbb8e5
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/tricky-moose-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

`<UnderlineNav>` menu now uses the Anchored Position on smaller screen sizes to not clip, or go out of bounds.
17 changes: 17 additions & 0 deletions packages/react/src/UnderlineNav/UnderlineNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {

import {UnderlineNav} from '.'
import {checkExports, checkStoriesForAxeViolations} from '../utils/testing'
import {baseMenuMinWidth, menuStyles} from './styles'

// window.matchMedia() is not implemented by JSDOM so we have to create a mock:
// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
Expand Down Expand Up @@ -169,6 +170,22 @@ describe('UnderlineNav', () => {
expect(spy).toHaveBeenCalled()
spy.mockRestore()
})

it(`menuStyles should set the menu position, if the container size is below ${baseMenuMinWidth} px`, () => {
// GIVEN
// Mock the refs.
const containerRef = document.createElement('div')
const listRef = document.createElement('div')
// Set the clientWidth on the mock element
Object.defineProperty(listRef, 'clientWidth', {value: baseMenuMinWidth - 1})

// WHEN
const results = menuStyles(containerRef, listRef)

// THEN
// We are expecting a left value back, that way we know the `getAnchoredPosition` ran.
expect(results).toEqual(expect.objectContaining({left: 0}))
})
})

describe('Keyboard Navigation', () => {
Expand Down
18 changes: 16 additions & 2 deletions packages/react/src/UnderlineNav/UnderlineNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@ import {useResizeObserver} from '../hooks/useResizeObserver'
import {useTheme} from '../ThemeProvider'
import type {ChildWidthArray, ResponsiveProps, ChildSize} from './types'
import VisuallyHidden from '../_VisuallyHidden'
import {moreBtnStyles, getDividerStyle, getNavStyles, ulStyles, menuStyles, menuItemStyles, GAP} from './styles'
import {
moreBtnStyles,
getDividerStyle,
getNavStyles,
ulStyles,
menuStyles,
menuItemStyles,
GAP,
baseMenuStyles,
baseMenuMinWidth,
} from './styles'
import styled from 'styled-components'
import {Button} from '../Button'
import {TriangleDownIcon} from '@primer/octicons-react'
Expand Down Expand Up @@ -341,7 +351,11 @@ export const UnderlineNav = forwardRef(
selectionVariant="single"
ref={containerRef}
id={disclosureWidgetId}
sx={menuStyles}
sx={
listRef.current?.clientWidth && listRef.current.clientWidth >= baseMenuMinWidth
? baseMenuStyles
: menuStyles(containerRef.current, listRef.current)
}
style={{display: isWidgetOpen ? 'block' : 'none'}}
>
{menuItems.map((menuItem, index) => {
Expand Down
26 changes: 23 additions & 3 deletions packages/react/src/UnderlineNav/styles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type {Theme} from '../ThemeProvider'
import type {BetterSystemStyleObject} from '../sx'
import {getAnchoredPosition} from '@primer/behaviors'

// The gap between the list items. It is a constant because the gap is used to calculate the possible number of items that can fit in the container.
export const GAP = 8
Expand Down Expand Up @@ -136,16 +138,34 @@ export const menuItemStyles = {
textDecoration: 'none',
}

export const menuStyles = {
export const baseMenuMinWidth = 192
export const baseMenuStyles: BetterSystemStyleObject = {
position: 'absolute',
zIndex: 1,
top: '90%',
right: '0',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)',
borderRadius: '12px',
backgroundColor: 'canvas.overlay',
listStyle: 'none',
// Values are from ActionMenu
minWidth: '192px',
minWidth: `${baseMenuMinWidth}px`,
maxWidth: '640px',
right: '0',
}

/**
*
* @param containerRef The Menu List Container Reference.
* @param listRef The Underline Nav Container Reference.
* @description This calculates the position of the menu
*/
export const menuStyles = (containerRef: Element | null, listRef: Element | null): BetterSystemStyleObject => {
if (containerRef && listRef) {
const {left} = getAnchoredPosition(containerRef, listRef, {align: 'start', side: 'outside-bottom'})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {right, ...rest} = baseMenuStyles
return {...rest, left}
}

return baseMenuStyles
}

0 comments on commit 8bbb8e5

Please sign in to comment.