Skip to content

Commit

Permalink
Adds hawt config properties to change appearance of the header and si…
Browse files Browse the repository at this point in the history
…debar

 * Creates 'appearance' property group in hawtconfig with properties
   that determine whether
  - showHeader: to show/hide the main header bar
  - showBrand: to show/hide the brand logo in the header bar
  - showUserHeader: to show/hide the user header dropdown in the header bar
  - showSideBar: to show/hide the sidebar

 * config-manager.ts
  * Introduction of Appearance type

 * HawtioHeader.tsx
 * HawtioPage.tsx
  * Make the various components conditional acciroding to the properties
  • Loading branch information
phantomjinx committed Feb 17, 2025
1 parent 53d80dd commit 60245b3
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 54 deletions.
6 changes: 6 additions & 0 deletions app/public/hawtconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
"css": "",
"favicon": "favicon.ico"
},
"appearance": {
"showHeader": true,
"showBrand": true,
"showUserHeader": true,
"showSideBar": true
},
"login": {
"description": "Login page for Hawtio Management Console.",
"links": [
Expand Down
31 changes: 31 additions & 0 deletions packages/hawtio/src/core/config-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,35 @@ describe('ConfigManager', () => {
expect(product?.name).toEqual('Hawtio React')
expect(product?.value).toEqual('1.0.0')
})

test('appearance is loaded', async () => {
// response for fetching hawtconfig.json
fetchMock.mockResponse(
JSON.stringify({
appearance: {
showHeader: true,
showBrand: true,
showSideBar: true,
showUserHeader: true,
},
}),
)

const config = await configManager.getHawtconfig()
expect(config.appearance?.showHeader).toBe(true)
expect(config.appearance?.showBrand).toBe(true)
expect(config.appearance?.showUserHeader).toBe(true)
expect(config.appearance?.showSideBar).toBe(true)
})

test('appearance defaults', async () => {
// response for fetching hawtconfig.json
fetchMock.mockResponse(JSON.stringify({}))

const config = await configManager.getHawtconfig()
expect(config.appearance?.showHeader).toBeUndefined()
expect(config.appearance?.showBrand).toBeUndefined()
expect(config.appearance?.showUserHeader).toBeUndefined()
expect(config.appearance?.showSideBar).toBeUndefined()
})
})
22 changes: 22 additions & 0 deletions packages/hawtio/src/core/config-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export type Hawtconfig = {
*/
branding?: BrandingConfig

/**
* Configuration for the placement and structure of the UI
*/
appearance?: AppearanceConfig

/**
* Configuration for the built-in login page.
*/
Expand Down Expand Up @@ -55,6 +60,23 @@ export type BrandingConfig = {
favicon?: string
}

/**
* Appearance configuration type.
*/
export type AppearanceConfig = {
// Whether to display the header bar (default: true)
showHeader?: boolean

// Whether to display the brand logo on the header bar (default: true)
showBrand?: boolean

// Whether to display the user header dropdown on the header bar (default: true)
showUserHeader?: boolean

// Whether to display the sidebar (default: true)
showSideBar?: boolean
}

/**
* Login configuration type.
*/
Expand Down
124 changes: 75 additions & 49 deletions packages/hawtio/src/ui/page/HawtioHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { PUBLIC_USER, userService } from '@hawtiosrc/auth'
import { DEFAULT_APP_NAME, useHawtconfig, UniversalHeaderItem, isUniversalHeaderItem } from '@hawtiosrc/core'
import {
DEFAULT_APP_NAME,
useHawtconfig,
UniversalHeaderItem,
isUniversalHeaderItem,
Hawtconfig,
} from '@hawtiosrc/core'
import { hawtioLogo, userAvatar } from '@hawtiosrc/img'
import { preferencesService } from '@hawtiosrc/preferences/preferences-service'
import { HawtioAbout } from '@hawtiosrc/ui/about'
Expand Down Expand Up @@ -31,43 +37,54 @@ import './HawtioHeader.css'
import { PageContext } from './context'

export const HawtioHeader: React.FunctionComponent = () => {
const { hawtconfig, hawtconfigLoaded } = useHawtconfig()
const [navOpen, setNavOpen] = useState(preferencesService.isShowVerticalNavByDefault())

if (!hawtconfigLoaded) {
return null
}

const onNavToggle = () => setNavOpen(!navOpen)

// If not defined then assume the default of shown
const sideBarShown = hawtconfig.appearance?.showSideBar ?? true
const isBrandShown = hawtconfig.appearance?.showBrand ?? true

return (
<Masthead id='hawtio-header' display={{ default: 'inline' }}>
<MastheadToggle>
<PageToggleButton
variant='plain'
aria-label='Global navigation'
isSidebarOpen={navOpen}
onSidebarToggle={onNavToggle}
id='vertical-nav-toggle'
>
<BarsIcon />
</PageToggleButton>
</MastheadToggle>
<MastheadMain>
<HawtioBrand />
</MastheadMain>
{sideBarShown && (
<MastheadToggle>
<PageToggleButton
variant='plain'
aria-label='Global navigation'
isSidebarOpen={navOpen}
onSidebarToggle={onNavToggle}
id='vertical-nav-toggle'
>
<BarsIcon />
</PageToggleButton>
</MastheadToggle>
)}
{isBrandShown && (
<MastheadMain>
<HawtioBrand hawtconfig={hawtconfig} />
</MastheadMain>
)}
<MastheadContent>
<HawtioHeaderToolbar />
<HawtioHeaderToolbar hawtconfig={hawtconfig} />
</MastheadContent>
</Masthead>
)
}

const HawtioBrand: React.FunctionComponent = () => {
const { hawtconfig, hawtconfigLoaded } = useHawtconfig()

if (!hawtconfigLoaded) {
return null
}
type HawtioBrandProps = {
hawtconfig: Hawtconfig
}

const appLogo = hawtconfig.branding?.appLogoUrl ?? hawtioLogo
const appName = hawtconfig.branding?.appName ?? DEFAULT_APP_NAME
const showAppName = hawtconfig.branding?.showAppName ?? false
const HawtioBrand: React.FunctionComponent<HawtioBrandProps> = props => {
const appLogo = props.hawtconfig.branding?.appLogoUrl ?? hawtioLogo
const appName = props.hawtconfig.branding?.appName ?? DEFAULT_APP_NAME
const showAppName = props.hawtconfig.branding?.showAppName ?? false

return (
<MastheadBrand id='hawtio-header-brand' component={props => <Link to='/' {...props} />}>
Expand All @@ -81,7 +98,11 @@ const HawtioBrand: React.FunctionComponent = () => {
)
}

const HawtioHeaderToolbar: React.FunctionComponent = () => {
type HawtioHeaderToolbarProps = {
hawtconfig: Hawtconfig
}

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

Expand All @@ -97,6 +118,9 @@ const HawtioHeaderToolbar: React.FunctionComponent = () => {

const logout = () => userService.logout()

// If not defined then assume the default of shown
const userHeaderShown = props.hawtconfig.appearance?.showUserHeader ?? true

const helpItems = [
<DropdownItem key='help'>
<Link to='/help'>Help</Link>{' '}
Expand Down Expand Up @@ -186,29 +210,31 @@ const HawtioHeaderToolbar: React.FunctionComponent = () => {
</Dropdown>
</ToolbarItem>
</ToolbarGroup>
<ToolbarGroup>
<ToolbarItem>
<Dropdown
onSelect={onUserSelect}
isOpen={userOpen}
onOpenChange={setUserOpen}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
id='hawtio-header-user-dropdown-toggle'
onClick={() => setUserOpen(!userOpen)}
icon={<Avatar src={userAvatar} alt='user' />}
isExpanded={userOpen}
isFullHeight
>
{isPublic ? '' : username}
</MenuToggle>
)}
>
<DropdownList>{userItems}</DropdownList>
</Dropdown>
</ToolbarItem>
</ToolbarGroup>
{userHeaderShown && (
<ToolbarGroup>
<ToolbarItem>
<Dropdown
onSelect={onUserSelect}
isOpen={userOpen}
onOpenChange={setUserOpen}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
id='hawtio-header-user-dropdown-toggle'
onClick={() => setUserOpen(!userOpen)}
icon={<Avatar src={userAvatar} alt='user' />}
isExpanded={userOpen}
isFullHeight
>
{isPublic ? '' : username}
</MenuToggle>
)}
>
<DropdownList>{userItems}</DropdownList>
</Dropdown>
</ToolbarItem>
</ToolbarGroup>
)}
</ToolbarContent>
<HawtioAbout isOpen={aboutOpen} onClose={onAboutToggle} />
</Toolbar>
Expand Down
15 changes: 10 additions & 5 deletions packages/hawtio/src/ui/page/HawtioPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useUser } from '@hawtiosrc/auth/hooks'
import { usePlugins } from '@hawtiosrc/core'
import { useHawtconfig, usePlugins } from '@hawtiosrc/core'
import { HawtioHelp } from '@hawtiosrc/help/HawtioHelp'
import { background } from '@hawtiosrc/img'
import { PluginNodeSelectionContext, usePluginNodeSelected } from '@hawtiosrc/plugins'
Expand Down Expand Up @@ -28,6 +28,7 @@ import './HawtioPage.css'
export const HawtioPage: React.FunctionComponent = () => {
const { username, isLogin, userLoaded, userLoading } = useUser()
const { plugins, pluginsLoaded } = usePlugins()
const { hawtconfig, hawtconfigLoaded } = useHawtconfig()
const navigate = useNavigate()
const { search } = useLocation()
const { selectedNode, setSelectedNode } = usePluginNodeSelected()
Expand All @@ -40,7 +41,7 @@ export const HawtioPage: React.FunctionComponent = () => {
}
}, [isLogin, navigate, userLoading])

if (!userLoaded || !pluginsLoaded || userLoading) {
if (!userLoaded || !pluginsLoaded || userLoading || !hawtconfigLoaded) {
log.debug('Loading:', 'user =', userLoaded, ', plugins =', pluginsLoaded)
return <HawtioLoadingPage />
}
Expand All @@ -63,14 +64,18 @@ export const HawtioPage: React.FunctionComponent = () => {
sessionService.userActivity()
}

// If not defined then assume the default of shown
const headerShown = hawtconfig.appearance?.showHeader ?? true
const sideBarShown = hawtconfig.appearance?.showSideBar ?? true

return (
<PageContext.Provider value={{ username, plugins }}>
<BackgroundImage src={background} />
<Page
id='hawtio-main-page'
header={<HawtioHeader />}
sidebar={<HawtioSidebar />}
isManagedSidebar
header={headerShown && <HawtioHeader />}
sidebar={sideBarShown && <HawtioSidebar />}
isManagedSidebar={sideBarShown}
defaultManagedSidebarIsOpen={showVerticalNavByDefault}
onClick={keepAlive}
>
Expand Down

0 comments on commit 60245b3

Please sign in to comment.