Skip to content

Commit

Permalink
feature: Ability to add custom toolbar items to main page nav bar
Browse files Browse the repository at this point in the history
* core.ts
 * Adds optional toolbarItems property to plugin interface

* HawtioHeader.tsx
 * Picks up the plugins from the context and find the plugin that is
   currently being displayed. If this plugin has a toolbar item property
   then pick up the components into a custom toolbar group
  • Loading branch information
phantomjinx committed Sep 7, 2023
1 parent 8e9ffee commit bf4c67b
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 4 deletions.
7 changes: 6 additions & 1 deletion app/src/examples/example3/Example3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ export const Example3: React.FunctionComponent = () => (
<PageSection variant={PageSectionVariants.light}>
<TextContent>
<Text component='h1'>Example 3</Text>
<Text component='p'>This is yet another example plugin.</Text>
<Text component='p'>
This is another example plugin that also demonstrates the addition of custom components
to the main header toolbar.
Components should be added in the Plugin structure using the `headerItems` array.
Toolbar components should be created as single FunctionComponents and added to the array.
</Text>
</TextContent>
</PageSection>
)
91 changes: 91 additions & 0 deletions app/src/examples/example3/ToolbarItemComp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react'
import { Button, Dropdown, DropdownItem, DropdownSeparator, DropdownToggle, Modal } from '@patternfly/react-core'

export const ToolbarItemComp1: React.FunctionComponent = () => {
const [isModalOpen, setIsModalOpen] = React.useState(false)

const handleModalToggle = () => {
setIsModalOpen(!isModalOpen)
}

return (
<React.Fragment>
<Button variant='primary' onClick={handleModalToggle}>
Click Me!
</Button>

<Modal
title='Basic modal'
isOpen={isModalOpen}
onClose={handleModalToggle}
actions={[
<Button key='confirm' variant='primary' onClick={handleModalToggle}>
Confirm
</Button>,
<Button key='cancel' variant='link' onClick={handleModalToggle}>
Cancel
</Button>,
]}
>
Hello World!
</Modal>
</React.Fragment>
)
}

export const ToolbarItemComp2: React.FunctionComponent = () => {
const [isOpen, setIsOpen] = React.useState(false)

const onToggle = (isOpen: boolean) => {
setIsOpen(isOpen)
}

const onFocus = () => {
const element = document.getElementById('toggle-basic')
element?.focus()
}

const onSelect = () => {
setIsOpen(false)
onFocus()
}

const dropdownItems = [
<DropdownItem key='link' tooltip='Tooltip for enabled link'>
Link
</DropdownItem>,
<DropdownItem key='action' component='button' tooltip='Tooltip for enabled button'>
Action
</DropdownItem>,
<DropdownItem key='disabled link' isDisabled href='www.google.com'>
Disabled link
</DropdownItem>,
<DropdownItem
key='disabled action'
isAriaDisabled
component='button'
tooltip='Tooltip for disabled item'
tooltipProps={{ position: 'top' }}
>
Disabled action
</DropdownItem>,
<DropdownSeparator key='separator' />,
<DropdownItem key='separated link'>Separated link</DropdownItem>,
<DropdownItem key='separated action' component='button'>
Separated action
</DropdownItem>,
]

return (
<Dropdown
onSelect={onSelect}
toggle={
<DropdownToggle id='toggle-basic' onToggle={onToggle}>
Dropdown
</DropdownToggle>
}
isOpen={isOpen}
dropdownItems={dropdownItems}
/>
)
}
2 changes: 2 additions & 0 deletions app/src/examples/example3/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { hawtio, HawtioPlugin } from '@hawtio/react'
import { Example3 } from './Example3'
import { ToolbarItemComp1, ToolbarItemComp2 } from './ToolbarItemComp'

export const registerExample3: HawtioPlugin = () => {
hawtio.addPlugin({
id: 'example3',
title: 'Example 3',
path: '/example3',
component: Example3,
headerItems: [ToolbarItemComp1, ToolbarItemComp2],
isActive: async () => true,
})
}
3 changes: 3 additions & 0 deletions packages/hawtio/src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export interface Plugin {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: React.ComponentType<any>

// eslint-disable-next-line @typescript-eslint/no-explicit-any
headerItems?: React.ComponentType<any>[]

/**
* Returns if this plugin should be activated.
* This method needs to return a promise as the process of resolving if a plugin
Expand Down
23 changes: 20 additions & 3 deletions packages/hawtio/src/ui/page/HawtioHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PUBLIC_USER, userService } from '@hawtiosrc/auth'
import { DEFAULT_APP_NAME, useHawtconfig } from '@hawtiosrc/core'
import { DEFAULT_APP_NAME, useHawtconfig, Plugin } from '@hawtiosrc/core'
import { hawtioLogo, userAvatar } from '@hawtiosrc/img'
import { preferencesService } from '@hawtiosrc/preferences/preferences-service'
import { HawtioAbout } from '@hawtiosrc/ui/about'
Expand All @@ -22,7 +22,7 @@ import {
} from '@patternfly/react-core'
import { BarsIcon, HelpIcon } from '@patternfly/react-icons'
import React, { useContext, useState } from 'react'
import { Link } from 'react-router-dom'
import { Link, useLocation } from 'react-router-dom'
import './HawtioHeader.css'
import { PageContext } from './context'

Expand Down Expand Up @@ -72,7 +72,8 @@ const HawtioBrand: React.FunctionComponent = () => {
}

const HawtioHeaderToolbar: React.FunctionComponent = () => {
const { username } = useContext(PageContext)
const { username, plugins } = useContext(PageContext)
const location = useLocation()

const isPublic = username === PUBLIC_USER

Expand Down Expand Up @@ -104,9 +105,25 @@ const HawtioHeaderToolbar: React.FunctionComponent = () => {
userItems.pop()
}

/*
* Determine which plugin is currently displaying
* based on the path of the current location
*/
const pluginFromLocation = (): Plugin | null => {
const path = location.pathname
return plugins.find(plugin => path.startsWith(plugin.path)) ?? null
}

const plugin = pluginFromLocation()

return (
<Toolbar id='hawtio-header-toolbar'>
<ToolbarContent>
<ToolbarGroup>
{plugin?.headerItems?.map((comp, index) => (
<ToolbarItem key={`hawtio-header-toolbar-plugin-item-${index}`}>{React.createElement(comp)}</ToolbarItem>
))}
</ToolbarGroup>
<ToolbarGroup>
<ToolbarItem>
<Dropdown
Expand Down

0 comments on commit bf4c67b

Please sign in to comment.