mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
Improve modal accessibility
Implement initial focus for modals. Change all modals including forms to put initial focus on the first input. When Enter is pressed on any input (CTRL + Enter for Textareas), the form is submitted if it is valid. Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
committed by
GitHub
parent
d8fcb12402
commit
d0cf976a54
@@ -22,12 +22,13 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import * as React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { FC, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
|
||||
import { Button, ButtonGroup, ErrorNotification, Modal } from "@scm-manager/ui-components";
|
||||
import SuccessNotification from "./SuccessNotification";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
pendingPlugins?: PendingPlugins;
|
||||
installedPlugins?: PluginCollection;
|
||||
@@ -37,148 +38,131 @@ type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
error?: Error | null;
|
||||
success: boolean;
|
||||
children?: React.Node;
|
||||
};
|
||||
|
||||
class PluginActionModal extends React.Component<Props> {
|
||||
renderNotifications = () => {
|
||||
const { children, error, success } = this.props;
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
} else if (success) {
|
||||
return <SuccessNotification />;
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
};
|
||||
const PluginActionModal: FC<Props> = ({
|
||||
error,
|
||||
success,
|
||||
children,
|
||||
installedPlugins,
|
||||
pendingPlugins,
|
||||
description,
|
||||
label,
|
||||
loading,
|
||||
onClose,
|
||||
execute
|
||||
}) => {
|
||||
const [t] = useTranslation("admin");
|
||||
const initialFocusRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
renderModalContent = () => {
|
||||
return (
|
||||
<>
|
||||
{this.renderUpdatable()}
|
||||
{this.renderInstallQueue()}
|
||||
{this.renderUpdateQueue()}
|
||||
{this.renderUninstallQueue()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderUpdatable = () => {
|
||||
const { installedPlugins, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{installedPlugins && installedPlugins._embedded && installedPlugins._embedded.plugins && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<ul>
|
||||
{installedPlugins._embedded.plugins
|
||||
.filter(plugin => plugin._links && plugin._links.update)
|
||||
.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderInstallQueue = () => {
|
||||
const { pendingPlugins, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.new.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.installQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.new.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderUpdateQueue = () => {
|
||||
const { pendingPlugins, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.update.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.update.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderUninstallQueue = () => {
|
||||
const { pendingPlugins, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.uninstall.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.uninstallQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.uninstall.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderBody = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="media">
|
||||
<div className="content">
|
||||
<p>{this.props.description}</p>
|
||||
{this.renderModalContent()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="media">{this.renderNotifications()}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderFooter = () => {
|
||||
const { onClose, t, loading, error, success } = this.props;
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
color="warning"
|
||||
label={this.props.label}
|
||||
loading={loading}
|
||||
action={this.props.execute}
|
||||
disabled={!!error || success}
|
||||
/>
|
||||
<Button label={t("plugins.modal.abort")} action={onClose} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onClose } = this.props;
|
||||
return (
|
||||
<Modal
|
||||
title={this.props.label}
|
||||
closeFunction={onClose}
|
||||
body={this.renderBody()}
|
||||
footer={this.renderFooter()}
|
||||
active={true}
|
||||
/>
|
||||
);
|
||||
let notifications;
|
||||
if (error) {
|
||||
notifications = <ErrorNotification error={error} />;
|
||||
} else if (success) {
|
||||
notifications = <SuccessNotification />;
|
||||
} else {
|
||||
notifications = children;
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("admin")(PluginActionModal);
|
||||
const updatable = (
|
||||
<>
|
||||
{installedPlugins && installedPlugins._embedded && installedPlugins._embedded.plugins && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<ul>
|
||||
{installedPlugins._embedded.plugins
|
||||
.filter(plugin => plugin._links && plugin._links.update)
|
||||
.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const installQueue = (
|
||||
<>
|
||||
{pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.new.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.installQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.new.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const updateQueue = pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.update.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.update.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
|
||||
const uninstallQueue = pendingPlugins && pendingPlugins._embedded && pendingPlugins._embedded.uninstall.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.uninstallQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.uninstall.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
|
||||
const content = (
|
||||
<>
|
||||
{updatable}
|
||||
{installQueue}
|
||||
{updateQueue}
|
||||
{uninstallQueue}
|
||||
</>
|
||||
);
|
||||
|
||||
const body = (
|
||||
<>
|
||||
<div className="media">
|
||||
<div className="content">
|
||||
<p>{description}</p>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<div className="media">{notifications}</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
color="warning"
|
||||
label={label}
|
||||
loading={loading}
|
||||
action={execute}
|
||||
disabled={!!error || success}
|
||||
ref={initialFocusRef}
|
||||
/>
|
||||
<Button label={t("plugins.modal.abort")} action={onClose} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={label}
|
||||
closeFunction={onClose}
|
||||
body={body}
|
||||
footer={footer}
|
||||
active={true}
|
||||
initialFocusRef={initialFocusRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginActionModal;
|
||||
|
||||
Reference in New Issue
Block a user