mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-04 19:30:51 +01:00
Clean up html structure (#1869)
Fix different html syntax errors to improve a11y.
This commit is contained in:
2
gradle/changelog/cleanup_html.yaml
Normal file
2
gradle/changelog/cleanup_html.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: fixed
|
||||
description: Cleanup html errors ([#1869](https://github.com/scm-manager/scm-manager/pull/1869))
|
||||
@@ -36,8 +36,9 @@ const DateFromNow: FC<Props> = ({ className, ...dateProps }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dateTime = formatter.formatFull();
|
||||
return (
|
||||
<DateElement className={className} title={formatter.formatFull()}>
|
||||
<DateElement className={className} dateTime={dateTime} title={dateTime}>
|
||||
{formatter.formatDistance()}
|
||||
</DateElement>
|
||||
);
|
||||
|
||||
@@ -36,8 +36,9 @@ const DateShort: FC<Props> = ({ className, ...dateProps }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dateTime = formatter.formatFull();
|
||||
return (
|
||||
<DateElement className={className} title={formatter.formatFull()}>
|
||||
<DateElement className={className} dateTime={dateTime} title={dateTime}>
|
||||
{formatter.formatShort()}
|
||||
</DateElement>
|
||||
);
|
||||
|
||||
@@ -46,14 +46,14 @@ const Icon: FC<Props> = ({
|
||||
className,
|
||||
onClick,
|
||||
testId,
|
||||
tabIndex = -1,
|
||||
tabIndex,
|
||||
onEnter,
|
||||
alt = title,
|
||||
alt = title
|
||||
}) => {
|
||||
return (
|
||||
<i
|
||||
onClick={onClick}
|
||||
onKeyPress={(event) => event.key === "Enter" && onEnter && onEnter(event)}
|
||||
onKeyPress={event => event.key === "Enter" && onEnter && onEnter(event)}
|
||||
title={title}
|
||||
className={classNames(iconStyle, "fa-fw", "fa-" + name, `has-text-${color}`, className)}
|
||||
tabIndex={tabIndex}
|
||||
|
||||
@@ -52,7 +52,7 @@ const OverviewPageActions: FC<Props> = ({
|
||||
groupSelected,
|
||||
label,
|
||||
testId,
|
||||
searchPlaceholder,
|
||||
searchPlaceholder
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
@@ -63,7 +63,7 @@ const OverviewPageActions: FC<Props> = ({
|
||||
<div className="column is-flex">
|
||||
<Select
|
||||
className="is-fullwidth"
|
||||
options={groups.map((g) => ({ value: g, label: g }))}
|
||||
options={groups.map(g => ({ value: g, label: g }))}
|
||||
value={currentGroup}
|
||||
onChange={groupSelected}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,7 @@ export type TooltipLocation = "bottom" | "right" | "top" | "left";
|
||||
|
||||
class Tooltip extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
location: "right",
|
||||
location: "right"
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -77,6 +77,7 @@ const FilterInput: FC<Props> = ({ filter, value, testId, placeholder, autoFocus,
|
||||
onChange={event => setStateValue(event.target.value)}
|
||||
autoFocus={autoFocus || false}
|
||||
aria-describedby={id}
|
||||
aria-label={t("filterEntries")}
|
||||
{...createAttributesForTesting(testId)}
|
||||
/>
|
||||
<span className="icon is-small is-left">
|
||||
|
||||
@@ -69,7 +69,7 @@ const InnerSelect: FC<FieldProps<BaseProps, HTMLSelectElement, string>> = ({
|
||||
const field = useInnerRef(props.innerRef);
|
||||
|
||||
let opts = options;
|
||||
if (value && addValueToOptions && !options.some((o) => o.value === value)) {
|
||||
if (value && addValueToOptions && !options.some(o => o.value === value)) {
|
||||
opts = [{ label: value, value }, ...options];
|
||||
}
|
||||
|
||||
@@ -124,11 +124,11 @@ const InnerSelect: FC<FieldProps<BaseProps, HTMLSelectElement, string>> = ({
|
||||
onChange={handleInput}
|
||||
onBlur={handleBlur}
|
||||
disabled={disabled}
|
||||
aria-labelledby={a11yId}
|
||||
aria-describedby={helpId}
|
||||
aria-labelledby={label ? a11yId : undefined}
|
||||
aria-describedby={helpText ? helpId : undefined}
|
||||
{...createAttributesForTesting(testId)}
|
||||
>
|
||||
{opts.map((opt) => {
|
||||
{opts.map(opt => {
|
||||
return (
|
||||
<option value={opt.value} key={"KEY_" + opt.value}>
|
||||
{opt.label}
|
||||
|
||||
@@ -37,7 +37,7 @@ const SmallHeader: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
|
||||
const LargeHeader: FC = () => {
|
||||
return (
|
||||
<section className="hero has-scm-background is-small">
|
||||
<div className="hero has-scm-background is-small">
|
||||
<div className="hero-body">
|
||||
<div className="container">
|
||||
<div className="columns is-vcentered">
|
||||
@@ -47,7 +47,7 @@ const LargeHeader: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
||||
aria-label={menuAriaLabel}
|
||||
>
|
||||
{collapsible ? (
|
||||
<Icon color="info" className="is-medium" collapsed={isCollapsed}>
|
||||
<Icon className="is-medium" collapsed={isCollapsed}>
|
||||
{arrowIcon}
|
||||
</Icon>
|
||||
) : null}
|
||||
|
||||
@@ -86,13 +86,13 @@ const RepositoryFlags: FC<Props> = ({ repository, className, tooltipLocation = "
|
||||
);
|
||||
|
||||
return (
|
||||
<span className={classNames("is-flex", "is-align-items-center", className)}>
|
||||
<div className={classNames("is-flex", "is-align-items-center", className)}>
|
||||
{modal}
|
||||
<RepositoryFlagContainer>
|
||||
{repositoryFlags}
|
||||
<ExtensionPoint name="repository.flags" props={{ repository, tooltipLocation }} renderAll={true} />
|
||||
</RepositoryFlagContainer>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@
|
||||
"logout": "Abmelden",
|
||||
"login": "Anmelden",
|
||||
"groups": "Gruppen",
|
||||
"admin": "Administration"
|
||||
"admin": "Administration",
|
||||
"navbarBurger": "Navigation öffnen"
|
||||
},
|
||||
"secondaryNavigation": {
|
||||
"showContent": "Navigation vergrößern",
|
||||
@@ -179,6 +180,8 @@
|
||||
"w_plural": "{{count}} Wochen"
|
||||
},
|
||||
"search": {
|
||||
"ariaLabel": "Globale Suche",
|
||||
"placeholder": "Suche...",
|
||||
"title": "Suche",
|
||||
"subtitle": "{{type}} Ergebnisse für \"{{query}}\"",
|
||||
"types": "Ergebnisse",
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
"logout": "Logout",
|
||||
"login": "Login",
|
||||
"groups": "Groups",
|
||||
"admin": "Administration"
|
||||
"admin": "Administration",
|
||||
"navbarBurger": "Open navigation"
|
||||
},
|
||||
"secondaryNavigation": {
|
||||
"showContent": "Enlarge navigation",
|
||||
@@ -180,6 +181,8 @@
|
||||
"w_plural": "{{count}} weeks"
|
||||
},
|
||||
"search": {
|
||||
"ariaLabel": "Global search",
|
||||
"placeholder": "Search...",
|
||||
"title": "Search",
|
||||
"subtitle": "{{type}} results for \"{{query}}\"",
|
||||
"types": "Results",
|
||||
|
||||
@@ -30,6 +30,7 @@ import Notifications from "./Notifications";
|
||||
import OmniSearch from "./OmniSearch";
|
||||
import LogoutButton from "./LogoutButton";
|
||||
import LoginButton from "./LoginButton";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const StyledMenuBar = styled.div`
|
||||
background-color: transparent !important;
|
||||
@@ -116,6 +117,7 @@ type Props = {
|
||||
|
||||
const NavigationBar: FC<Props> = ({ links }) => {
|
||||
const [burgerActive, setBurgerActive] = useState(false);
|
||||
const [t] = useTranslation("commons");
|
||||
useEffect(() => {
|
||||
const close = () => {
|
||||
if (burgerActive) {
|
||||
@@ -127,7 +129,7 @@ const NavigationBar: FC<Props> = ({ links }) => {
|
||||
}, [burgerActive]);
|
||||
|
||||
return (
|
||||
<StyledNavBar className="navbar mb-0 container" role="navigation" aria-label="main navigation">
|
||||
<StyledNavBar className="navbar mb-0 container" aria-label="main navigation">
|
||||
<div className="navbar-brand">
|
||||
<LogoItem className="navbar-item logo">
|
||||
<Logo withText={false} className="image is-32x32" />
|
||||
@@ -135,7 +137,8 @@ const NavigationBar: FC<Props> = ({ links }) => {
|
||||
<button
|
||||
className={classNames("navbar-burger", { "is-active": burgerActive })}
|
||||
aria-expanded="true"
|
||||
onClick={() => setBurgerActive((active) => !active)}
|
||||
onClick={() => setBurgerActive(active => !active)}
|
||||
aria-label={t("primary-navigation.navbarBurger")}
|
||||
>
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
|
||||
@@ -119,10 +119,10 @@ const HitsList: FC<HitsProps> = ({ hits, index, clear, gotoDetailSearch }) => {
|
||||
return (
|
||||
<>
|
||||
{hits.map((hit, idx) => (
|
||||
<div key={id(hit)} onMouseDown={(e) => e.preventDefault()} onClick={clear}>
|
||||
<div key={id(hit)} onMouseDown={e => e.preventDefault()} onClick={clear}>
|
||||
<Link
|
||||
className={classNames("is-flex", "dropdown-item", "has-text-weight-medium", "is-ellipsis-overflow", {
|
||||
"is-active": idx === index,
|
||||
"is-active": idx === index
|
||||
})}
|
||||
title={id(hit)}
|
||||
to={`/repo/${id(hit)}`}
|
||||
@@ -179,7 +179,7 @@ const useKeyBoardNavigation = (gotoDetailSearch: () => void, clear: () => void,
|
||||
switch (e.which) {
|
||||
case 40: // e.code: ArrowDown
|
||||
if (hits) {
|
||||
setIndex((idx) => {
|
||||
setIndex(idx => {
|
||||
if (idx + 1 < hits.length) {
|
||||
return idx + 1;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ const useKeyBoardNavigation = (gotoDetailSearch: () => void, clear: () => void,
|
||||
break;
|
||||
case 38: // e.code: ArrowUp
|
||||
if (hits) {
|
||||
setIndex((idx) => {
|
||||
setIndex(idx => {
|
||||
if (idx > 0) {
|
||||
return idx - 1;
|
||||
}
|
||||
@@ -219,7 +219,7 @@ const useKeyBoardNavigation = (gotoDetailSearch: () => void, clear: () => void,
|
||||
|
||||
return {
|
||||
onKeyDown,
|
||||
index,
|
||||
index
|
||||
};
|
||||
};
|
||||
|
||||
@@ -278,7 +278,7 @@ const useShowResultsOnFocus = () => {
|
||||
},
|
||||
onKeyPress: () => setShowResults(true),
|
||||
onFocus: () => setShowResults(true),
|
||||
hideResults: () => setShowResults(false),
|
||||
hideResults: () => setShowResults(false)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -303,11 +303,12 @@ const useSearchParams = () => {
|
||||
|
||||
return {
|
||||
searchType,
|
||||
initialQuery,
|
||||
initialQuery
|
||||
};
|
||||
};
|
||||
|
||||
const OmniSearch: FC = () => {
|
||||
const [t] = useTranslation("commons");
|
||||
const { searchType, initialQuery } = useSearchParams();
|
||||
const [query, setQuery] = useState(initialQuery);
|
||||
const debouncedQuery = useDebounce(query, 250);
|
||||
@@ -334,7 +335,7 @@ const OmniSearch: FC = () => {
|
||||
{showHelp ? <SyntaxModal close={closeHelp} /> : null}
|
||||
<div
|
||||
className={classNames("control", "has-icons-right", {
|
||||
"is-loading": isLoading,
|
||||
"is-loading": isLoading
|
||||
})}
|
||||
>
|
||||
<div className={classNames("dropdown", { "is-active": (!!data || error) && showResults })}>
|
||||
@@ -342,13 +343,15 @@ const OmniSearch: FC = () => {
|
||||
<Input
|
||||
className="input is-small"
|
||||
type="text"
|
||||
placeholder="Search ..."
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t("search.placeholder")}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
value={query}
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
data-omnisearch="true"
|
||||
aria-expanded={query.length > 2}
|
||||
aria-label={t("search.ariaLabel")}
|
||||
{...handlers}
|
||||
/>
|
||||
{isLoading ? null : (
|
||||
@@ -357,7 +360,7 @@ const OmniSearch: FC = () => {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<DropdownMenu className="dropdown-menu" onMouseDown={(e) => e.preventDefault()}>
|
||||
<DropdownMenu className="dropdown-menu" onMouseDown={e => e.preventDefault()}>
|
||||
{error ? (
|
||||
<QuickSearchNotification>
|
||||
<SearchErrorNotification error={error} showHelp={openHelp} />
|
||||
|
||||
@@ -35,7 +35,7 @@ const RepositoryGroupEntry: FC<Props> = ({ group }) => {
|
||||
const [t] = useTranslation("namespaces");
|
||||
|
||||
const settingsLink = group.namespace?._links?.permissions && (
|
||||
<Link to={`/namespace/${group.name}/settings`}>
|
||||
<Link to={`/namespace/${group.name}/settings`} aria-label={t("repositoryOverview.settings.tooltip")}>
|
||||
<Icon color="inherit" name="cog" title={t("repositoryOverview.settings.tooltip")} className="is-size-6 ml-2" />
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -33,13 +33,11 @@ type Props = {
|
||||
const FileIcon: FC<Props> = ({ file }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
if (file.subRepository) {
|
||||
return (
|
||||
<Icon title={t("sources.fileTree.subRepository")} iconStyle="far" name="folder" color="inherit" tabIndex={-1} />
|
||||
);
|
||||
return <Icon title={t("sources.fileTree.subRepository")} iconStyle="far" name="folder" color="inherit" />;
|
||||
} else if (file.directory) {
|
||||
return <Icon title={t("sources.fileTree.folder")} name="folder" color="inherit" tabIndex={-1} />;
|
||||
return <Icon title={t("sources.fileTree.folder")} name="folder" color="inherit" />;
|
||||
}
|
||||
return <Icon title={t("sources.fileTree.file")} name="file" color="inherit" tabIndex={-1} />;
|
||||
return <Icon title={t("sources.fileTree.file")} name="file" color="inherit" />;
|
||||
};
|
||||
|
||||
export default FileIcon;
|
||||
|
||||
@@ -55,9 +55,7 @@ class UserRow extends React.Component<Props> {
|
||||
{iconType} {this.renderLink(to, user.name)}
|
||||
</td>
|
||||
<td className="is-hidden-mobile">{this.renderLink(to, user.displayName)}</td>
|
||||
<td>
|
||||
<a href={`mailto:${user.mail}`}>{user.mail}</a>
|
||||
</td>
|
||||
<td>{user.mail ? <a href={`mailto:${user.mail}`}>{user.mail}</a> : null}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user