Skip to content
Open
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
7 changes: 7 additions & 0 deletions docs/adr/0001-modal-open-state-suppresses-global-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
status: proposed
---

# Suppress global page error while any modal is open

To prevent visual conflict between modal content and page-level errors, the application tracks open modal state globally and suppresses rendering of the top-level Error component while at least one modal is open. We chose this over a ConfigureGit-only fix because the problem is cross-cutting, and we chose explicit modal lifecycle dispatch over ad-hoc button handlers because it is more resilient to close/unmount edge cases.
51 changes: 51 additions & 0 deletions src/components/InformationBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { render, screen } from '@testing-library/react'
import InformationBanner from './InformationBanner'
import '@testing-library/jest-dom'

jest.mock(
'./Iconify',
() =>
function MockIconify(props: any) {
return <div data-testid='iconify' {...props} />
},
)

describe('InformationBanner', () => {
it('renders the message', () => {
render(<InformationBanner message='This is information' />)

expect(screen.getByText('This is information')).toBeTruthy()
})

it('defaults to info type', () => {
render(<InformationBanner message='Default info banner' />)

expect(screen.getByTestId('iconify')).toHaveAttribute('icon', 'material-symbols:info')
expect(screen.getByTestId('iconify')).toHaveAttribute('color', '#c7d030d9')
expect(screen.getByTestId('iconify')).toHaveAttribute('width', '40')
})

it('renders error type', () => {
render(<InformationBanner type='error' message='Error banner' />)

expect(screen.getByTestId('iconify')).toHaveAttribute('icon', 'material-symbols:error-rounded')
expect(screen.getByTestId('iconify')).toHaveAttribute('color', '#d32f2f')
expect(screen.getByTestId('iconify')).toHaveAttribute('width', '30')
})

it('renders children', () => {
render(
<InformationBanner message='With child'>
<button type='button'>Action</button>
</InformationBanner>,
)

expect(screen.getByText('Action')).toBeTruthy()
})

it('renders a React node message', () => {
render(<InformationBanner message={<span>Node message</span>} />)

expect(screen.getByText('Node message')).toBeTruthy()
})
})
25 changes: 19 additions & 6 deletions src/components/InformationBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import { Box, SxProps, Typography, styled, useTheme } from '@mui/material'
import React from 'react'
import Iconify from './Iconify'

const StyledInfoBanner = styled(Box)<{ small?: boolean }>(({ theme, small }) => ({
backgroundColor: '#f2f2894d',
type InformationBannerType = 'info' | 'error'

const StyledInfoBanner = styled(Box)<{
small?: boolean
type: InformationBannerType
}>(({ small, type }) => ({
Comment thread
dennisvankekem marked this conversation as resolved.
Comment thread
dennisvankekem marked this conversation as resolved.
Comment thread
dennisvankekem marked this conversation as resolved.
backgroundColor: type === 'error' ? '#722e38' : '#f2f2894d',
padding: small ? '5px' : '10px',
border: '1px solid #d4d402',
border: `1px solid ${type === 'error' ? '#d32f2f' : '#d4d402'}`,
Comment on lines +7 to +13
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
Expand All @@ -17,13 +22,21 @@ interface Props {
small?: boolean
children?: React.ReactNode
sx?: SxProps
type?: InformationBannerType
}

export default function InformationBanner({ message, children, small, sx }: Props) {
export default function InformationBanner({ message, children, small, sx, type = 'info' }: Props) {
const theme = useTheme()

const icon = type === 'error' ? 'material-symbols:error-rounded' : 'material-symbols:info'

const iconColor = type === 'error' ? '#d32f2f' : '#c7d030d9'

const width = type === 'error' ? 30 : 40

return (
<StyledInfoBanner small={small} sx={{ ...sx }}>
<Iconify icon='material-symbols:info' width={40} height={28} color='#c7d030d9' />
<StyledInfoBanner small={small} type={type} sx={sx}>
<Iconify icon={icon} width={width} height={28} color={iconColor} />
<Typography sx={{ color: theme.palette.text.primary, ml: 1 }}>{message}</Typography>
Comment thread
dennisvankekem marked this conversation as resolved.
{children}
</StyledInfoBanner>
Expand Down
Loading
Loading