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

feat: Support external links in navigation #1958

Merged
merged 4 commits into from
May 2, 2023
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
2 changes: 2 additions & 0 deletions py/examples/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ async def serve(q: Q):
ui.nav_group('Help', items=[
ui.nav_item(name='#about', label='About', icon='Info'),
ui.nav_item(name='#support', label='Support', icon='Help'),
ui.nav_item(name='faq', label='FAQ', icon='OfficeChat', path='https://h2o.ai/'),
])
],
secondary_items=[
Expand Down Expand Up @@ -61,6 +62,7 @@ async def serve(q: Q):
ui.nav_group('Help', items=[
ui.nav_item(name='#about', label='About', icon='Info'),
ui.nav_item(name='#support', label='Support', icon='Help'),
ui.nav_item(name='faq', label='FAQ', icon='OfficeChat', path='https://h2o.ai/'),
])
],
secondary_items=[ui.button(name='logout', label='Logout', width='100%')],
Expand Down
10 changes: 10 additions & 0 deletions py/h2o_lightwave/h2o_lightwave/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8700,12 +8700,14 @@ def __init__(
icon: Optional[str] = None,
disabled: Optional[bool] = None,
tooltip: Optional[str] = None,
path: Optional[str] = None,
):
_guard_scalar('NavItem.name', name, (str,), True, False, False)
_guard_scalar('NavItem.label', label, (str,), False, False, False)
_guard_scalar('NavItem.icon', icon, (str,), False, True, False)
_guard_scalar('NavItem.disabled', disabled, (bool,), False, True, False)
_guard_scalar('NavItem.tooltip', tooltip, (str,), False, True, False)
_guard_scalar('NavItem.path', path, (str,), False, True, False)
self.name = name
"""The name of this item. Prefix the name with a '#' to trigger hash-change navigation."""
self.label = label
Expand All @@ -8716,6 +8718,8 @@ def __init__(
"""True if this item should be disabled."""
self.tooltip = tooltip
"""An optional tooltip message displayed when a user hovers over this item."""
self.path = path
"""The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html`"""

def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
Expand All @@ -8724,12 +8728,14 @@ def dump(self) -> Dict:
_guard_scalar('NavItem.icon', self.icon, (str,), False, True, False)
_guard_scalar('NavItem.disabled', self.disabled, (bool,), False, True, False)
_guard_scalar('NavItem.tooltip', self.tooltip, (str,), False, True, False)
_guard_scalar('NavItem.path', self.path, (str,), False, True, False)
return _dump(
name=self.name,
label=self.label,
icon=self.icon,
disabled=self.disabled,
tooltip=self.tooltip,
path=self.path,
)

@staticmethod
Expand All @@ -8745,17 +8751,21 @@ def load(__d: Dict) -> 'NavItem':
_guard_scalar('NavItem.disabled', __d_disabled, (bool,), False, True, False)
__d_tooltip: Any = __d.get('tooltip')
_guard_scalar('NavItem.tooltip', __d_tooltip, (str,), False, True, False)
__d_path: Any = __d.get('path')
_guard_scalar('NavItem.path', __d_path, (str,), False, True, False)
name: str = __d_name
label: str = __d_label
icon: Optional[str] = __d_icon
disabled: Optional[bool] = __d_disabled
tooltip: Optional[str] = __d_tooltip
path: Optional[str] = __d_path
return NavItem(
name,
label,
icon,
disabled,
tooltip,
path,
)


Expand Down
3 changes: 3 additions & 0 deletions py/h2o_lightwave/h2o_lightwave/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3054,6 +3054,7 @@ def nav_item(
icon: Optional[str] = None,
disabled: Optional[bool] = None,
tooltip: Optional[str] = None,
path: Optional[str] = None,
) -> NavItem:
"""Create a navigation item.

Expand All @@ -3063,6 +3064,7 @@ def nav_item(
icon: An optional icon to display next to the label.
disabled: True if this item should be disabled.
tooltip: An optional tooltip message displayed when a user hovers over this item.
path: The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html`
Returns:
A `h2o_wave.types.NavItem` instance.
"""
Expand All @@ -3072,6 +3074,7 @@ def nav_item(
icon,
disabled,
tooltip,
path,
)


Expand Down
10 changes: 10 additions & 0 deletions py/h2o_wave/h2o_wave/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8700,12 +8700,14 @@ def __init__(
icon: Optional[str] = None,
disabled: Optional[bool] = None,
tooltip: Optional[str] = None,
path: Optional[str] = None,
):
_guard_scalar('NavItem.name', name, (str,), True, False, False)
_guard_scalar('NavItem.label', label, (str,), False, False, False)
_guard_scalar('NavItem.icon', icon, (str,), False, True, False)
_guard_scalar('NavItem.disabled', disabled, (bool,), False, True, False)
_guard_scalar('NavItem.tooltip', tooltip, (str,), False, True, False)
_guard_scalar('NavItem.path', path, (str,), False, True, False)
self.name = name
"""The name of this item. Prefix the name with a '#' to trigger hash-change navigation."""
self.label = label
Expand All @@ -8716,6 +8718,8 @@ def __init__(
"""True if this item should be disabled."""
self.tooltip = tooltip
"""An optional tooltip message displayed when a user hovers over this item."""
self.path = path
"""The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html`"""

def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
Expand All @@ -8724,12 +8728,14 @@ def dump(self) -> Dict:
_guard_scalar('NavItem.icon', self.icon, (str,), False, True, False)
_guard_scalar('NavItem.disabled', self.disabled, (bool,), False, True, False)
_guard_scalar('NavItem.tooltip', self.tooltip, (str,), False, True, False)
_guard_scalar('NavItem.path', self.path, (str,), False, True, False)
return _dump(
name=self.name,
label=self.label,
icon=self.icon,
disabled=self.disabled,
tooltip=self.tooltip,
path=self.path,
)

@staticmethod
Expand All @@ -8745,17 +8751,21 @@ def load(__d: Dict) -> 'NavItem':
_guard_scalar('NavItem.disabled', __d_disabled, (bool,), False, True, False)
__d_tooltip: Any = __d.get('tooltip')
_guard_scalar('NavItem.tooltip', __d_tooltip, (str,), False, True, False)
__d_path: Any = __d.get('path')
_guard_scalar('NavItem.path', __d_path, (str,), False, True, False)
name: str = __d_name
label: str = __d_label
icon: Optional[str] = __d_icon
disabled: Optional[bool] = __d_disabled
tooltip: Optional[str] = __d_tooltip
path: Optional[str] = __d_path
return NavItem(
name,
label,
icon,
disabled,
tooltip,
path,
)


Expand Down
3 changes: 3 additions & 0 deletions py/h2o_wave/h2o_wave/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3054,6 +3054,7 @@ def nav_item(
icon: Optional[str] = None,
disabled: Optional[bool] = None,
tooltip: Optional[str] = None,
path: Optional[str] = None,
) -> NavItem:
"""Create a navigation item.

Expand All @@ -3063,6 +3064,7 @@ def nav_item(
icon: An optional icon to display next to the label.
disabled: True if this item should be disabled.
tooltip: An optional tooltip message displayed when a user hovers over this item.
path: The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html`
Returns:
A `h2o_wave.types.NavItem` instance.
"""
Expand All @@ -3072,6 +3074,7 @@ def nav_item(
icon,
disabled,
tooltip,
path,
)


Expand Down
8 changes: 6 additions & 2 deletions r/R/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -3549,25 +3549,29 @@ ui_grid_card <- function(
#' @param icon An optional icon to display next to the label.
#' @param disabled True if this item should be disabled.
#' @param tooltip An optional tooltip message displayed when a user hovers over this item.
#' @param path The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html`
#' @return A NavItem instance.
#' @export
ui_nav_item <- function(
name,
label,
icon = NULL,
disabled = NULL,
tooltip = NULL) {
tooltip = NULL,
path = NULL) {
.guard_scalar("name", "character", name)
.guard_scalar("label", "character", label)
.guard_scalar("icon", "character", icon)
.guard_scalar("disabled", "logical", disabled)
.guard_scalar("tooltip", "character", tooltip)
.guard_scalar("path", "character", path)
.o <- list(
name=name,
label=label,
icon=icon,
disabled=disabled,
tooltip=tooltip)
tooltip=tooltip,
path=path)
class(.o) <- append(class(.o), c(.wave_obj, "WaveNavItem"))
return(.o)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1733,12 +1733,13 @@
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_nav_item" value="ui.nav_item(name='$name$',label='$label$',icon='$icon$',disabled=$disabled$,tooltip='$tooltip$'),$END$" description="Create Wave NavItem with full attributes." toReformat="true" toShortenFQNames="true">
<template name="w_full_nav_item" value="ui.nav_item(name='$name$',label='$label$',icon='$icon$',disabled=$disabled$,tooltip='$tooltip$',path='$path$'),$END$" description="Create Wave NavItem with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="name" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="label" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="icon" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="disabled" expression="" defaultValue="&quot;False&quot;" alwaysStopAt="true"/>
<variable name="tooltip" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="path" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
Expand Down
2 changes: 1 addition & 1 deletion tools/vscode-extension/component-snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,7 @@
"Wave Full NavItem": {
"prefix": "w_full_nav_item",
"body": [
"ui.nav_item(name='$1', label='$2', icon='$3', disabled=${4:False}, tooltip='$5'),$0"
"ui.nav_item(name='$1', label='$2', icon='$3', disabled=${4:False}, tooltip='$5', path='$6'),$0"
],
"description": "Create a full Wave NavItem."
},
Expand Down
18 changes: 18 additions & 0 deletions ui/src/nav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,22 @@ describe('Nav.tsx', () => {
const { container } = render(<View {...props} />)
expect(container.querySelector('.is-expanded')).not.toBeInTheDocument()
})

it('Does redirect if path is specified', () => {
const
props = {
...navProps,
state: {
items: [
{ label: 'group1', items: [{ name, label, path: 'https://h2o.ai/' }] }
]
},
},
windowOpenMock = jest.fn(),
{ getByTitle } = render(<View {...props} />)

window.open = windowOpenMock
fireEvent.click(getByTitle(label))
expect(windowOpenMock).toHaveBeenCalled()
})
})
16 changes: 9 additions & 7 deletions ui/src/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export interface NavItem {
/** True if this item should be disabled. */
disabled?: B
/** An optional tooltip message displayed when a user hovers over this item. */
tooltip?: S
tooltip?: S,
/** The path or URL to link to. If specified, the `name` is ignored. The URL is opened in a new browser window or tab. E.g. `/foo.html` or `http://example.com/foo.html` */
path?: S
}

/** Create a group of navigation items. */
Expand Down Expand Up @@ -119,7 +121,7 @@ export const
const groups = items.map((g): Fluent.INavLinkGroup => ({
name: g.label,
collapseByDefault: g.collapsed,
links: g.items.map(({ name, label, icon, disabled, tooltip }): Fluent.INavLink => ({
links: g.items.map(({ name, label, icon, disabled, tooltip, path }): Fluent.INavLink => ({
key: name,
name: label,
icon,
Expand All @@ -129,12 +131,12 @@ export const
url: '',
onClick: () => {
if (hideNav) hideNav()
if (name.startsWith('#')) {
window.location.hash = name.substring(1)
return
if (path) window.open(path, "_blank")
else if (name.startsWith('#')) window.location.hash = name.substring(1)
else {
wave.args[name] = true
wave.push()
}
wave.args[name] = true
wave.push()
}
}))
}))
Expand Down
Binary file modified website/docs/examples/assets/nav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions website/widgets/content/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,19 @@ q.page['nav'] = ui.nav_card(
]
)
```

## With path

Use a `path` attribute to navigate to internal or external link. See [links docs](/docs/widgets/form/link/) for more details.

```py
q.page['nav'] = ui.nav_card(
box='1 1 2 6',
items=[
ui.nav_group('Menu', items=[
ui.nav_item(name='internal_path_item', label='Internal', path='/demo'),
ui.nav_item(name='external_path_item', label='External', path='https://h2o.ai/')
])
]
)
```