-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(resource-adm): copy LeftNavigationBar component to resourceadm (#…
- Loading branch information
Showing
19 changed files
with
892 additions
and
4 deletions.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
frontend/resourceadm/components/LeftNavigationBar/GoBackButton/GoBackButton.module.css
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,17 @@ | ||
.icon { | ||
font-size: 1.8rem; | ||
color: var(--fds-semantic-text-neutral-default); | ||
} | ||
|
||
.backButton { | ||
border-bottom: 2px solid var(--fds-semantic-border-divider-default); | ||
} | ||
|
||
.backButton:hover { | ||
background-color: var(--fds-semantic-surface-neutral-subtle-hover); | ||
} | ||
|
||
.buttonText { | ||
color: var(--fds-semantic-text-neutral-default); | ||
margin-left: 10px; | ||
} |
28 changes: 28 additions & 0 deletions
28
frontend/resourceadm/components/LeftNavigationBar/GoBackButton/GoBackButton.test.tsx
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,28 @@ | ||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import type { GoBackButtonProps } from './GoBackButton'; | ||
import { GoBackButton } from './GoBackButton'; | ||
import { MemoryRouter } from 'react-router-dom'; | ||
|
||
const mockBackButtonText: string = 'Go back'; | ||
|
||
describe('GoBackButton', () => { | ||
afterEach(jest.clearAllMocks); | ||
|
||
const defaultProps: GoBackButtonProps = { | ||
className: '.navigationElement', | ||
to: '/back', | ||
text: mockBackButtonText, | ||
}; | ||
|
||
it('calls the "onClickBackButton" function when the button is clicked', () => { | ||
render( | ||
<MemoryRouter initialEntries={['/']}> | ||
<GoBackButton {...defaultProps} /> | ||
</MemoryRouter>, | ||
); | ||
|
||
const backButton = screen.getByRole('link', { name: mockBackButtonText }); | ||
expect(backButton).toBeInTheDocument(); | ||
}); | ||
}); |
50 changes: 50 additions & 0 deletions
50
frontend/resourceadm/components/LeftNavigationBar/GoBackButton/GoBackButton.tsx
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,50 @@ | ||
import type { ReactNode } from 'react'; | ||
import React from 'react'; | ||
import classes from './GoBackButton.module.css'; | ||
import cn from 'classnames'; | ||
import { ArrowLeftIcon } from '@studio/icons'; | ||
import { Paragraph } from '@digdir/designsystemet-react'; | ||
import { NavLink } from 'react-router-dom'; | ||
|
||
export type GoBackButtonProps = { | ||
/** | ||
* Classname for navigation element | ||
*/ | ||
className?: string; | ||
/** | ||
* Text on the back button | ||
*/ | ||
text: string; | ||
/** | ||
* Where to navigate to | ||
*/ | ||
to: string; | ||
}; | ||
|
||
/** | ||
* @component | ||
* Displays the back button on top of the LeftNavigationBar | ||
* | ||
* @example | ||
* <GoBackButton | ||
* className={classes.navigationElement} | ||
* text={backButtonText} | ||
* to={someUrl} | ||
* /> | ||
* | ||
* @property {string}[className] - Classname for navigation element | ||
* @property {string}[text] - Text on the back button | ||
* @property {string}[to] - Where to navigate to | ||
* | ||
* @returns {ReactNode} - The rendered component | ||
*/ | ||
export const GoBackButton = ({ className, text, to }: GoBackButtonProps): ReactNode => { | ||
return ( | ||
<NavLink className={cn(className, classes.backButton)} to={to}> | ||
<ArrowLeftIcon className={classes.icon} /> | ||
<Paragraph asChild size='small' variant='short' className={classes.buttonText}> | ||
<span>{text}</span> | ||
</Paragraph> | ||
</NavLink> | ||
); | ||
}; |
1 change: 1 addition & 0 deletions
1
frontend/resourceadm/components/LeftNavigationBar/GoBackButton/index.ts
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 @@ | ||
export { GoBackButton } from './GoBackButton'; |
47 changes: 47 additions & 0 deletions
47
frontend/resourceadm/components/LeftNavigationBar/LeftNavigationBar.module.css
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,47 @@ | ||
.navigationBar { | ||
background-color: var(--fds-semantic-background-subtle); | ||
width: 250px; | ||
height: 100%; | ||
left: 0; | ||
border-right: 1px solid var(--fds-semantic-border-divider-default); | ||
border-top: none; | ||
} | ||
|
||
.navigationElements { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.navigationElement { | ||
display: flex; | ||
align-items: center; | ||
padding-left: 15px; | ||
padding-block: 20px; | ||
border: none; | ||
border-bottom: 1px solid var(--fds-semantic-border-divider-default); | ||
background-color: var(--fds-semantic-background-subtle); | ||
cursor: pointer; | ||
} | ||
|
||
.navigationElement:hover { | ||
background-color: var(--fds-semantic-background-default); | ||
z-index: 1; | ||
} | ||
|
||
.navigationElement:focus-visible { | ||
background-color: var(--fds-semantic-background-default); | ||
z-index: 2; | ||
border-bottom: none; | ||
|
||
outline: var(--focus-border-width) solid var(--focus-border-outline-color); | ||
outline-offset: var(--focus-border-width); | ||
box-shadow: | ||
8px 0 0 0 var(--fds-semantic-border-action-active) inset, | ||
0 0 0 var(--focus-border-width) var(--focus-border-inner-color); | ||
} | ||
|
||
.navigationElement:focus:not(:focus-visible) { | ||
outline: none; | ||
box-shadow: none; | ||
background-color: var(--fds-semantic-background-subtle); | ||
} |
178 changes: 178 additions & 0 deletions
178
frontend/resourceadm/components/LeftNavigationBar/LeftNavigationBar.test.tsx
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,178 @@ | ||
import React from 'react'; | ||
import { render as rtlRender, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import type { LeftNavigationBarProps } from './LeftNavigationBar'; | ||
import { LeftNavigationBar } from './LeftNavigationBar'; | ||
import type { LeftNavigationTab, TabAction } from './Tab/LeftNavigationTab'; | ||
import { TestFlaskIcon } from '@studio/icons'; | ||
import { MemoryRouter } from 'react-router-dom'; | ||
import { textMock } from '@studio/testing/mocks/i18nMock'; | ||
|
||
const mockOnClick = jest.fn(); | ||
|
||
const mockTo: string = '/test'; | ||
|
||
const mockLinkAction1: TabAction = { | ||
type: 'link', | ||
to: mockTo, | ||
onClick: mockOnClick, | ||
}; | ||
|
||
const mockLinkAction2: TabAction = { | ||
type: 'link', | ||
to: mockTo, | ||
}; | ||
|
||
const mockButtonAction: TabAction = { | ||
type: 'button', | ||
onClick: mockOnClick, | ||
}; | ||
|
||
const mockTabId1: string = 'tab1'; | ||
const mockTabId2: string = 'tab2'; | ||
const mockTabId3: string = 'tab3'; | ||
|
||
const mockTabs: LeftNavigationTab[] = [ | ||
{ | ||
icon: <TestFlaskIcon />, | ||
tabName: `test.test_${mockTabId1}`, | ||
tabId: mockTabId1, | ||
action: mockLinkAction1, | ||
isActiveTab: true, | ||
}, | ||
{ | ||
icon: <TestFlaskIcon />, | ||
tabName: `test.test_${mockTabId2}`, | ||
tabId: mockTabId2, | ||
action: mockButtonAction, | ||
isActiveTab: false, | ||
}, | ||
{ | ||
icon: <TestFlaskIcon />, | ||
tabName: `test.test_${mockTabId3}`, | ||
tabId: mockTabId3, | ||
action: mockLinkAction2, | ||
isActiveTab: false, | ||
}, | ||
]; | ||
|
||
const mockBackButtonText: string = 'Go back'; | ||
const mockBackButtonHref: string = '/back'; | ||
|
||
describe('LeftNavigationBar', () => { | ||
afterEach(jest.clearAllMocks); | ||
|
||
const defaultProps: LeftNavigationBarProps = { | ||
tabs: mockTabs, | ||
upperTab: 'backButton', | ||
backLink: mockBackButtonHref, | ||
backLinkText: mockBackButtonText, | ||
selectedTab: mockTabId1, | ||
}; | ||
|
||
it('calls the onClick function when a tab is clicked and action type is button', async () => { | ||
const user = userEvent.setup(); | ||
render(defaultProps); | ||
|
||
const nextTab = mockTabs[1]; | ||
|
||
const tab2 = screen.getByRole('tab', { name: textMock(nextTab.tabName) }); | ||
await user.click(tab2); | ||
expect(nextTab.action.onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('calls the onClick function when a tab is clicked and action type is link and onClick is present', async () => { | ||
const user = userEvent.setup(); | ||
render(defaultProps); | ||
|
||
const nextTab = mockTabs[1]; | ||
const tab2 = screen.getByRole('tab', { name: textMock(nextTab.tabName) }); | ||
await user.click(tab2); | ||
|
||
const newNextTab = mockTabs[0]; | ||
const tab1 = screen.getByRole('tab', { name: textMock(newNextTab.tabName) }); | ||
await user.click(tab1); | ||
|
||
expect(newNextTab.action.onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('does not call the onClick function when a tab is clicked and action type is link and onClick is not present', async () => { | ||
const user = userEvent.setup(); | ||
render(defaultProps); | ||
|
||
const nextTab = mockTabs[2]; | ||
|
||
const tab3 = screen.getByRole('tab', { name: textMock(nextTab.tabName) }); | ||
await user.click(tab3); | ||
expect(mockOnClick).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('does not call the onClick function when the active tab is clicked', async () => { | ||
const user = userEvent.setup(); | ||
render(defaultProps); | ||
|
||
const currentTab = mockTabs[0]; | ||
|
||
const tab1 = screen.getByRole('tab', { name: textMock(currentTab.tabName) }); | ||
await user.click(tab1); | ||
expect(currentTab.action.onClick).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('displays back button when "upperTab" is backButton and "backButtonHref" and "backButtonText" is present', () => { | ||
render(defaultProps); | ||
|
||
const backButton = screen.getByRole('link', { name: mockBackButtonText }); | ||
expect(backButton).toBeInTheDocument(); | ||
}); | ||
|
||
it('does not display the back button when "upperTab" is backButton and "backButtonHref" or "backButtonText" is not present', () => { | ||
render({ tabs: mockTabs, selectedTab: mockTabId1 }); | ||
|
||
const backButton = screen.queryByRole('link', { name: mockBackButtonText }); | ||
expect(backButton).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('handles tab navigation correctly', async () => { | ||
const user = userEvent.setup(); | ||
render({ tabs: mockTabs, selectedTab: mockTabId1 }); | ||
|
||
await user.tab(); | ||
expect(getTabItem(mockTabId1)).toHaveFocus(); | ||
await user.keyboard('{arrowdown}'); | ||
expect(getTabItem(mockTabId2)).toHaveFocus(); | ||
await user.keyboard('{arrowdown}'); | ||
expect(getTabItem(mockTabId3)).toHaveFocus(); | ||
await user.keyboard('{arrowdown}'); | ||
expect(getTabItem(mockTabId1)).toHaveFocus(); | ||
await user.keyboard('{arrowup}'); | ||
expect(getTabItem(mockTabId3)).toHaveFocus(); | ||
await user.keyboard('{arrowup}'); | ||
expect(getTabItem(mockTabId2)).toHaveFocus(); | ||
}); | ||
|
||
it('selects a tab when pressing "enter"', async () => { | ||
const user = userEvent.setup(); | ||
render({ tabs: mockTabs, selectedTab: mockTabId1 }); | ||
|
||
await user.tab(); | ||
expect(getTabItem(mockTabId1)).toHaveFocus(); | ||
await user.keyboard('{arrowdown}'); | ||
expect(getTabItem(mockTabId2)).toHaveFocus(); | ||
|
||
await user.keyboard('{enter}'); | ||
expect(mockTabs[1].action.onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
const getTabItem = (tabId: string) => { | ||
const tabName: string = `test.test_${tabId}`; | ||
return screen.getByRole('tab', { name: textMock(tabName) }); | ||
}; | ||
|
||
const render = (props: LeftNavigationBarProps) => { | ||
return rtlRender( | ||
<MemoryRouter initialEntries={['/']}> | ||
<LeftNavigationBar {...props} /> | ||
</MemoryRouter>, | ||
); | ||
}; |
Oops, something went wrong.