forked from janus-idp/backstage-showcase
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app): make branding icons and primary color configurable (janus-…
…idp#555) * feat(app): make branding icons and primary color configurable * Update .changeset/shy-cobras-tease.md Co-authored-by: Frank Kong <50030060+Zaperex@users.noreply.github.com>
- Loading branch information
1 parent
20eba1d
commit fb319ee
Showing
12 changed files
with
314 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'app': minor | ||
--- | ||
|
||
Adds ability to configure branding icons and primary color |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { useSidebarOpenState } from '@backstage/core-components'; | ||
import { useApi } from '@backstage/core-plugin-api'; | ||
import { BrowserRouter } from 'react-router-dom'; | ||
import { SidebarLogo } from './SidebarLogo'; | ||
|
||
jest.mock('@backstage/core-components', () => ({ | ||
...jest.requireActual('@backstage/core-components'), | ||
useSidebarOpenState: jest.fn(), | ||
})); | ||
|
||
jest.mock('@backstage/core-plugin-api', () => ({ | ||
...jest.requireActual('@backstage/core-plugin-api'), | ||
useApi: jest.fn(), | ||
})); | ||
|
||
jest.mock('./LogoFull.tsx', () => () => ( | ||
<svg data-testid="default-full-logo" /> | ||
)); | ||
jest.mock('./LogoIcon.tsx', () => () => ( | ||
<svg data-testid="default-icon-logo" /> | ||
)); | ||
|
||
describe('SidebarLogo', () => { | ||
it('when sidebar is open renders the component with full logo base64 provided by config', () => { | ||
(useApi as any).mockReturnValue({ | ||
getOptionalString: jest.fn().mockReturnValue('fullLogoBase64URI'), | ||
}); | ||
|
||
(useSidebarOpenState as any).mockReturnValue({ isOpen: true }); | ||
const { getByTestId } = render( | ||
<BrowserRouter> | ||
<SidebarLogo /> | ||
</BrowserRouter>, | ||
); | ||
|
||
const fullLogo = getByTestId('home-logo'); | ||
expect(fullLogo).toBeInTheDocument(); | ||
expect(fullLogo).toHaveAttribute('src', 'fullLogoBase64URI'); // Check the expected attribute value | ||
}); | ||
|
||
it('when sidebar is open renders the component with default full logo if config is undefined', () => { | ||
(useApi as any).mockReturnValue({ | ||
getOptionalString: jest.fn().mockReturnValue(undefined), | ||
}); | ||
|
||
(useSidebarOpenState as any).mockReturnValue({ isOpen: true }); | ||
const { getByTestId } = render( | ||
<BrowserRouter> | ||
<SidebarLogo /> | ||
</BrowserRouter>, | ||
); | ||
|
||
expect(getByTestId('default-full-logo')).toBeInTheDocument(); | ||
}); | ||
|
||
it('when sidebar is closed renders the component with icon logo base64 provided by config', () => { | ||
(useApi as any).mockReturnValue({ | ||
getOptionalString: jest.fn().mockReturnValue('iconLogoBase64URI'), | ||
}); | ||
|
||
(useSidebarOpenState as any).mockReturnValue({ isOpen: false }); | ||
const { getByTestId } = render( | ||
<BrowserRouter> | ||
<SidebarLogo /> | ||
</BrowserRouter>, | ||
); | ||
|
||
const fullLogo = getByTestId('home-logo'); | ||
expect(fullLogo).toBeInTheDocument(); | ||
expect(fullLogo).toHaveAttribute('src', 'iconLogoBase64URI'); | ||
}); | ||
|
||
it('when sidebar is closed renders the component with icon logo from default if not provided with config', () => { | ||
(useApi as any).mockReturnValue({ | ||
getOptionalString: jest.fn().mockReturnValue(undefined), | ||
}); | ||
|
||
(useSidebarOpenState as any).mockReturnValue({ isOpen: false }); | ||
const { getByTestId } = render( | ||
<BrowserRouter> | ||
<SidebarLogo /> | ||
</BrowserRouter>, | ||
); | ||
|
||
expect(getByTestId('default-icon-logo')).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Link, useSidebarOpenState } from '@backstage/core-components'; | ||
import React from 'react'; | ||
import { makeStyles } from 'tss-react/mui'; | ||
import LogoFull from './LogoFull'; | ||
import LogoIcon from './LogoIcon'; | ||
import { configApiRef, useApi } from '@backstage/core-plugin-api'; | ||
|
||
const useStyles = makeStyles()({ | ||
sidebarLogo: { | ||
margin: '24px 0px 6px 24px', | ||
}, | ||
}); | ||
|
||
const LogoRender = ({ | ||
base64Logo, | ||
defaultLogo, | ||
width, | ||
}: { | ||
base64Logo: string | undefined; | ||
defaultLogo: React.JSX.Element; | ||
width: number; | ||
}) => { | ||
return base64Logo ? ( | ||
<img | ||
data-testid="home-logo" | ||
src={base64Logo} | ||
alt="Home logo" | ||
width={width} | ||
/> | ||
) : ( | ||
defaultLogo | ||
); | ||
}; | ||
|
||
export const SidebarLogo = () => { | ||
const { classes } = useStyles(); | ||
const { isOpen } = useSidebarOpenState(); | ||
const configApi = useApi(configApiRef); | ||
const logoFullBase64URI = configApi.getOptionalString( | ||
'app.branding.fullLogo', | ||
); | ||
const logoIconBase64URI = configApi.getOptionalString( | ||
'app.branding.iconLogo', | ||
); | ||
|
||
return ( | ||
<div className={classes.sidebarLogo}> | ||
<Link to="/" underline="none" aria-label="Home"> | ||
{isOpen ? ( | ||
<LogoRender | ||
base64Logo={logoFullBase64URI} | ||
defaultLogo={<LogoFull />} | ||
width={110} | ||
/> | ||
) : ( | ||
<LogoRender | ||
base64Logo={logoIconBase64URI} | ||
defaultLogo={<LogoIcon />} | ||
width={28} | ||
/> | ||
)} | ||
</Link> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { renderHook } from '@testing-library/react-hooks'; | ||
import { useApi } from '@backstage/core-plugin-api'; | ||
import { useUpdateTheme } from './useUpdateTheme'; | ||
|
||
jest.mock('@backstage/core-plugin-api', () => ({ | ||
...jest.requireActual('@backstage/core-plugin-api'), | ||
useApi: jest.fn(), | ||
})); | ||
|
||
describe('useUpdateTheme', () => { | ||
it('returns the primaryColor when config is available', () => { | ||
(useApi as any).mockReturnValue({ | ||
getOptionalString: jest.fn().mockReturnValue('blue'), | ||
}); | ||
|
||
const { result } = renderHook(() => useUpdateTheme('someTheme')); | ||
expect(result.current.primaryColor).toBe('blue'); | ||
}); | ||
|
||
it('returns undefined when config is unavailable', () => { | ||
// Mock the useApi function to throw an error (simulate unavailable config) | ||
(useApi as any).mockImplementation( | ||
jest.fn(() => { | ||
throw new Error('Custom hook error'); | ||
}), | ||
); | ||
|
||
const { result } = renderHook(() => useUpdateTheme('someTheme')); | ||
expect(result.current.primaryColor).toBeUndefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { configApiRef, useApi } from '@backstage/core-plugin-api'; | ||
|
||
export const useUpdateTheme = ( | ||
selTheme: string, | ||
): { primaryColor: string | undefined } => { | ||
let primaryColor: string | undefined; | ||
try { | ||
const configApi = useApi(configApiRef); | ||
primaryColor = configApi.getOptionalString( | ||
`app.branding.theme.${selTheme}.primaryColor`, | ||
); | ||
} catch (err) { | ||
// useApi won't be initialized initally in createApp theme provider, and will get updated later | ||
} | ||
return { primaryColor }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.