@@ -28,64 +28,51 @@ import { FlagErrorIcon, FlagInfoIcon, FlagSuccessIcon, FlagWarningIcon } from '. | |||
export type Variant = 'error' | 'warning' | 'success' | 'info'; | |||
interface Props { | |||
ariaLabel: string; | |||
variant: Variant; | |||
} | |||
interface VariantInformation { | |||
backGroundColor: ThemeColors; | |||
borderColor: ThemeColors; | |||
icon: JSX.Element; | |||
role: string; | |||
} | |||
function getVariantInfo(variant: Variant): VariantInformation { | |||
const variantList: Record<Variant, VariantInformation> = { | |||
function getVariantInfo(variant: Variant) { | |||
const variantList = { | |||
error: { | |||
icon: <FlagErrorIcon />, | |||
borderColor: 'errorBorder', | |||
backGroundColor: 'errorBackground', | |||
role: 'alert', | |||
}, | |||
warning: { | |||
icon: <FlagWarningIcon />, | |||
borderColor: 'warningBorder', | |||
backGroundColor: 'warningBackground', | |||
role: 'alert', | |||
}, | |||
success: { | |||
icon: <FlagSuccessIcon />, | |||
borderColor: 'successBorder', | |||
backGroundColor: 'successBackground', | |||
role: 'status', | |||
}, | |||
info: { | |||
icon: <FlagInfoIcon />, | |||
borderColor: 'infoBorder', | |||
backGroundColor: 'infoBackground', | |||
role: 'status', | |||
}, | |||
}; | |||
} as const; | |||
return variantList[variant]; | |||
} | |||
export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>) { | |||
const { ariaLabel, className, variant, ...domProps } = props; | |||
const { className, variant, ...domProps } = props; | |||
const variantInfo = getVariantInfo(variant); | |||
return ( | |||
<StyledFlag | |||
aria-label={ariaLabel} | |||
backGroundColor={variantInfo.backGroundColor} | |||
borderColor={variantInfo.borderColor} | |||
className={classNames('alert', className)} | |||
role={variantInfo.role} | |||
variantInfo={variantInfo} | |||
{...domProps} | |||
> | |||
<StyledFlagInner> | |||
<StyledFlagIcon variantInfo={variantInfo}>{variantInfo.icon}</StyledFlagIcon> | |||
<StyledFlagContent>{props.children}</StyledFlagContent> | |||
</StyledFlagInner> | |||
<div className="flag-inner"> | |||
<div className="flag-icon">{variantInfo.icon}</div> | |||
<div className="flag-content">{props.children}</div> | |||
</div> | |||
</StyledFlag> | |||
); | |||
} | |||
@@ -93,32 +80,33 @@ export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>) | |||
FlagMessage.displayName = 'FlagMessage'; // so that tests don't see the obfuscated production name | |||
export const StyledFlag = styled.div<{ | |||
variantInfo: VariantInformation; | |||
backGroundColor: ThemeColors; | |||
borderColor: ThemeColors; | |||
}>` | |||
${tw`sw-inline-flex`} | |||
${tw`sw-min-h-10`} | |||
${tw`sw-rounded-1`} | |||
border: ${({ variantInfo }) => themeBorder('default', variantInfo.borderColor)}; | |||
border: ${({ borderColor }) => themeBorder('default', borderColor)}; | |||
background-color: ${themeColor('flagMessageBackground')}; | |||
`; | |||
const StyledFlagInner = styled.div` | |||
${tw`sw-flex sw-items-stretch`} | |||
${tw`sw-box-border`} | |||
`; | |||
& > .flag-inner { | |||
${tw`sw-flex sw-items-stretch`} | |||
${tw`sw-box-border`} | |||
} | |||
const StyledFlagIcon = styled.div<{ variantInfo: VariantInformation }>` | |||
${tw`sw-flex sw-justify-center sw-items-center`} | |||
${tw`sw-rounded-l-1`} | |||
${tw`sw-px-3`} | |||
background-color: ${({ variantInfo }) => themeColor(variantInfo.backGroundColor)}; | |||
`; | |||
& .flag-icon { | |||
${tw`sw-flex sw-justify-center sw-items-center`} | |||
${tw`sw-rounded-l-1`} | |||
${tw`sw-px-3`} | |||
background-color: ${({ backGroundColor }) => themeColor(backGroundColor)}; | |||
} | |||
const StyledFlagContent = styled.div` | |||
${tw`sw-flex sw-flex-auto sw-items-center`} | |||
${tw`sw-overflow-auto`} | |||
${tw`sw-text-left`} | |||
${tw`sw-mx-3 sw-my-2`} | |||
${tw`sw-body-sm`} | |||
color: ${themeContrast('flagMessageBackground')}; | |||
& .flag-content { | |||
${tw`sw-flex sw-flex-auto sw-items-center`} | |||
${tw`sw-overflow-auto`} | |||
${tw`sw-text-left`} | |||
${tw`sw-mx-3 sw-my-2`} | |||
${tw`sw-body-sm`} | |||
color: ${themeContrast('flagMessageBackground')}; | |||
} | |||
`; |
@@ -23,21 +23,21 @@ import { FCProps } from '../../types/misc'; | |||
import { FlagMessage, Variant } from '../FlagMessage'; | |||
it.each([ | |||
['error', 'alert', '1px solid rgb(249,112,102)'], | |||
['warning', 'alert', '1px solid rgb(248,205,92)'], | |||
['success', 'status', '1px solid rgb(50,213,131)'], | |||
['info', 'status', '1px solid rgb(110,185,228)'], | |||
])('should render properly for "%s" variant', (variant: Variant, expectedRole, color) => { | |||
['error', '1px solid rgb(249,112,102)'], | |||
['warning', '1px solid rgb(248,205,92)'], | |||
['success', '1px solid rgb(50,213,131)'], | |||
['info', '1px solid rgb(110,185,228)'], | |||
])('should render properly for "%s" variant', (variant: Variant, color) => { | |||
renderFlagMessage({ variant }); | |||
const item = screen.getByRole(expectedRole); | |||
const item = screen.getByRole('status'); | |||
expect(item).toBeInTheDocument(); | |||
expect(item).toHaveStyle({ border: color }); | |||
}); | |||
function renderFlagMessage(props: Partial<FCProps<typeof FlagMessage>> = {}) { | |||
return render( | |||
<FlagMessage ariaLabel="label" variant="error" {...props}> | |||
<FlagMessage role="status" variant="error" {...props}> | |||
This is an error! | |||
</FlagMessage> | |||
); |
@@ -63,7 +63,7 @@ export function AnalysisStatus(props: HeaderMetaProps) { | |||
if (currentTask?.status === TaskStatuses.Failed) { | |||
return ( | |||
<> | |||
<FlagMessage ariaLabel={translate('alert.tooltip.error')} variant="error"> | |||
<FlagMessage variant="error"> | |||
<span>{translate('project_navigation.analysis_status.failed')}</span> | |||
<Link className="sw-ml-1" blurAfterClick onClick={openModal} preventDefault to={{}}> | |||
{translate('project_navigation.analysis_status.details_link')} | |||
@@ -84,7 +84,7 @@ export function AnalysisStatus(props: HeaderMetaProps) { | |||
if (warnings.length > 0) { | |||
return ( | |||
<> | |||
<FlagMessage ariaLabel={translate('alert.tooltip.warning')} variant="warning"> | |||
<FlagMessage variant="warning"> | |||
<span>{translate('project_navigation.analysis_status.warnings')}</span> | |||
<Link className="sw-ml-1" blurAfterClick onClick={openModal} preventDefault to={{}}> | |||
{translate('project_navigation.analysis_status.details_link')} |
@@ -120,11 +120,7 @@ export default function CodeAppRenderer(props: Props) { | |||
<A11ySkipTarget anchor="code_main" /> | |||
{!canBrowseAllChildProjects && isPortfolio && ( | |||
<FlagMessage | |||
ariaLabel={translate('code_viewer.not_all_measures_are_shown')} | |||
variant="warning" | |||
className="it__portfolio_warning sw-mb-4" | |||
> | |||
<FlagMessage variant="warning" className="it__portfolio_warning sw-mb-4"> | |||
{translate('code_viewer.not_all_measures_are_shown')} | |||
<HelpTooltip | |||
className="sw-ml-2" |
@@ -475,7 +475,7 @@ function getPageObject() { | |||
viewSelect: byLabelText('component_measures.view_as'), | |||
emptyText: byText('component_measures.empty'), | |||
detailsUnavailableText: byText('component_measures.details_are_not_available'), | |||
noAccessWarning: byRole('alert'), | |||
noAccessWarning: byText('component_measures.not_all_measures_are_shown'), | |||
showingOutOfTxt: (x: string, y: string) => byText(`x_of_y_shown.${x}.${y}`), | |||
showAllBtn: byRole('button', { | |||
name: 'component_measures.hidden_best_score_metrics_show_label', |
@@ -181,18 +181,7 @@ export default class FilesView extends React.PureComponent<Props, State> { | |||
view={this.props.view} | |||
/> | |||
{hidingBestMeasures && this.props.paging && ( | |||
<FlagMessage | |||
ariaLabel={translateWithParameters( | |||
'component_measures.hidden_best_score_metrics', | |||
formatMeasure( | |||
this.props.paging.total - filteredComponents.length, | |||
MetricType.Integer | |||
), | |||
formatMeasure(this.props.metric.bestValue, this.props.metric.type) | |||
)} | |||
variant="info" | |||
className="sw-mt-4" | |||
> | |||
<FlagMessage variant="info" className="sw-mt-4"> | |||
{translateWithParameters( | |||
'component_measures.hidden_best_score_metrics', | |||
formatMeasure( |
@@ -90,11 +90,7 @@ export default function Sidebar(props: Props) { | |||
}} | |||
> | |||
{!canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( | |||
<FlagMessage | |||
ariaLabel={translate('component_measures.not_all_measures_are_shown')} | |||
className="sw-mt-4 it__portfolio_warning" | |||
variant="warning" | |||
> | |||
<FlagMessage className="sw-mt-4 it__portfolio_warning" variant="warning"> | |||
{translate('component_measures.not_all_measures_are_shown')} | |||
<HelpTooltip | |||
className="spacer-left" |
@@ -437,11 +437,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> { | |||
<DeferredSpinner loading={loading}> | |||
<form id="bulk-change-form" onSubmit={this.handleSubmit}> | |||
{limitReached && ( | |||
<FlagMessage | |||
ariaLabel={translate('alert.tooltip.warning')} | |||
className="sw-mb-4" | |||
variant="warning" | |||
> | |||
<FlagMessage className="sw-mb-4" variant="warning"> | |||
<span> | |||
<FormattedMessage | |||
defaultMessage={translate('issue_bulk_change.max_issues_reached')} | |||
@@ -461,9 +457,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> { | |||
{this.renderCommentField()} | |||
{issues.length > 0 && this.renderNotificationsField()} | |||
{issues.length === 0 && ( | |||
<FlagMessage ariaLabel={translate('alert.tooltip.warning')} variant="warning"> | |||
{translate('issue_bulk_change.no_match')} | |||
</FlagMessage> | |||
<FlagMessage variant="warning">{translate('issue_bulk_change.no_match')}</FlagMessage> | |||
)} | |||
</form> | |||
</DeferredSpinner> |
@@ -1031,7 +1031,6 @@ export class App extends React.PureComponent<Props, State> { | |||
const warning = !canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( | |||
<FlagMessage | |||
ariaLabel={translate('issues.not_all_issue_show')} | |||
className="it__portfolio_warning sw-flex" | |||
title={translate('issues.not_all_issue_show_why')} | |||
variant="warning" | |||
@@ -1236,10 +1235,7 @@ export class App extends React.PureComponent<Props, State> { | |||
) : ( | |||
<DeferredSpinner loading={loading} ariaLabel={translate('issues.loading_issues')}> | |||
{checkAll && paging && paging.total > MAX_PAGE_SIZE && ( | |||
<FlagMessage | |||
ariaLabel={translate('issue_bulk_change.max_issues_reached')} | |||
variant="warning" | |||
> | |||
<FlagMessage variant="warning"> | |||
<span> | |||
<FormattedMessage | |||
defaultMessage={translate('issue_bulk_change.max_issues_reached')} | |||
@@ -1251,14 +1247,7 @@ export class App extends React.PureComponent<Props, State> { | |||
)} | |||
{cannotShowOpenIssue && (!paging || paging.total > 0) && ( | |||
<FlagMessage | |||
ariaLabel={translateWithParameters( | |||
'issues.cannot_open_issue_max_initial_X_fetched', | |||
MAX_INITAL_FETCH | |||
)} | |||
className="sw-mb-4" | |||
variant="warning" | |||
> | |||
<FlagMessage className="sw-mb-4" variant="warning"> | |||
{translateWithParameters( | |||
'issues.cannot_open_issue_max_initial_X_fetched', | |||
MAX_INITAL_FETCH |
@@ -281,11 +281,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone | |||
return ( | |||
<> | |||
{issueIsClosed && ( | |||
<FlagMessage | |||
className="sw-mb-2 sw-flex" | |||
variant="success" | |||
ariaLabel={translate(closedIssueMessageKey)} | |||
> | |||
<FlagMessage className="sw-mb-2 sw-flex" variant="success"> | |||
<div className="sw-block"> | |||
<FormattedMessage | |||
id={closedIssueMessageKey} |
@@ -335,11 +335,7 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> { | |||
/> | |||
{mightHaveMoreResults && this.state.showFullList && ( | |||
<FlagMessage | |||
ariaLabel={translate('facet_might_have_more_results')} | |||
className="sw-flex sw-my-4" | |||
variant="warning" | |||
> | |||
<FlagMessage className="sw-flex sw-my-4" variant="warning"> | |||
{translate('facet_might_have_more_results')} | |||
</FlagMessage> | |||
)} | |||
@@ -382,11 +378,7 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> { | |||
</FacetItemsList> | |||
{searchMaxResults && ( | |||
<FlagMessage | |||
ariaLabel={translate('facet_might_have_more_results')} | |||
className="sw-flex sw-my-4" | |||
variant="warning" | |||
> | |||
<FlagMessage className="sw-flex sw-my-4" variant="warning"> | |||
{translate('facet_might_have_more_results')} | |||
</FlagMessage> | |||
)} |
@@ -38,13 +38,7 @@ export default function ApplicationNonCaycProjectWarning({ projects, caycStatus | |||
return ( | |||
<Card className="sw-mt-4 sw-body-sm"> | |||
{caycStatus === CaycStatus.NonCompliant ? ( | |||
<FlagMessage | |||
ariaLabel={translateWithParameters( | |||
'overview.quality_gate.application.non_cayc.projects_x', | |||
projects.length | |||
)} | |||
variant="warning" | |||
> | |||
<FlagMessage variant="warning"> | |||
{translateWithParameters( | |||
'overview.quality_gate.application.non_cayc.projects_x', | |||
projects.length |
@@ -34,10 +34,7 @@ export default function CleanAsYouCodeWarning({ component }: Props) { | |||
return ( | |||
<> | |||
<FlagMessage | |||
ariaLabel={translate('overview.quality_gate.conditions.cayc.warning')} | |||
variant="warning" | |||
> | |||
<FlagMessage variant="warning"> | |||
{translate('overview.quality_gate.conditions.cayc.warning')} | |||
</FlagMessage> | |||
{component.qualityGate ? ( |
@@ -83,11 +83,7 @@ export function NoCodeWarning({ branchLike, component, measures }: Props) { | |||
} | |||
/* eslint-enable no-lonely-if */ | |||
return ( | |||
<FlagMessage ariaLabel={title} variant="warning"> | |||
{title} | |||
</FlagMessage> | |||
); | |||
return <FlagMessage variant="warning">{title}</FlagMessage>; | |||
} | |||
export default React.memo(NoCodeWarning); |
@@ -25,11 +25,7 @@ import { translate } from '../../../helpers/l10n'; | |||
export default function IgnoredConditionWarning() { | |||
return ( | |||
<FlagMessage | |||
ariaLabel={translate('overview.quality_gate.ignored_conditions')} | |||
className="sw-mb-4" | |||
variant="info" | |||
> | |||
<FlagMessage className="sw-mb-4" variant="info"> | |||
<span>{translate('overview.quality_gate.ignored_conditions')}</span> | |||
<HelpTooltip | |||
className="sw-ml-2" |
@@ -112,10 +112,7 @@ export default class DuplicationPopup extends PureComponent<Props> { | |||
return ( | |||
<div className="sw-w-abs-400"> | |||
{inRemovedComponent && ( | |||
<FlagMessage | |||
ariaLabel={translate('duplications.dups_found_on_deleted_resource')} | |||
variant="warning" | |||
> | |||
<FlagMessage variant="warning"> | |||
{translate('duplications.dups_found_on_deleted_resource')} | |||
</FlagMessage> | |||
)} |
@@ -128,14 +128,7 @@ export default class RuleDescription extends React.PureComponent<Props, State> { | |||
{translate('coding_rules.description_context.title')} | |||
</h2> | |||
{defaultContext && ( | |||
<FlagMessage | |||
variant="info" | |||
className="sw-mb-4" | |||
ariaLabel={translateWithParameters( | |||
'coding_rules.description_context.default_information', | |||
defaultContext.displayName | |||
)} | |||
> | |||
<FlagMessage variant="info" className="sw-mb-4"> | |||
{translateWithParameters( | |||
'coding_rules.description_context.default_information', | |||
defaultContext.displayName |