1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00

Chore/remove badges from tab order (#9643)

Makes badges not tabbable by default instead of tabbable by default.
Turns out, badges aren't tabbable by default and they never were until I
made them as much (for some reason that I don't quite understand now).

Anyway, I've gone through the list of uses for the Badge element and
made any element that should be reachable by tab either have an explicit
tab index (if it's within a tooltip, for instance), or be wrapped in a
Link (instead of having an on-click handler). The two places I've
wrapped it in a link, I've also gone and changed the item group to be a
list (for HTML semantics). I've also updated some spacing for the
profile tab.

Application list (one is before, one is after. don't remember which is
which; it's now a list):

![image](https://github.com/user-attachments/assets/66fdabf7-7a80-46cb-b302-6242c16ad43e)

![image](https://github.com/user-attachments/assets/660506ce-0ec4-417f-bb3a-3fbf23d5591c)


Profile page (now a list + improved spacing)

Before:

![image](https://github.com/user-attachments/assets/17a841e6-d500-410e-8f11-4c78ca0e9e12)

![image](https://github.com/user-attachments/assets/38a60e67-682e-45b5-9312-f4c2013192bb)

After:

![image](https://github.com/user-attachments/assets/602f3d06-0b7e-4a10-9958-fb565fb6d06b)


![image](https://github.com/user-attachments/assets/67c7d39c-cdf9-4586-98d9-63ceff4fd867)
This commit is contained in:
Thomas Heartman 2025-03-28 16:05:32 +01:00 committed by GitHub
parent fc0383620b
commit 398246c3ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 80 additions and 55 deletions

View File

@ -88,7 +88,6 @@ export const RequestSummary: FC<Props> = ({
: 'error' : 'error'
: 'neutral' : 'neutral'
} }
tabIndex={-1}
> >
{usageTotal.toLocaleString( {usageTotal.toLocaleString(
locationSettings.locale ?? 'en-US', locationSettings.locale ?? 'en-US',

View File

@ -14,7 +14,7 @@ import { ApplicationIssues } from './ApplicationIssues/ApplicationIssues';
import { ApplicationChart } from './ApplicationChart'; import { ApplicationChart } from './ApplicationChart';
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined'; import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
import { Badge } from '../common/Badge/Badge'; import { Badge } from '../common/Badge/Badge';
import { useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useFeedback } from '../feedbackNew/useFeedback'; import { useFeedback } from '../feedbackNew/useFeedback';
@ -35,11 +35,22 @@ const ApplicationContainer = styled(Box)(({ theme }) => ({
alignSelf: 'stretch', alignSelf: 'stretch',
})); }));
const ProjectContainer = styled(Box)(({ theme }) => ({ const ProjectContainer = styled('ul')(({ theme }) => ({
padding: 0,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: theme.spacing(2), gap: theme.spacing(2),
alignSelf: 'stretch', alignSelf: 'stretch',
listStyle: 'none',
}));
const StyledBadgeLink = styled(Link)(({ theme }) => ({
':hover,:focus-visible': {
outline: 'none',
'> *': {
outline: `1px solid ${theme.palette.primary.main}`,
},
},
})); }));
const ApplicationHeader = styled('div')(({ theme }) => ({ const ApplicationHeader = styled('div')(({ theme }) => ({
@ -97,18 +108,18 @@ const ApplicationOverview = () => {
<ProjectContainer> <ProjectContainer>
Application is connected to these projects: Application is connected to these projects:
{data.projects.map((project) => ( {data.projects.map((project) => (
<Badge <li key={project}>
sx={{ cursor: 'pointer' }} <StyledBadgeLink
key={project} to={`/projects/${project}`}
onClick={(e) => { >
e.preventDefault(); <Badge
navigate(`/projects/${project}`); color='secondary'
}} icon={<TopicOutlinedIcon />}
color='secondary' >
icon={<TopicOutlinedIcon />} {project}
> </Badge>
{project} </StyledBadgeLink>
</Badge> </li>
))} ))}
</ProjectContainer> </ProjectContainer>
<Button <Button

View File

@ -97,14 +97,12 @@ export const Badge: FC<IBadgeProps> = forwardRef(
className, className,
sx, sx,
children, children,
tabIndex = 0,
...props ...props
}: IBadgeProps, }: IBadgeProps,
ref: ForwardedRef<HTMLDivElement>, ref: ForwardedRef<HTMLDivElement>,
) => ( ) => (
<StyledBadge <StyledBadge
as={as} as={as}
tabIndex={tabIndex}
color={color} color={color}
icon={icon} icon={icon}
className={className} className={className}

View File

@ -27,7 +27,12 @@ export const RoleBadge = ({ roleId, hideIcon, children }: IRoleBadgeProps) => {
return ( return (
<HtmlTooltip title={<RoleDescription roleId={roleId} tooltip />} arrow> <HtmlTooltip title={<RoleDescription roleId={roleId} tooltip />} arrow>
<Badge color='success' icon={icon} sx={{ cursor: 'pointer' }}> <Badge
tabIndex={0}
color='success'
icon={icon}
sx={{ cursor: 'pointer' }}
>
{role.name} {role.name}
</Badge> </Badge>
</HtmlTooltip> </HtmlTooltip>

View File

@ -342,7 +342,9 @@ export const PrimaryFeatureInfo: FC<{
/> />
{archivedAt && ( {archivedAt && (
<HtmlTooltip arrow title={archivedDate} describeChild> <HtmlTooltip arrow title={archivedDate} describeChild>
<Badge color='neutral'>Archived</Badge> <Badge tabIndex={0} color='neutral'>
Archived
</Badge>
</HtmlTooltip> </HtmlTooltip>
)} )}
</FeatureNameAndType> </FeatureNameAndType>

View File

@ -117,13 +117,11 @@ export const FeatureDetails = ({
<p> <p>
{feature?.strategies?.result !== 'unknown' ? ( {feature?.strategies?.result !== 'unknown' ? (
<PlaygroundResultChip <PlaygroundResultChip
tabindex={-1}
enabled={feature.isEnabled} enabled={feature.isEnabled}
label={feature.isEnabled ? 'True' : 'False'} label={feature.isEnabled ? 'True' : 'False'}
/> />
) : ( ) : (
<PlaygroundResultChip <PlaygroundResultChip
tabindex={-1}
enabled='unknown' enabled='unknown'
label={'Unknown'} label={'Unknown'}
showIcon={false} showIcon={false}

View File

@ -14,14 +14,12 @@ interface IResultChipProps {
label: string; label: string;
// Result icon - defaults to true // Result icon - defaults to true
showIcon?: boolean; showIcon?: boolean;
tabindex?: number;
} }
export const PlaygroundResultChip: VFC<IResultChipProps> = ({ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
enabled, enabled,
label, label,
showIcon = true, showIcon = true,
tabindex,
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const flagOverviewRedesign = useUiFlag('flagOverviewRedesign'); const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
@ -73,7 +71,6 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
condition={typeof enabled === 'boolean' && Boolean(enabled)} condition={typeof enabled === 'boolean' && Boolean(enabled)}
show={ show={
<Badge <Badge
tabIndex={tabindex}
color='success' color='success'
icon={showIcon ? icon : undefined} icon={showIcon ? icon : undefined}
> >
@ -81,11 +78,7 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
</Badge> </Badge>
} }
elseShow={ elseShow={
<Badge <Badge color='error' icon={showIcon ? icon : undefined}>
color='error'
icon={showIcon ? icon : undefined}
tabIndex={tabindex}
>
{label} {label}
</Badge> </Badge>
} }

View File

@ -32,7 +32,6 @@ export const PlaygroundResultChip: FC<ResultChipProps> = ({
return ( return (
<Badge <Badge
tabIndex={-1}
color={color} color={color}
icon={ icon={
showIcon ? ( showIcon ? (

View File

@ -15,7 +15,7 @@ import { useProfile } from 'hooks/api/getters/useProfile/useProfile';
import { useLocationSettings } from 'hooks/useLocationSettings'; import { useLocationSettings } from 'hooks/useLocationSettings';
import type { IUser } from 'interfaces/user'; import type { IUser } from 'interfaces/user';
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined'; import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
import { useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { RoleBadge } from 'component/common/RoleBadge/RoleBadge'; import { RoleBadge } from 'component/common/RoleBadge/RoleBadge';
@ -56,14 +56,19 @@ const StyledSectionLabel = styled(Typography)(({ theme }) => ({
const StyledAccess = styled('div')(({ theme }) => ({ const StyledAccess = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
gap: theme.spacing(2),
'& > div > p': { '& > div > p': {
marginBottom: theme.spacing(1.5), marginBottom: theme.spacing(1.5),
}, },
})); }));
const StyledBadge = styled(Badge)(({ theme }) => ({ const StyledBadgeLink = styled(Link)(({ theme }) => ({
cursor: 'pointer', ':hover,:focus-visible': {
marginRight: theme.spacing(1), outline: 'none',
'> *': {
outline: `2px solid ${theme.palette.primary.main}`,
},
},
})); }));
const StyledDivider = styled('div')(({ theme }) => ({ const StyledDivider = styled('div')(({ theme }) => ({
@ -86,6 +91,14 @@ interface IProfileTabProps {
user: IUser; user: IUser;
} }
const ProjectList = styled('ul')(({ theme }) => ({
listStyle: 'none',
padding: 0,
display: 'flex',
flexFlow: 'row wrap',
gap: theme.spacing(1),
}));
export const ProfileTab = ({ user }: IProfileTabProps) => { export const ProfileTab = ({ user }: IProfileTabProps) => {
const { profile, refetchProfile } = useProfile(); const { profile, refetchProfile } = useProfile();
const navigate = useNavigate(); const navigate = useNavigate();
@ -158,33 +171,40 @@ export const ProfileTab = ({ user }: IProfileTabProps) => {
<Typography variant='body2'>Projects</Typography> <Typography variant='body2'>Projects</Typography>
<ConditionallyRender <ConditionallyRender
condition={Boolean(profile?.projects.length)} condition={Boolean(profile?.projects.length)}
show={profile?.projects.map((project) => ( show={
<Tooltip <ProjectList>
key={project} {profile?.projects.map((project) => (
title='View project' <li key={project}>
arrow <Tooltip
placement='bottom-end' title='View project'
describeChild arrow
> placement='bottom-end'
<StyledBadge describeChild
onClick={(e) => { >
e.preventDefault(); <StyledBadgeLink
navigate(`/projects/${project}`); to={`/projects/${project}`}
}} >
color='secondary' <Badge
icon={<TopicOutlinedIcon />} color='secondary'
> icon={
{project} <TopicOutlinedIcon />
</StyledBadge> }
</Tooltip> >
))} {project}
</Badge>
</StyledBadgeLink>
</Tooltip>
</li>
))}
</ProjectList>
}
elseShow={ elseShow={
<Tooltip <Tooltip
title='You are not assigned to any projects' title='You are not assigned to any projects'
arrow arrow
describeChild describeChild
> >
<Badge>No projects</Badge> <Badge tabIndex={0}>No projects</Badge>
</Tooltip> </Tooltip>
} }
/> />