Autocomplete for namespaces (#1916)

Changes the "namespace" input in the repository creation form or the "rename repository" dialog to an
autocomplete input. Of course this is only available for the "custom" namespace strategy.
This commit is contained in:
René Pfeuffer
2022-01-11 16:17:57 +01:00
committed by GitHub
parent 01fa96d29c
commit e9f22e89ec
20 changed files with 346 additions and 77 deletions

View File

@@ -37,8 +37,11 @@ type Props = {
placeholder: string;
loadingMessage: string;
noOptionsMessage: string;
errorMessage?: string;
informationMessage?: string;
creatable?: boolean;
className?: string;
disabled?: boolean;
};
type State = {};
@@ -78,8 +81,11 @@ class Autocomplete extends React.Component<Props, State> {
loadingMessage,
noOptionsMessage,
loadSuggestions,
errorMessage,
informationMessage,
creatable,
className
className,
disabled
} = this.props;
return (
@@ -108,6 +114,7 @@ class Autocomplete extends React.Component<Props, State> {
});
}}
aria-label={helpText || label}
isDisabled={disabled}
/>
) : (
<Async
@@ -121,9 +128,12 @@ class Autocomplete extends React.Component<Props, State> {
loadingMessage={() => loadingMessage}
noOptionsMessage={() => noOptionsMessage}
aria-label={helpText || label}
isDisabled={disabled}
/>
)}
</div>
{errorMessage ? <p className="help is-danger">{errorMessage}</p> : null}
{informationMessage ? <p className="help is-info">{informationMessage}</p> : null}
</div>
);
}

View File

@@ -19219,6 +19219,10 @@ exports[`Storyshots Modal/Modal With form elements 1`] = `null`;
exports[`Storyshots Modal/Modal With long tooltips 1`] = `null`;
exports[`Storyshots Modal/Modal With overflow 1`] = `null`;
exports[`Storyshots Modal/Modal With overflow and footer 1`] = `null`;
exports[`Storyshots Notification Closeable 1`] = `
<div
className="Notificationstories__Wrapper-sc-8fx7tr-0 gJPbVB"

View File

@@ -32,6 +32,8 @@ import ExternalLink from "../navigation/ExternalLink";
import { Radio, Textarea, InputField } from "../forms";
import { ButtonGroup, Button } from "../buttons";
import Notification from "../Notification";
import { Autocomplete } from "../index";
import { SelectValue } from "@scm-manager/ui-types";
const TopAndBottomMargin = styled.div`
margin: 0.75rem 0; // only for aesthetic reasons
@@ -51,7 +53,9 @@ const text = `Mind-paralyzing change needed improbability vortex machine sorts s
ordinary mob.`;
// eslint-disable-next-line @typescript-eslint/no-empty-function
const doNothing = () => {};
const doNothing = () => {
// nothing to do
};
const withFormElementsBody = (
<>
<RadioList>
@@ -71,8 +75,21 @@ const withFormElementsFooter = (
</ButtonGroup>
);
const loadSuggestions: (p: string) => Promise<SelectValue[]> = () =>
new Promise(resolve => {
setTimeout(() => {
resolve([
{ value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
{ value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
{ value: { id: "ford", displayName: "Ford Prefect" }, label: "Ford Prefect" },
{ value: { id: "dent", displayName: "Arthur Dent" }, label: "Arthur Dent" },
{ value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " }
]);
});
});
storiesOf("Modal/Modal", module)
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.add("Default", () => (
<NonCloseableModal>
<p>{text}</p>
@@ -104,7 +121,7 @@ storiesOf("Modal/Modal", module)
This story exists because we had a problem, that long tooltips causes a horizontal scrollbar on the modal.
</Notification>
<hr />
<p>The following elements will have a verly long help text, which has triggered the scrollbar in the past.</p>
<p>The following elements will have a very long help text, which has triggered the scrollbar in the past.</p>
<hr />
<TopAndBottomMargin>
<Checkbox label="Checkbox" checked={true} helpText={text} />
@@ -211,10 +228,47 @@ storiesOf("Modal/Modal", module)
</p>
</div>
</NonCloseableModal>
));
))
.add("With overflow", () => {
return (
<NonCloseableModal overflowVisible={true}>
<h1 className="title">Please Select</h1>
<Autocomplete
valueSelected={() => {
// nothing to do
}}
loadSuggestions={loadSuggestions}
/>
</NonCloseableModal>
);
})
.add("With overflow and footer", () => {
return (
<NonCloseableModal overflowVisible={true} footer={withFormElementsFooter}>
<h1 className="title">Please Select</h1>
<Autocomplete
valueSelected={() => {
// nothing to do
}}
loadSuggestions={loadSuggestions}
/>
</NonCloseableModal>
);
});
const NonCloseableModal: FC = ({ children }) => {
return <Modal body={children} closeFunction={doNothing} active={true} title={"Hitchhiker Modal"} />;
type NonCloseableModalProps = { overflowVisible?: boolean; footer?: any };
const NonCloseableModal: FC<NonCloseableModalProps> = ({ overflowVisible, footer, children }) => {
return (
<Modal
body={children}
closeFunction={doNothing}
active={true}
title={"Hitchhiker Modal"}
overflowVisible={overflowVisible}
footer={footer}
/>
);
};
const CloseableModal: FC = ({ children }) => {

View File

@@ -42,10 +42,24 @@ type Props = {
headColor?: string;
headTextColor?: string;
size?: ModalSize;
overflowVisible?: boolean;
};
const SizedModal = styled.div<{ size?: ModalSize }>`
const SizedModal = styled.div<{ size?: ModalSize; overflow: string }>`
width: ${props => (props.size ? `${modalSizes[props.size]}%` : "640px")};
overflow: ${props => props.overflow};
`;
const DivWithOptionalOverflow = styled.div<{ overflow: string; borderBottomRadius: string }>`
overflow: ${props => props.overflow};
border-bottom-left-radius: ${props => props.borderBottomRadius};
border-bottom-right-radius: ${props => props.borderBottomRadius};
`;
const SectionWithOptionalOverflow = styled.section<{ overflow: string; borderBottomRadius: string }>`
overflow: ${props => props.overflow};
border-bottom-left-radius: ${props => props.borderBottomRadius};
border-bottom-right-radius: ${props => props.borderBottomRadius};
`;
export const Modal: FC<Props> = ({
@@ -57,7 +71,8 @@ export const Modal: FC<Props> = ({
className,
headColor = "secondary-less",
headTextColor = "secondary-most",
size
size,
overflowVisible
}) => {
const portalRootElement = usePortalRootElement("modalsRoot");
const initialFocusRef = useRef(null);
@@ -85,18 +100,29 @@ export const Modal: FC<Props> = ({
}
};
const overflowAttribute = overflowVisible ? "visible" : "auto";
const borderBottomRadiusAttribute = overflowVisible && !footer ? "inherit" : "unset";
const modalElement = (
<div className={classNames("modal", className, isActive)} ref={trapRef} onKeyDown={onKeyDown}>
<DivWithOptionalOverflow
className={classNames("modal", className, isActive)}
ref={trapRef}
onKeyDown={onKeyDown}
overflow={overflowAttribute}
borderBottomRadius={borderBottomRadiusAttribute}
>
<div className="modal-background" onClick={closeFunction} />
<SizedModal className="modal-card" size={size}>
<SizedModal className="modal-card" size={size} overflow={overflowAttribute}>
<header className={classNames("modal-card-head", `has-background-${headColor}`)}>
<h2 className={`modal-card-title m-0 has-text-${headTextColor}`}>{title}</h2>
<button className="delete" aria-label="close" onClick={closeFunction} ref={initialFocusRef} autoFocus />
</header>
<section className="modal-card-body">{body}</section>
<SectionWithOptionalOverflow className="modal-card-body" overflow={overflowAttribute} borderBottomRadius={borderBottomRadiusAttribute}>
{body}
</SectionWithOptionalOverflow>
{showFooter}
</SizedModal>
</div>
</DivWithOptionalOverflow>
);
return ReactDOM.createPortal(modalElement, portalRootElement);