mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-01-23 07:52:13 +01:00
Add alternative text to controls to allow screen readers to read them aloud (#1840)
Add alternative text to controls to allow screen readers to read them aloud. Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
2
gradle/changelog/alt_tags.yaml
Normal file
2
gradle/changelog/alt_tags.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: added
|
||||
descripion: Add alternative text to controls to allow screen readers to read them aloud ([#1840](https://github.com/scm-manager/scm-manager/pull/1840))
|
||||
@@ -44,7 +44,7 @@ const baseUrl = "scm-manager.org/scm/repo/hitchhiker/heartOfGold/sources";
|
||||
const sources = Git;
|
||||
const prefix = (
|
||||
<a href="#link">
|
||||
<Icon name="heart" color="danger" />
|
||||
<Icon name="heart" color="danger" alt="heart icon" />
|
||||
</a>
|
||||
);
|
||||
|
||||
|
||||
@@ -194,10 +194,10 @@ const Breadcrumb: FC<Props> = ({
|
||||
</ul>
|
||||
<PermaLinkWrapper className="ml-1">
|
||||
{copying ? (
|
||||
<Icon name="spinner fa-spin" />
|
||||
<Icon name="spinner fa-spin" alt={t("breadcrumb.loading")} />
|
||||
) : (
|
||||
<Tooltip message={t("breadcrumb.copyPermalink")}>
|
||||
<Icon name="link" color="inherit" onClick={() => copySource()} />
|
||||
<Icon name="link" color="inherit" onClick={() => copySource()} alt={t("breadcrumb.copyPermalink")} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</PermaLinkWrapper>
|
||||
|
||||
@@ -36,7 +36,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const link = "/foo/bar";
|
||||
const avatar = <Icon name="icons fa-2x fa-fw" />;
|
||||
const avatar = <Icon name="icons fa-2x fa-fw" alt="avatar" />;
|
||||
const title = <strong>title</strong>;
|
||||
const footerLeft = <small>left footer</small>;
|
||||
const footerRight = <small>right footer</small>;
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import Icon from "./Icon";
|
||||
import { withTranslation, WithTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
type Props = WithTranslation & {
|
||||
name: ReactNode;
|
||||
url?: string;
|
||||
elements: ReactNode[];
|
||||
@@ -35,7 +37,7 @@ type State = {
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
class CardColumnGroup extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -62,12 +64,13 @@ export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { name, url, elements } = this.props;
|
||||
const { name, url, elements, t } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||
let icon = <Icon name="angle-right" color="inherit" alt={t("cardColumnGroup.showContent")} />;
|
||||
let content = null;
|
||||
if (!collapsed) {
|
||||
icon = <Icon name="angle-down" color="inherit" alt={t("cardColumnGroup.hideContent")} />;
|
||||
content = elements.map((entry, index) => {
|
||||
const fullColumnWidth = this.isFullSize(elements, index);
|
||||
const sizeClass = fullColumnWidth ? "is-full" : "is-half";
|
||||
@@ -83,7 +86,7 @@ export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
<div className="mb-4">
|
||||
<h2>
|
||||
<span className={classNames("is-size-4", "is-clickable")} onClick={this.toggleCollapse}>
|
||||
<i className={classNames("fa", icon)} />
|
||||
{icon}
|
||||
</span>{" "}
|
||||
{url ? (
|
||||
<Link to={url} className="has-text-dark">
|
||||
@@ -100,3 +103,5 @@ export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("commons")(CardColumnGroup);
|
||||
|
||||
@@ -34,7 +34,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const link = "/foo/bar";
|
||||
const icon = <Icon name="icons fa-2x fa-fw" />;
|
||||
const avatar = <Icon name="icons fa-2x fa-fw" alt="avatar" />;
|
||||
const contentLeft = <strong className="m-0">main content</strong>;
|
||||
const contentRight = <small>more text</small>;
|
||||
|
||||
@@ -42,13 +42,13 @@ storiesOf("CardColumnSmall", module)
|
||||
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator((storyFn) => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.add("Default", () => (
|
||||
<CardColumnSmall link={link} avatar={icon} contentLeft={contentLeft} contentRight={contentRight} />
|
||||
<CardColumnSmall link={link} avatar={avatar} contentLeft={contentLeft} contentRight={contentRight} />
|
||||
))
|
||||
.add("Minimal", () => <CardColumnSmall link={link} contentLeft={contentLeft} contentRight={contentRight} />)
|
||||
.add("Task", () => (
|
||||
<CardColumnSmall
|
||||
link={link}
|
||||
avatar={<Icon name="exchange-alt" className="fa-fw fa-lg" color="inherit" />}
|
||||
avatar={<Icon name="exchange-alt" className="fa-fw fa-lg" color="inherit" alt="avatar" />}
|
||||
contentLeft={<strong>Repository created</strong>}
|
||||
contentRight={<small>over 42 years ago</small>}
|
||||
footer="New: scmadmin/spaceship"
|
||||
|
||||
@@ -75,7 +75,7 @@ const RedirectPage = () => {
|
||||
"is-align-items-center"
|
||||
)}
|
||||
>
|
||||
<Icon name="directions" className="fa-7x" />
|
||||
<Icon name="directions" className="fa-7x" alt="" />
|
||||
</RedirectIconContainer>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -28,6 +28,6 @@ type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const HelpIcon: FC<Props> = ({ className }) => <Icon name="question-circle" color="blue-light" className={className} />;
|
||||
const HelpIcon: FC<Props> = ({ className }) => <Icon name="question-circle" color="blue-light" className={className} alt="" />;
|
||||
|
||||
export default HelpIcon;
|
||||
|
||||
@@ -35,6 +35,7 @@ type Props = {
|
||||
onEnter?: (event: React.KeyboardEvent) => void;
|
||||
testId?: string;
|
||||
tabIndex?: number;
|
||||
alt?: string;
|
||||
};
|
||||
|
||||
const Icon: FC<Props> = ({
|
||||
@@ -47,6 +48,7 @@ const Icon: FC<Props> = ({
|
||||
testId,
|
||||
tabIndex = -1,
|
||||
onEnter,
|
||||
alt = title,
|
||||
}) => {
|
||||
return (
|
||||
<i
|
||||
@@ -55,6 +57,7 @@ const Icon: FC<Props> = ({
|
||||
title={title}
|
||||
className={classNames(iconStyle, "fa-fw", "fa-" + name, `has-text-${color}`, className)}
|
||||
tabIndex={tabIndex}
|
||||
aria-label={alt}
|
||||
{...createAttributesForTesting(testId)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -31,12 +31,12 @@ storiesOf("SplitAndReplace", module).add("Simple replacement", () => {
|
||||
const replacements = [
|
||||
{
|
||||
textToReplace: "'",
|
||||
replacement: <Icon name={"quote-left"} />,
|
||||
replacement: <Icon name="quote-left" alt="" />,
|
||||
replaceAll: true,
|
||||
},
|
||||
{
|
||||
textToReplace: "`",
|
||||
replacement: <Icon name={"quote-right"} />,
|
||||
replacement: <Icon name="quote-right" alt="" />,
|
||||
replaceAll: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -78,7 +78,7 @@ const SyntaxHighlighterRenderer: FC<Props> = ({
|
||||
stylesheet,
|
||||
useInlineStyles,
|
||||
createLinePermaLink,
|
||||
showLineNumbers = true
|
||||
showLineNumbers = true,
|
||||
}) => {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
@@ -108,7 +108,7 @@ const SyntaxHighlighterRenderer: FC<Props> = ({
|
||||
node,
|
||||
stylesheet,
|
||||
useInlineStyles,
|
||||
key: `code-segment${i}`
|
||||
key: `code-segment${i}`,
|
||||
});
|
||||
return (
|
||||
<RowContainer
|
||||
@@ -119,10 +119,14 @@ const SyntaxHighlighterRenderer: FC<Props> = ({
|
||||
{showLineNumbers && (
|
||||
<>
|
||||
{copying ? (
|
||||
<Icon name="spinner fa-spin" />
|
||||
<Icon name="spinner fa-spin" alt={t("sources.content.loading")} />
|
||||
) : (
|
||||
<Tooltip message={t("sources.content.copyPermalink")}>
|
||||
<Icon name="link" onClick={() => lineNumberClick(lineNumber)} />
|
||||
<Icon
|
||||
name="link"
|
||||
onClick={() => lineNumberClick(lineNumber)}
|
||||
alt={t("sources.content.copyPermalink")}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<span
|
||||
@@ -143,7 +147,7 @@ const SyntaxHighlighterRenderer: FC<Props> = ({
|
||||
|
||||
//
|
||||
export const create = (createLinePermaLink: CreateLinePermaLinkFn, showLineNumbers = false): FC<Props> => {
|
||||
return props => (
|
||||
return (props) => (
|
||||
<SyntaxHighlighterRenderer {...props} createLinePermaLink={createLinePermaLink} showLineNumbers={showLineNumbers} />
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
import React, { MouseEvent, ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import { withRouter, RouteComponentProps } from "react-router-dom";
|
||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import Icon from "../Icon";
|
||||
import { createAttributesForTesting } from "../devBuild";
|
||||
|
||||
@@ -51,7 +51,7 @@ type Props = ButtonProps &
|
||||
class Button extends React.Component<Props> {
|
||||
static defaultProps: Partial<Props> = {
|
||||
type: "button",
|
||||
color: "default"
|
||||
color: "default",
|
||||
};
|
||||
|
||||
onClick = (event: React.MouseEvent) => {
|
||||
@@ -76,7 +76,7 @@ class Button extends React.Component<Props> {
|
||||
fullWidth,
|
||||
reducedMobile,
|
||||
children,
|
||||
testId
|
||||
testId,
|
||||
} = this.props;
|
||||
if (icon) {
|
||||
return (
|
||||
|
||||
@@ -34,7 +34,7 @@ type Props = {
|
||||
tooltipStyle?: "tooltipComponent" | "htmlTitle";
|
||||
};
|
||||
|
||||
const Button = styled.a`
|
||||
const Button = styled.button`
|
||||
width: 50px;
|
||||
&:hover {
|
||||
color: #33b2e8;
|
||||
@@ -52,6 +52,7 @@ const OpenInFullscreenButton: FC<Props> = ({ modalTitle, modalBody, tooltipStyle
|
||||
title={tooltipStyle === "htmlTitle" ? tooltip : undefined}
|
||||
className="button"
|
||||
onClick={() => setShowModal(true)}
|
||||
aria-label={tooltip}
|
||||
>
|
||||
<i className="fas fa-search-plus" />
|
||||
</Button>
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Button, ButtonGroup } from "../buttons";
|
||||
import copyToClipboard from "../CopyToClipboard";
|
||||
|
||||
const link = "/foo/bar";
|
||||
const icon = <Icon name="icons fa-2x fa-fw" />;
|
||||
const avatar = <Icon name="icons fa-2x fa-fw" alt="avatar" />;
|
||||
const name = <strong className="m-0">main content</strong>;
|
||||
const description = <small>more text</small>;
|
||||
const longName = (
|
||||
@@ -53,12 +53,12 @@ storiesOf("GroupEntry", module)
|
||||
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator((storyFn) => <div className="m-5">{storyFn()}</div>)
|
||||
.add("Default", () => (
|
||||
<GroupEntry link={link} avatar={icon} name={name} description={description} contentRight={contentRight} />
|
||||
<GroupEntry link={link} avatar={avatar} name={name} description={description} contentRight={contentRight} />
|
||||
))
|
||||
.add("With long texts", () => (
|
||||
<GroupEntry
|
||||
link={link}
|
||||
avatar={icon}
|
||||
avatar={avatar}
|
||||
name={longName}
|
||||
description={
|
||||
<small>
|
||||
|
||||
@@ -88,10 +88,10 @@ const MarkdownHeadingRenderer: FC<Props> = ({ children, level, permalink, id })
|
||||
.finally(() => setCopying(false));
|
||||
};
|
||||
const CopyButton = copying ? (
|
||||
<Icon name="spinner fa-spin" />
|
||||
<Icon name="spinner fa-spin" alt={t("sources.content.loading")} />
|
||||
) : (
|
||||
<Tooltip message={t("sources.content.copyPermalink")}>
|
||||
<Icon name="link" onClick={copyPermalink} />
|
||||
<Icon name="link" onClick={copyPermalink} alt={t("sources.content.copyPermalink")} />
|
||||
</Tooltip>
|
||||
);
|
||||
const headingElement = React.createElement("h" + level, {id: anchorId}, [...reactChildren, CopyButton]);
|
||||
|
||||
@@ -26,6 +26,7 @@ import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import useMenuContext from "./MenuContext";
|
||||
import classNames from "classnames";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
@@ -61,6 +62,7 @@ const MenuLabel = styled.p<CollapsedProps>`
|
||||
`;
|
||||
|
||||
const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const menuContext = useMenuContext();
|
||||
const isCollapsed = collapsible && menuContext.isCollapsed();
|
||||
|
||||
@@ -77,6 +79,7 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
||||
};
|
||||
|
||||
const arrowIcon = isCollapsed ? <i className="fas fa-caret-down" /> : <i className="fas fa-caret-right" />;
|
||||
const menuAriaLabel = isCollapsed ? t("secondaryNavigation.showContent") : t("secondaryNavigation.hideContent");
|
||||
|
||||
return (
|
||||
<SectionContainer className="menu">
|
||||
@@ -85,6 +88,7 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
||||
className={classNames("menu-label", { "is-clickable": collapsible })}
|
||||
collapsed={isCollapsed}
|
||||
onClick={toggleCollapseState}
|
||||
aria-label={menuAriaLabel}
|
||||
>
|
||||
{collapsible ? (
|
||||
<Icon color="info" className="is-medium" collapsed={isCollapsed}>
|
||||
|
||||
@@ -40,9 +40,9 @@ const CommitAuthor: FC = () => {
|
||||
return (
|
||||
<>
|
||||
{!me.mail && <Notification type="warning">{t("commit.commitAuthor.noMail")}</Notification>}
|
||||
<span className="mb-2">
|
||||
<div className="mb-2">
|
||||
<strong>{t("commit.commitAuthor.author")}</strong> {`${me.displayName} <${mail}>`}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ import React, { FC, MouseEvent } from "react";
|
||||
import styled from "styled-components";
|
||||
import Tooltip from "../Tooltip";
|
||||
|
||||
const Button = styled.a`
|
||||
const Button = styled.button`
|
||||
width: 50px;
|
||||
&:hover {
|
||||
color: #33b2e8;
|
||||
|
||||
@@ -353,10 +353,11 @@ class DiffFile extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
renderFileTitle = (file: FileDiff) => {
|
||||
const { t } = this.props;
|
||||
if (file.oldPath !== file.newPath && (file.type === "copy" || file.type === "rename")) {
|
||||
return (
|
||||
<>
|
||||
{file.oldPath} <Icon name="arrow-right" color="inherit" /> {file.newPath}
|
||||
{file.oldPath} <Icon name="arrow-right" color="inherit" alt={t("diff.renamedTo")} /> {file.newPath}
|
||||
</>
|
||||
);
|
||||
} else if (file.type === "delete") {
|
||||
@@ -426,13 +427,13 @@ class DiffFile extends React.Component<Props, State> {
|
||||
</TokenizedDiffView>
|
||||
</div>
|
||||
);
|
||||
let icon = "angle-right";
|
||||
let icon = <Icon name="angle-right" color="inherit" alt={t("diff.showContent")} />;
|
||||
let body = null;
|
||||
if (!collapsed) {
|
||||
icon = "angle-down";
|
||||
icon = <Icon name="angle-down" color="inherit" alt={t("diff.hideContent")} />;
|
||||
body = innerContent;
|
||||
}
|
||||
const collapseIcon = this.hasContent(file) ? <Icon name={icon} color="inherit" /> : null;
|
||||
const collapseIcon = this.hasContent(file) ? icon : null;
|
||||
const fileControls = fileControlFactory ? fileControlFactory(file, this.setCollapse) : null;
|
||||
const modalTitle = file.type === "delete" ? file.oldPath : file.newPath;
|
||||
const openInFullscreen = file?.hunks?.length ? (
|
||||
|
||||
@@ -43,7 +43,7 @@ const JumpToFileButton: FC<Props> = ({ link, tooltip }) => {
|
||||
return (
|
||||
<Tooltip message={tooltip} location="top">
|
||||
<Button aria-label={tooltip} className="button is-clickable" to={link}>
|
||||
<Icon name="file-code" color="inherit" />
|
||||
<Icon name="file-code" color="inherit" alt="" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ class RepositoryEntryLink extends React.Component<Props> {
|
||||
render() {
|
||||
const { to, icon, tooltip } = this.props;
|
||||
|
||||
let content = <Icon className="fa-lg" name={icon} color="inherit" />;
|
||||
let content = <Icon className="fa-lg" name={icon} color="inherit" alt={`${icon} icon`} />;
|
||||
if (tooltip) {
|
||||
content = (
|
||||
<Tooltip message={tooltip} location="top">
|
||||
|
||||
@@ -31,7 +31,13 @@ type Props = {
|
||||
};
|
||||
|
||||
const SortIcon: FC<Props> = (props: Props) => {
|
||||
return <Icon className={classNames("ml-1", { "is-invisible": !props.isVisible })} name={props.name} />;
|
||||
return (
|
||||
<Icon
|
||||
className={classNames("ml-1", { "is-invisible": !props.isVisible })}
|
||||
name={props.name}
|
||||
alt={`${props.name} icon`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SortIcon;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"installedNavLink": "Installiert",
|
||||
"availableNavLink": "Verfügbar"
|
||||
},
|
||||
"markedAsPending": "Als ausstehend markiert",
|
||||
"showPending": "Änderungen anzeigen",
|
||||
"executePending": "Änderungen ausführen",
|
||||
"outdatedPlugins": "{{count}} Plugin aktualisieren",
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "Hauptseite",
|
||||
"copyPermalink": "Link in Zwischenablage kopieren"
|
||||
"copyPermalink": "Link in Zwischenablage kopieren",
|
||||
"loading": "Lade ..."
|
||||
},
|
||||
"errorNotification": {
|
||||
"prefix": "Fehler",
|
||||
@@ -59,6 +60,10 @@
|
||||
"groups": "Gruppen",
|
||||
"admin": "Administration"
|
||||
},
|
||||
"secondaryNavigation": {
|
||||
"showContent": "Navigation vergrößern",
|
||||
"hideContent": "Navigation verkleinern"
|
||||
},
|
||||
"filterEntries": "Einträge filtern",
|
||||
"autocomplete": {
|
||||
"group": "Gruppe",
|
||||
@@ -133,10 +138,16 @@
|
||||
"toastTitle": "Benachrichtigung",
|
||||
"xMore": "+{{ count }} Benachrichtigung",
|
||||
"xMore_plural": "+{{ count }} Benachrichtigungen",
|
||||
"loading": "Lade ...",
|
||||
"bellTitle": "Benachrichtigungen",
|
||||
"empty": "Keine Benachrichtigungen",
|
||||
"dismiss": "Löschen",
|
||||
"dismissAll": "Alle löschen"
|
||||
},
|
||||
"cardColumnGroup": {
|
||||
"showContent": "Inhalt einblenden",
|
||||
"hideContent": "Inhalt ausblenden"
|
||||
},
|
||||
"duration": {
|
||||
"ms": "{{count}} Millisekunde",
|
||||
"ms_plural": "{{count}} Millisekunden",
|
||||
@@ -179,6 +190,10 @@
|
||||
"exampleValue": "Beispielwert",
|
||||
"hints": "Hinweise"
|
||||
},
|
||||
"expandable": {
|
||||
"showMore": "Mehr Informationen einblenden",
|
||||
"hideMore": "Mehr Informationen ausblenden"
|
||||
},
|
||||
"exampleQueries": {
|
||||
"title": "Beispielabfragen",
|
||||
"description": "Felder mit Modifikatoren und Operatoren um Repositories zu finden.",
|
||||
@@ -191,7 +206,7 @@
|
||||
"utilities": {
|
||||
"title": "Hilfsmittel",
|
||||
"description": "Wandeln Sie menschlich-lesbare Zeitmarken in standard Millisekunden um, um diese in Ihren Queries zu verwenden. Das Datum ist ein Pflichtfeld, Stunde, Minute und Sekunde sind optional.",
|
||||
"datetime":{
|
||||
"datetime": {
|
||||
"label": "Datum in Standardzeitstempel umwandeln",
|
||||
"format": "yyyy-mm-dd hh:mm:ss",
|
||||
"convertButtonLabel": "Konvertieren"
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"add-member-autocomplete": {
|
||||
"placeholder": "Mitglied hinzufügen",
|
||||
"loading": "Suche...",
|
||||
"loading": "Suche ...",
|
||||
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
||||
},
|
||||
"groupForm": {
|
||||
|
||||
@@ -236,6 +236,8 @@
|
||||
},
|
||||
"contributors": {
|
||||
"mailto": "Mail senden an",
|
||||
"showList": "Liste der Mitwirkenden einblenden",
|
||||
"hideList": "Liste der Mitwirkenden ausblenden",
|
||||
"list": "Liste der Mitwirkenden",
|
||||
"authoredBy": "Verfasst von",
|
||||
"committedBy": "Committed von",
|
||||
@@ -323,12 +325,15 @@
|
||||
"showMarkdown": "Markdown rendern",
|
||||
"showSources": "Sources anzeigen"
|
||||
},
|
||||
"showMore": "Mehr Informationen einblenden",
|
||||
"hideMore": "Mehr Informationen ausblenden",
|
||||
"path": "Pfad",
|
||||
"branch": "Branch",
|
||||
"commitDate": "Commitdatum",
|
||||
"description": "Beschreibung",
|
||||
"size": "Größe",
|
||||
"copyPermalink": "Link in Zwischenablage kopieren"
|
||||
"copyPermalink": "Link in Zwischenablage kopieren",
|
||||
"loading": "Lade ..."
|
||||
},
|
||||
"noSources": "Keine Sources in diesem Branch gefunden.",
|
||||
"extension": {
|
||||
@@ -469,16 +474,20 @@
|
||||
"fullscreen": {
|
||||
"open": "In Vollbildansicht öffnen",
|
||||
"close": "Schließen"
|
||||
}
|
||||
},
|
||||
"renamedTo": "umbenannt in",
|
||||
"showContent": "Inhalt des Diffs einblenden",
|
||||
"hideContent": "Inhalt des Diffs ausblenden"
|
||||
},
|
||||
"fileUpload": {
|
||||
"clickHere": "Klicken Sie hier um Ihre Datei hochzuladen.",
|
||||
"dragAndDrop": "Sie können Ihre Datei auch direkt in die Dropzone ziehen."
|
||||
},
|
||||
"filesearch": {
|
||||
"fileSearch": {
|
||||
"button": {
|
||||
"title": "Dateipfad Suche"
|
||||
},
|
||||
"file": "Datei",
|
||||
"home": "Zurück zu Sources",
|
||||
"input": {
|
||||
"placeholder": "Dateipfad Suche",
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
"title": "Schlüssel erzeugt",
|
||||
"text1": "Ihr neuer API-Schlüssel ist bereit. Sie können diesen als Token für Zugriffe auf die REST-Schnittstelle nutzen oder anstelle Ihres Passworts zum Login mit SCM-Clients nutzen.",
|
||||
"text2": "Sichern Sie Ihren API-Schlüssel jetzt! Er wird hier einmalig angezeigt und kann später nicht mehr wiederbeschafft werden.",
|
||||
"alt": "Api Key",
|
||||
"clipboard": "In die Zwischenablage kopieren",
|
||||
"close": "Schließen"
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"installedNavLink": "Installed",
|
||||
"availableNavLink": "Available"
|
||||
},
|
||||
"markedAsPending": "Marked as pending",
|
||||
"showPending": "Show Changes",
|
||||
"executePending": "Execute Changes",
|
||||
"outdatedPlugins": "Update {{count}} Plugin",
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "Main page",
|
||||
"copyPermalink": "Copy Permalink to Clipboard"
|
||||
"copyPermalink": "Copy Permalink to Clipboard",
|
||||
"loading": "Loading ..."
|
||||
},
|
||||
"errorNotification": {
|
||||
"prefix": "Error",
|
||||
@@ -60,6 +61,10 @@
|
||||
"groups": "Groups",
|
||||
"admin": "Administration"
|
||||
},
|
||||
"secondaryNavigation": {
|
||||
"showContent": "Enlarge navigation",
|
||||
"hideContent": "Reduce navigation"
|
||||
},
|
||||
"filterEntries": "filter entries",
|
||||
"autocomplete": {
|
||||
"group": "Group",
|
||||
@@ -134,10 +139,16 @@
|
||||
"toastTitle": "Notification",
|
||||
"xMore": "+{{ count }} Notification",
|
||||
"xMore_plural": "+{{ count }} Notifications",
|
||||
"loading": "Loading ...",
|
||||
"bellTitle": "Notifications",
|
||||
"empty": "No notifications",
|
||||
"dismiss": "Dismiss",
|
||||
"dismissAll": "Dismiss all"
|
||||
},
|
||||
"cardColumnGroup": {
|
||||
"showContent": "Show content",
|
||||
"hideContent": "Hide content"
|
||||
},
|
||||
"duration": {
|
||||
"ms": "{{count}} millisecond",
|
||||
"ms_plural": "{{count}} milliseconds",
|
||||
@@ -180,6 +191,10 @@
|
||||
"exampleValue": "Example Value",
|
||||
"hints": "Hints"
|
||||
},
|
||||
"expandable": {
|
||||
"showMore": "Show more information",
|
||||
"hideMore": "Hide more information"
|
||||
},
|
||||
"exampleQueries": {
|
||||
"title": "Example Queries",
|
||||
"description": "Combine Fields with Modifiers and Operators to find your repositories.",
|
||||
@@ -192,7 +207,7 @@
|
||||
"utilities": {
|
||||
"title": "Utilities",
|
||||
"description": "Convert human-readable timestamps to Epoch Milliseconds to use in your query. Date is mandatory, hour, minute and seconds are optional.",
|
||||
"datetime":{
|
||||
"datetime": {
|
||||
"label": "Convert timestamps to Epoch Milliseconds",
|
||||
"format": "yyyy-mm-dd hh:mm:ss",
|
||||
"convertButtonLabel": "Convert"
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"add-member-autocomplete": {
|
||||
"placeholder": "Add Member",
|
||||
"loading": "Loading...",
|
||||
"loading": "Loading ...",
|
||||
"no-options": "No suggestion available"
|
||||
},
|
||||
"groupForm": {
|
||||
|
||||
@@ -240,6 +240,8 @@
|
||||
},
|
||||
"contributors": {
|
||||
"mailto": "Send mail to",
|
||||
"showList": "Show list of contributors",
|
||||
"hideList": "Hide list of contributors",
|
||||
"list": "List of contributors",
|
||||
"authoredBy": "Authored by",
|
||||
"committedBy": "committed by",
|
||||
@@ -323,12 +325,15 @@
|
||||
"showMarkdown": "Render markdown",
|
||||
"showSources": "Show sources"
|
||||
},
|
||||
"showMore": "Show more information",
|
||||
"hideMore": "Hide more information",
|
||||
"path": "Path",
|
||||
"branch": "Branch",
|
||||
"commitDate": "Commit Date",
|
||||
"description": "Description",
|
||||
"size": "Size",
|
||||
"copyPermalink": "Copy Permalink to Clipboard"
|
||||
"copyPermalink": "Copy Permalink to Clipboard",
|
||||
"loading": "Loading ..."
|
||||
},
|
||||
"noSources": "No sources found for this branch.",
|
||||
"extension": {
|
||||
@@ -476,16 +481,20 @@
|
||||
"fullscreen": {
|
||||
"open": "Open in Fullscreen",
|
||||
"close": "Close"
|
||||
}
|
||||
},
|
||||
"renamedTo": "renamed to",
|
||||
"showContent": "Show diff content",
|
||||
"hideContent": "Hide diff content"
|
||||
},
|
||||
"fileUpload": {
|
||||
"clickHere": "Click here to select your file",
|
||||
"dragAndDrop": "Drag 'n' drop some files here"
|
||||
},
|
||||
"filesearch": {
|
||||
"fileSearch": {
|
||||
"button": {
|
||||
"title": "Search filepath"
|
||||
},
|
||||
"file": "File",
|
||||
"home": "Go back to source root",
|
||||
"input": {
|
||||
"placeholder": "Search filepath",
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
"title": "Key Created",
|
||||
"text1": "Your new API key is ready. You can use it as a bearer token for REST calls or as a password for SCM clients.",
|
||||
"text2": "Store your API key in a safe place now! It is only displayed now and cannot be recovered later.",
|
||||
"alt": "Api Key",
|
||||
"clipboard": "Copy to clipboard",
|
||||
"close": "Close"
|
||||
}
|
||||
|
||||
@@ -71,7 +71,12 @@ const PluginEntry: FC<Props> = ({ plugin, openModal }) => {
|
||||
};
|
||||
|
||||
const pendingSpinner = () => (
|
||||
<Icon className="fa-spin fa-lg" name="spinner" color={plugin.markedForUninstall ? "danger" : "info"} />
|
||||
<Icon
|
||||
className="fa-spin fa-lg"
|
||||
name="spinner"
|
||||
color={plugin.markedForUninstall ? "danger" : "info"}
|
||||
alt={t("plugins.markedAsPending")}
|
||||
/>
|
||||
);
|
||||
const actionBar = () => (
|
||||
<ActionbarWrapper className="is-flex">
|
||||
|
||||
@@ -76,7 +76,7 @@ class InfoBox extends React.Component<Props> {
|
||||
"is-align-items-center"
|
||||
)}
|
||||
>
|
||||
<Icon className="has-text-blue-light mb-2 fa-2x" name={icon} color="inherit" />
|
||||
<Icon className="has-text-blue-light mb-2 fa-2x" name={icon} color="inherit" alt="" />
|
||||
<div className="is-size-4">{t("login." + type)}</div>
|
||||
<div className="is-size-4">{t("login.tip")}</div>
|
||||
</FixedSizedIconWrapper>
|
||||
|
||||
@@ -128,7 +128,7 @@ const NotificationEntry: FC<EntryProps> = ({ notification, removeToast }) => {
|
||||
</DateColumn>
|
||||
<DismissColumn className="is-darker">
|
||||
{isLoading ? (
|
||||
<div className="small-loading-spinner" />
|
||||
<div className="small-loading-spinner" aria-label={t("notifications.loading")} />
|
||||
) : (
|
||||
<Icon
|
||||
name="trash"
|
||||
@@ -165,7 +165,7 @@ const ClearEntry: FC<ClearEntryProps> = ({ notifications, clearToasts }) => {
|
||||
<div className={classNames("dropdown-item", "has-text-centered")}>
|
||||
<ErrorNotification error={error} />
|
||||
<DismissAllButton className="is-outlined" color="link" loading={isLoading} action={clear}>
|
||||
<Icon color="link" name="trash" className="mr-1" /> {t("notifications.dismissAll")}
|
||||
<Icon color="link" name="trash" className="mr-1" alt="" /> {t("notifications.dismissAll")}
|
||||
</DismissAllButton>
|
||||
</div>
|
||||
);
|
||||
@@ -292,13 +292,20 @@ type BellNotificationIconProps = {
|
||||
};
|
||||
|
||||
const BellNotificationIcon: FC<BellNotificationIconProps> = ({ data, onClick }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const counter = data?._embedded.notifications.length || 0;
|
||||
return (
|
||||
<BellNotificationContainer
|
||||
className={classNames("is-relative", "is-flex", "is-justify-content-center", "is-align-items-center")}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className="is-size-4" iconStyle={counter === 0 ? "far" : "fas"} name="bell" color="white" />
|
||||
<Icon
|
||||
className="is-size-4"
|
||||
iconStyle={counter === 0 ? "far" : "fas"}
|
||||
name="bell"
|
||||
color="white"
|
||||
alt={t("notifications.bellTitle")}
|
||||
/>
|
||||
{counter > 0 ? <NotificationCounter count={counter}>{counter < 100 ? counter : "∞"}</NotificationCounter> : null}
|
||||
</BellNotificationContainer>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class GroupMember extends React.Component<Props> {
|
||||
renderLink(to: string, label: string) {
|
||||
return (
|
||||
<Link to={to}>
|
||||
<Icon name="user" color="inherit" /> {label}
|
||||
<Icon name="user" color="inherit" alt="" /> {label}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ const FileSearchButton: FC<Props> = ({ baseUrl, revision }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
return (
|
||||
<Link to={`${baseUrl}/search/${encodeURIComponent(revision)}`}>
|
||||
<SearchIcon title={t("filesearch.button.title")} name="search" color="inherit" />
|
||||
<SearchIcon title={t("fileSearch.button.title")} name="search" color="inherit" />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -52,12 +52,13 @@ type PathResultRowProps = {
|
||||
};
|
||||
|
||||
const PathResultRow: FC<PathResultRowProps> = ({ contentBaseUrl, path }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const link = urls.concat(contentBaseUrl, path);
|
||||
return (
|
||||
<tr>
|
||||
<IconColumn>
|
||||
<Link to={link}>
|
||||
<Icon title="File" name="file" color="inherit" />
|
||||
<Icon title={t("fileSearch.file")} name="file" color="inherit" />
|
||||
</Link>
|
||||
</IconColumn>
|
||||
<LeftOverflowTd>
|
||||
@@ -90,14 +91,14 @@ const FileSearchResults: FC<Props> = ({ query, contentBaseUrl, paths = [] }) =>
|
||||
if (query.length <= 1) {
|
||||
body = (
|
||||
<Notification className="m-4" type="info">
|
||||
{t("filesearch.notifications.queryToShort")}
|
||||
{t("fileSearch.notifications.queryToShort")}
|
||||
</Notification>
|
||||
);
|
||||
} else if (paths.length === 0) {
|
||||
const queryCmp = <strong>{query}</strong>;
|
||||
body = (
|
||||
<Notification className="m-4" type="info">
|
||||
<Trans i18nKey="repos:filesearch.notifications.emptyResult" values={{ query }} components={[queryCmp]} />
|
||||
<Trans i18nKey="repos:fileSearch.notifications.emptyResult" values={{ query }} components={[queryCmp]} />
|
||||
</Notification>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -113,16 +113,16 @@ const FileSearch: FC<Props> = ({ repository, baseUrl, branches, selectedBranch }
|
||||
)}
|
||||
>
|
||||
<HomeLink className={classNames("mr-3", "pr-3")} to={contentBaseUrl}>
|
||||
<HomeIcon title={t("filesearch.home")} name="home" color="inherit" />
|
||||
<HomeIcon title={t("fileSearch.home")} name="home" color="inherit" />
|
||||
</HomeLink>
|
||||
<FilterInput
|
||||
className="is-full-width"
|
||||
placeholder={t("filesearch.input.placeholder")}
|
||||
placeholder={t("fileSearch.input.placeholder")}
|
||||
value={query}
|
||||
filter={search}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<Help className="ml-3" message={t("filesearch.input.help")} />
|
||||
<Help className="ml-3" message={t("fileSearch.input.help")} />
|
||||
</div>
|
||||
<ErrorNotification error={error} />
|
||||
{isLoading ? <Loading /> : <FileSearchResults contentBaseUrl={contentBaseUrl} query={query} paths={result} />}
|
||||
|
||||
@@ -93,7 +93,7 @@ const Contributors: FC<{ changeset: Changeset }> = ({ changeset }) => {
|
||||
<div className="is-flex is-flex-direction-column mb-4">
|
||||
<div className="is-flex">
|
||||
<p className="is-ellipsis-overflow is-clickable mb-2" onClick={(e) => setOpen(!open)}>
|
||||
<Icon name="angle-down" /> {t("changeset.contributors.list")}
|
||||
<Icon name="angle-down" alt={t("changeset.contributors.hideList")} /> {t("changeset.contributors.list")}
|
||||
</p>
|
||||
{signatureIcon}
|
||||
</div>
|
||||
@@ -106,7 +106,8 @@ const Contributors: FC<{ changeset: Changeset }> = ({ changeset }) => {
|
||||
<>
|
||||
<div className="is-flex is-clickable" onClick={(e) => setOpen(!open)}>
|
||||
<ContributorColumn className="is-ellipsis-overflow">
|
||||
<Icon name="angle-right" /> <ChangesetAuthor changeset={changeset} />
|
||||
<Icon name="angle-right" alt={t("changeset.contributors.showList")} />{" "}
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</ContributorColumn>
|
||||
{signatureIcon}
|
||||
<CountColumn className="is-hidden-mobile is-hidden-tablet-only is-hidden-desktop-only">
|
||||
@@ -229,6 +230,7 @@ const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory
|
||||
color="default"
|
||||
icon={collapsed ? "eye" : "eye-slash"}
|
||||
label={t("changesets.collapseDiffs")}
|
||||
title={t("changesets.collapseDiffs")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ const RepositoryFormButton: FC<RepositoryForm> = ({ path, icon, label }) => {
|
||||
color={isSelected ? "link is-selected" : undefined}
|
||||
link={!isSelected ? href : undefined}
|
||||
>
|
||||
<Icon className="pr-2" name={icon} color={isSelected ? "white" : "default"} />
|
||||
<Icon className="pr-2" name={icon} color={isSelected ? "white" : "default"} alt="" />
|
||||
<p className={classNames("is-hidden-mobile", "is-hidden-tablet-only")}>{t(`plugins:${label}`, label)}</p>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ const RepositoryGroupEntry: FC<Props> = ({ group }) => {
|
||||
|
||||
const settingsLink = group.namespace?._links?.permissions && (
|
||||
<Link to={`/namespace/${group.name}/settings`}>
|
||||
<Icon color="is-link" name="cog" title={t("repositoryOverview.settings.tooltip")} className="is-size-6 ml-2" />
|
||||
<Icon color="inherit" name="cog" title={t("repositoryOverview.settings.tooltip")} className="is-size-6 ml-2" />
|
||||
</Link>
|
||||
);
|
||||
const namespaceHeader = (
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespace, Permission, Repository } from "@scm-manager/ui-types";
|
||||
import { ConfirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { ConfirmAlert, ErrorNotification, Icon } from "@scm-manager/ui-components";
|
||||
import { useDeletePermission } from "@scm-manager/ui-api";
|
||||
|
||||
type Props = {
|
||||
@@ -63,12 +63,12 @@ const DeletePermissionButton: FC<Props> = ({ namespaceOrRepository, permission,
|
||||
className: "is-outlined",
|
||||
label: t("permission.delete-permission-button.confirm-alert.submit"),
|
||||
isLoading,
|
||||
onClick: () => deletePermission()
|
||||
onClick: () => deletePermission(),
|
||||
},
|
||||
{
|
||||
label: t("permission.delete-permission-button.confirm-alert.cancel"),
|
||||
onClick: () => null
|
||||
}
|
||||
onClick: () => null,
|
||||
},
|
||||
]}
|
||||
close={() => setShowConfirmAlert(false)}
|
||||
/>
|
||||
@@ -80,7 +80,7 @@ const DeletePermissionButton: FC<Props> = ({ namespaceOrRepository, permission,
|
||||
<ErrorNotification error={error} />
|
||||
<a className="level-item" onClick={action}>
|
||||
<span className="icon is-small">
|
||||
<i className="fas fa-trash" />
|
||||
<Icon name="trash" title={t("permission.delete-permission-button.label")} color="inherit" />
|
||||
</span>
|
||||
</a>
|
||||
</>
|
||||
|
||||
@@ -78,13 +78,13 @@ class FileTreeLeaf extends React.Component<Props> {
|
||||
} else if (file.computationAborted) {
|
||||
return (
|
||||
<Tooltip location="top" message={t("sources.fileTree.computationAborted")}>
|
||||
<Icon name="question-circle" />
|
||||
<Icon name="question-circle" alt={t("sources.fileTree.computationAborted")} />
|
||||
</Tooltip>
|
||||
);
|
||||
} else if (file.partialResult) {
|
||||
return (
|
||||
<Tooltip location="top" message={t("sources.fileTree.notYetComputed")}>
|
||||
<Icon name="hourglass" />
|
||||
<Icon name="hourglass" alt={t("sources.fileTree.notYetComputed")} />
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -56,27 +56,33 @@ class FileButtonAddons extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<ButtonAddons className={className}>
|
||||
<div title={t("sources.content.sourcesButton")}>
|
||||
<Button action={showSources} color={this.color(selected === "source")}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-code" />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div title={t("sources.content.annotateButton")}>
|
||||
<Button action={showAnnotations} color={this.color(selected === "annotations")}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-user-clock" />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div title={t("sources.content.historyButton")}>
|
||||
<Button action={showHistory} color={this.color(selected === "history")}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-history" />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
action={showSources}
|
||||
color={this.color(selected === "source")}
|
||||
title={t("sources.content.sourcesButton")}
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="fas fa-code" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
action={showAnnotations}
|
||||
color={this.color(selected === "annotations")}
|
||||
title={t("sources.content.annotateButton")}
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="fas fa-user-clock" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
action={showHistory}
|
||||
color={this.color(selected === "history")}
|
||||
title={t("sources.content.historyButton")}
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="fas fa-history" />
|
||||
</span>
|
||||
</Button>
|
||||
</ButtonAddons>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,26 @@ const Content: FC<Props> = ({ file, repository, revision, breadcrumb, error }) =
|
||||
};
|
||||
|
||||
const showHeader = (content: ReactNode) => {
|
||||
const icon = collapsed ? "angle-right" : "angle-down";
|
||||
let icon;
|
||||
if (collapsed) {
|
||||
icon = (
|
||||
<Icon
|
||||
className={classNames("is-inline", "mr-2")}
|
||||
name="angle-right fa-fw"
|
||||
color="inherit"
|
||||
alt={t("sources.content.showMore")}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
icon = (
|
||||
<Icon
|
||||
className={classNames("is-inline", "mr-2")}
|
||||
name="angle-down fa-fw"
|
||||
color="inherit"
|
||||
alt={t("sources.content.hideMore")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const selector = file._links.history ? (
|
||||
<FileButtonAddons
|
||||
@@ -107,7 +126,7 @@ const Content: FC<Props> = ({ file, repository, revision, breadcrumb, error }) =
|
||||
className={classNames("level-left", "is-flex", "is-clickable", "is-word-break", "mr-2")}
|
||||
onClick={toggleCollapse}
|
||||
>
|
||||
<Icon className={classNames("is-inline", "mr-2")} name={`${icon} fa-fw`} color="inherit" />
|
||||
{icon}
|
||||
{file.name}
|
||||
</FullWidthTitleHeader>
|
||||
<div className={classNames("level-right", "buttons", "ml-auto")}>
|
||||
|
||||
@@ -50,13 +50,18 @@ type ExpandableProps = {
|
||||
};
|
||||
|
||||
const Expandable: FC<ExpandableProps> = ({ header, children, className }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
return (
|
||||
<div className={classNames("card", className)}>
|
||||
<header onClick={() => setExpanded(!expanded)} className="card-header is-clickable">
|
||||
<span className="card-header-title">{header}</span>
|
||||
<span className="card-header-icon">
|
||||
<Icon name={expanded ? "chevron-down" : "chevron-left"} />
|
||||
{expanded ? (
|
||||
<Icon name="chevron-down" alt={t("search.syntax.expandable.hideMore")} />
|
||||
) : (
|
||||
<Icon name="chevron-left" alt={t("search.syntax.expandable.showMore")} />
|
||||
)}
|
||||
</span>
|
||||
</header>
|
||||
{expanded ? <div className="card-content">{children}</div> : null}
|
||||
@@ -201,7 +206,13 @@ const TimestampConverter: FC = () => {
|
||||
{copying ? (
|
||||
<span className="small-loading-spinner" />
|
||||
) : (
|
||||
<Icon name="clipboard" color="inherit" className="is-size-4 fa-fw is-clickable" onClick={copyTimestamp} />
|
||||
<Icon
|
||||
name="clipboard"
|
||||
color="inherit"
|
||||
className="is-size-4 fa-fw is-clickable"
|
||||
onClick={copyTimestamp}
|
||||
alt={t("search.syntax.utilities.copyTimestampTooltip")}
|
||||
/>
|
||||
)}
|
||||
</StyledTooltip>
|
||||
</span>
|
||||
|
||||
@@ -35,7 +35,8 @@ type Props = {
|
||||
|
||||
const KeyArea = styled.textarea`
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
font-family: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro", monospace;
|
||||
height: 3rem;
|
||||
`;
|
||||
@@ -64,7 +65,13 @@ const ApiKeyCreatedModal: FC<Props> = ({ addedKey, close }) => {
|
||||
<hr />
|
||||
<div className="columns">
|
||||
<div className="column is-11">
|
||||
<KeyArea wrap={"soft"} ref={keyRef} className="input" value={addedKey.token} />
|
||||
<KeyArea
|
||||
wrap={"soft"}
|
||||
ref={keyRef}
|
||||
className="input"
|
||||
value={addedKey.token}
|
||||
aria-label={t("apiKey.modal.alt")}
|
||||
/>
|
||||
</div>
|
||||
<NoLeftMargin className="column is-1">
|
||||
<Icon
|
||||
|
||||
Reference in New Issue
Block a user