Skip to content

Commit

Permalink
feat(ui-drilldown,ui-top-nav-bar): add shouldCloseOnClick
Browse files Browse the repository at this point in the history
Closes: INSTUI-3911
  • Loading branch information
joyenjoyer committed Feb 1, 2024
1 parent 25271d8 commit c3df722
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class DrilldownOption extends Component<DrilldownOptionProps> {
beforeLabelContentVAlign: 'start',
afterLabelContentVAlign: 'start',
as: 'li',
role: 'menuitem'
role: 'menuitem',
shouldCloseOnClick: 'auto'
}

render() {
Expand Down
13 changes: 11 additions & 2 deletions packages/ui-drilldown/src/Drilldown/DrilldownOption/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type DrilldownOptionValue = string | number | undefined

type RenderContentVAlign = 'start' | 'center' | 'end'

type ShouldCloseOnClick = 'auto' | 'always' | 'never'

type DrilldownOptionVariant = Exclude<OptionsItemProps['variant'], 'selected'>

type RenderContentProps = {
Expand Down Expand Up @@ -171,6 +173,11 @@ type DrilldownOptionOwnProps = {
* Provides a reference to the underlying html root element
*/
elementRef?: (element: Element | null) => void

/**
* Should close the container menu component, if clicked on the option marked with this prop
*/
shouldCloseOnClick?: ShouldCloseOnClick
}

type PropKeys = keyof DrilldownOptionOwnProps
Expand Down Expand Up @@ -200,7 +207,8 @@ const propTypes: PropValidators<PropKeys> = {
descriptionRole: PropTypes.string,
onOptionClick: PropTypes.func,
defaultSelected: PropTypes.bool,
elementRef: PropTypes.func
elementRef: PropTypes.func,
shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never'])
}

const allowedProps: AllowedPropKeys = [
Expand All @@ -222,7 +230,8 @@ const allowedProps: AllowedPropKeys = [
'descriptionRole',
'onOptionClick',
'defaultSelected',
'elementRef'
'elementRef',
'shouldCloseOnClick'
]

export type { DrilldownOptionProps, DrilldownOptionValue }
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-drilldown/src/Drilldown/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ type DrilldownOwnProps = {
defaultShow?: boolean

/**
* Is the `<Drilldown />` open (should be accompanied by `onToggle`)
* Is the `<Drilldown />` open (should be accompanied by `onToggle` and `trigger`)
*/
show?: boolean // TODO: type controllable(PropTypes.bool, 'onToggle', 'defaultShow')

Expand Down
4 changes: 2 additions & 2 deletions packages/ui-top-nav-bar/src/TopNavBar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ class PlaygroundExample extends React.Component {
<Breadcrumb.Link href="#">Course page 2</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 3</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 4</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 5</Breadcrumb.Link>
<Breadcrumb.Link>Course page 5</Breadcrumb.Link>
</Breadcrumb>
</TopNavBar.Breadcrumb>
)}
Expand Down Expand Up @@ -1271,7 +1271,7 @@ type: example
<Breadcrumb.Link href="#">Course page 2</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 3</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 4</Breadcrumb.Link>
<Breadcrumb.Link href="#">Course page 5</Breadcrumb.Link>
<Breadcrumb.Link>Course page 5</Breadcrumb.Link>
</Breadcrumb>
</TopNavBar.Breadcrumb>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
static defaultProps = {
status: 'default',
variant: 'default',
showSubmenuChevron: true
showSubmenuChevron: true,
shouldCloseOnClick: 'auto'
} as const

declare context: React.ContextType<typeof TopNavBarContext>
Expand Down
12 changes: 10 additions & 2 deletions packages/ui-top-nav-bar/src/TopNavBar/TopNavBarItem/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { TopNavBarItem } from './index'

type ItemChild = React.ComponentElement<TopNavBarItemProps, TopNavBarItem>
type DrilldownSubmenu = React.ComponentElement<DrilldownProps, Drilldown>
type ShouldCloseOnClick = 'auto' | 'always' | 'never'

type TopNavBarItemTooltipType =
| string
Expand Down Expand Up @@ -241,6 +242,11 @@ type TopNavBarItemOwnProps = {
* A function that returns a reference to the button/link HTML element
*/
itemRef?: (el: HTMLButtonElement | HTMLLinkElement | null) => void

/**
* Should close the container menu component, if clicked on the option marked with this prop
*/
shouldCloseOnClick?: ShouldCloseOnClick
}

type PropKeys = keyof TopNavBarItemOwnProps
Expand Down Expand Up @@ -300,7 +306,8 @@ const propTypes: PropValidators<PropKeys> = {
onKeyDown: PropTypes.func,
onKeyUp: PropTypes.func,
elementRef: PropTypes.func,
itemRef: PropTypes.func
itemRef: PropTypes.func,
shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never'])
}

const allowedProps: AllowedPropKeys = [
Expand All @@ -325,7 +332,8 @@ const allowedProps: AllowedPropKeys = [
'onKeyDown',
'onKeyUp',
'elementRef',
'itemRef'
'itemRef',
'shouldCloseOnClick'
]

export type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
SmallViewportModeWrapper
} from '../../../utils/exampleHelpers'
import { TopNavBarItem } from '../../../TopNavBarItem'
import { TopNavBarMenuItems } from '../../../TopNavBarMenuItems'
import { TopNavBarUser } from '../../../TopNavBarUser'

import { TopNavBarSmallViewportLayout } from '../index'
Expand Down Expand Up @@ -1501,6 +1502,98 @@ describe('<TopNavBarSmallViewportLayout />', () => {
})
})

describe('shouldCloseOnClick prop', () => {
it('should be closed an item with shouldCloseOnClick prop is selected', async () => {
const onDropdownMenuSelect = jest.fn()
const onDropdownMenuToggle = jest.fn()

render(
<SmallViewportModeWrapper>
<TopNavBarSmallViewportLayout
{...defaultProps}
onDropdownMenuSelect={onDropdownMenuSelect}
onDropdownMenuToggle={onDropdownMenuToggle}
renderMenuItems={
<TopNavBarMenuItems
listLabel="Page navigation"
currentPageId="OverviewPage"
renderHiddenItemsMenuTriggerLabel={(hiddenChildrenCount) =>
`${hiddenChildrenCount} More`
}
>
<TopNavBarItem id="AlwaysClose" shouldCloseOnClick="always">
Always close
</TopNavBarItem>
<TopNavBarItem id="NeverClose" shouldCloseOnClick="never">
Never close
</TopNavBarItem>
<TopNavBarItem id="AutoHref" href="/#TopNavBar">
Auto behavior with href
</TopNavBarItem>
<TopNavBarItem id="AutoNoHref">
Auto behavior without href
</TopNavBarItem>
</TopNavBarMenuItems>
}
/>
</SmallViewportModeWrapper>
)

const menuTriggerButton = screen.getByRole('button')

fireEvent.click(menuTriggerButton)

// Opening menu
expect(onDropdownMenuToggle).toHaveBeenCalledTimes(1)

const alwaysCloseOption = await screen.findByText('Always close')

fireEvent.click(alwaysCloseOption)

// Selecting Always close option, menu is closing
expect(onDropdownMenuSelect).toHaveBeenCalledTimes(1)
expect(onDropdownMenuToggle).toHaveBeenCalledTimes(2)

// Opening menu again
fireEvent.click(menuTriggerButton)

expect(onDropdownMenuToggle).toHaveBeenCalledTimes(3)

const neverCloseOption = await screen.findByText('Never close')

fireEvent.click(neverCloseOption)

// Selecting Never close option, menu is not closing
expect(onDropdownMenuSelect).toHaveBeenCalledTimes(2)
expect(onDropdownMenuToggle).toHaveBeenCalledTimes(3)

const autoCloseOptionWithHref = await screen.findByText(
'Auto behavior with href'
)

fireEvent.click(autoCloseOptionWithHref)

// Selecting Auto behavior with href option, menu is closing
expect(onDropdownMenuSelect).toHaveBeenCalledTimes(3)
expect(onDropdownMenuToggle).toHaveBeenCalledTimes(4)

// Opening menu again
fireEvent.click(menuTriggerButton)

expect(onDropdownMenuToggle).toHaveBeenCalledTimes(5)

const autoCloseOptionWithoutHref = await screen.findByText(
'Auto behavior without href'
)

fireEvent.click(autoCloseOptionWithoutHref)

// Selecting Auto behavior without href option, menu is not closing
expect(onDropdownMenuSelect).toHaveBeenCalledTimes(4)
expect(onDropdownMenuToggle).toHaveBeenCalledTimes(5)
})
})

describe('should be accessible', () => {
it('a11y', async () => {
const { container } = render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,11 @@ class TopNavBarSmallViewportLayout extends Component<
onDropdownMenuSelect(e, args)
}

if (args.selectedOption.props.href) {
if (
(args.selectedOption.props.shouldCloseOnClick === 'auto' &&
!!args.selectedOption.props.href) ||
args.selectedOption.props.shouldCloseOnClick === 'always'
) {
this.toggleDropdownMenu()
}
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const mapItemsForDrilldown = (
status,
variant,
href,
onClick
onClick,
shouldCloseOnClick
} = item.props

let submenu: TopNavBarItemProps['renderSubmenu'] = renderSubmenu
Expand Down Expand Up @@ -182,7 +183,8 @@ const mapItemsForDrilldown = (
})
: children,
subPageId: optionSubPageId,
'aria-current': ariaCurrent
'aria-current': ariaCurrent,
shouldCloseOnClick: shouldCloseOnClick
}
})
})
Expand Down

0 comments on commit c3df722

Please sign in to comment.