fix autocompletion

This commit is contained in:
Florian Scholdei
2019-06-12 09:47:13 +02:00
parent bfd22aa507
commit 303e7f1e50
10 changed files with 235 additions and 130 deletions

View File

@@ -4,7 +4,6 @@ import { AsyncCreatable, Async } from "react-select";
import type { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
import LabelWithHelpIcon from "./forms/LabelWithHelpIcon";
type Props = {
loadSuggestions: string => Promise<AutocompleteObject>,
valueSelected: SelectValue => void,
@@ -17,12 +16,9 @@ type Props = {
creatable?: boolean
};
type State = {};
class Autocomplete extends React.Component<Props, State> {
static defaultProps = {
placeholder: "Type here",
loadingMessage: "Loading...",
@@ -34,7 +30,11 @@ class Autocomplete extends React.Component<Props, State> {
};
// We overwrite this to avoid running into a bug (https://github.com/JedWatson/react-select/issues/2944)
isValidNewOption = (inputValue: string, selectValue: SelectValue, selectOptions: SelectValue[]) => {
isValidNewOption = (
inputValue: string,
selectValue: SelectValue,
selectOptions: SelectValue[]
) => {
const isNotDuplicated = !selectOptions
.map(option => option.label)
.includes(inputValue);
@@ -43,12 +43,21 @@ class Autocomplete extends React.Component<Props, State> {
};
render() {
const { label, helpText, value, placeholder, loadingMessage, noOptionsMessage, loadSuggestions, creatable } = this.props;
const {
label,
helpText,
value,
placeholder,
loadingMessage,
noOptionsMessage,
loadSuggestions,
creatable
} = this.props;
return (
<div className="field">
<LabelWithHelpIcon label={label} helpText={helpText} />
<div className="control">
{creatable?
{creatable ? (
<AsyncCreatable
cacheOptions
loadOptions={loadSuggestions}
@@ -65,7 +74,7 @@ class Autocomplete extends React.Component<Props, State> {
});
}}
/>
:
) : (
<Async
cacheOptions
loadOptions={loadSuggestions}
@@ -75,13 +84,11 @@ class Autocomplete extends React.Component<Props, State> {
loadingMessage={() => loadingMessage}
noOptionsMessage={() => noOptionsMessage}
/>
}
)}
</div>
</div>
);
}
}
export default Autocomplete;

View File

@@ -0,0 +1,37 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { SelectValue } from "@scm-manager/ui-types";
import UserGroupAutocomplete from "./UserGroupAutocomplete";
type Props = {
groupAutocompleteLink: string,
valueSelected: SelectValue => void,
value?: SelectValue,
// Context props
t: string => string
};
class GroupAutocomplete extends React.Component<Props> {
selectName = (selection: SelectValue) => {
this.props.valueSelected(selection);
};
render() {
const { groupAutocompleteLink, t, value } = this.props;
return (
<UserGroupAutocomplete
autocompleteLink={groupAutocompleteLink}
label={t("autocomplete.group")}
noOptionsMessage={t("autocomplete.noGroupOptions")}
loadingMessage={t("autocomplete.loading")}
placeholder={t("autocomplete.groupPlaceholder")}
valueSelected={this.selectName}
value={value}
/>
);
}
}
export default translate("commons")(GroupAutocomplete);

View File

@@ -0,0 +1,37 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { SelectValue } from "@scm-manager/ui-types";
import UserGroupAutocomplete from "./UserGroupAutocomplete";
type Props = {
userAutocompleteLink: string,
valueSelected: SelectValue => void,
value?: SelectValue,
// Context props
t: string => string
};
class UserAutocomplete extends React.Component<Props> {
selectName = (selection: SelectValue) => {
this.props.valueSelected(selection);
};
render() {
const { userAutocompleteLink, t, value } = this.props;
return (
<UserGroupAutocomplete
autocompleteLink={userAutocompleteLink}
label={t("autocomplete.user")}
noOptionsMessage={t("autocomplete.noUserOptions")}
loadingMessage={t("autocomplete.loading")}
placeholder={t("autocomplete.userPlaceholder")}
valueSelected={this.selectName}
value={value}
/>
);
}
}
export default translate("commons")(UserAutocomplete);

View File

@@ -0,0 +1,57 @@
// @flow
import React from "react";
import type { SelectValue } from "@scm-manager/ui-types";
import Autocomplete from "./Autocomplete";
type Props = {
autocompleteLink: string,
label: string,
noOptionsMessage: string,
loadingMessage: string,
placeholder: string,
valueSelected: SelectValue => void,
value?: SelectValue
};
class UserGroupAutocomplete extends React.Component<Props> {
loadSuggestions = (inputValue: string) => {
const url = this.props.autocompleteLink;
const link = url + "?q=";
return fetch(link + inputValue)
.then(response => response.json())
.then(json => {
return json.map(element => {
const label = element.displayName
? `${element.displayName} (${element.id})`
: element.id;
return {
value: element,
label
};
});
});
};
selectName = (selection: SelectValue) => {
this.props.valueSelected(selection);
};
render() {
const { autocompleteLink, label, noOptionsMessage, loadingMessage, placeholder, value } = this.props;
return (
<Autocomplete
autocompleteLink={autocompleteLink}
label={label}
noOptionsMessage={noOptionsMessage}
loadingMessage={loadingMessage}
placeholder={placeholder}
loadSuggestions={this.loadSuggestions}
valueSelected={this.selectName}
value={value}
creatable={true}
/>
); // {...this.props}
}
}
export default UserGroupAutocomplete;

View File

@@ -40,6 +40,15 @@
"config": "Einstellungen"
},
"filterEntries": "Einträge filtern",
"autocomplete": {
"group": "Gruppe",
"user": "Benutzer",
"noGroupOptions": "Kein Gruppenname als Vorschlag verfügbar",
"groupPlaceholder": "Gruppe eingeben",
"noUserOptions": "Kein Benutzername als Vorschlag verfügbar",
"userPlaceholder": "Benutzer eingeben",
"loading": "suche..."
},
"paginator": {
"next": "Weiter",
"previous": "Zurück"

View File

@@ -150,13 +150,6 @@
"roleHelpText": "READ = read; WRITE = read und write; OWNER = read, write und auch die Möglichkeit Einstellungen und Berechtigungen zu verwalten. Wenn hier nichts angezeigt wird, den Erweitert-Button benutzen, um Details zu sehen.",
"permissionsHelpText": "Hier können individuelle Berechtigungen unabhängig von vordefinierten Rollen vergeben werden."
},
"autocomplete": {
"no-group-options": "Kein Gruppenname als Vorschlag verfügbar",
"group-placeholder": "Gruppe eingeben",
"no-user-options": "Kein Benutzername als Vorschlag verfügbar",
"user-placeholder": "Benutzer eingeben",
"loading": "suche..."
},
"advanced": {
"dialog": {
"title": "Erweiterte Berechtigungen",

View File

@@ -40,6 +40,15 @@
"config": "Configuration"
},
"filterEntries": "filter entries",
"autocomplete": {
"group": "Group",
"user": "User",
"noGroupOptions": "No group suggestion available",
"groupPlaceholder": "Enter group",
"noUserOptions": "No user suggestion available",
"userPlaceholder": "Enter user",
"loading": "Loading..."
},
"paginator": {
"next": "Next",
"previous": "Previous"

View File

@@ -153,13 +153,6 @@
"roleHelpText": "READ = read; WRITE = read and write; OWNER = read, write and also the ability to manage the properties and permissions. If nothing is selected here, use the 'Advanced' Button to see detailed permissions.",
"permissionsHelpText": "Use this to specify your own set of permissions regardless of predefined roles."
},
"autocomplete": {
"no-group-options": "No group suggestion available",
"group-placeholder": "Enter group",
"no-user-options": "No user suggestion available",
"user-placeholder": "Enter user",
"loading": "Loading..."
},
"advanced": {
"dialog": {
"title": "Advanced Permissions",

View File

@@ -9,11 +9,12 @@ import type {
} from "@scm-manager/ui-types";
import {
Subtitle,
Autocomplete,
SubmitButton,
Button,
LabelWithHelpIcon,
Radio
Radio,
GroupAutocomplete,
UserAutocomplete
} from "@scm-manager/ui-components";
import * as validator from "../components/permissionValidation";
import RoleSelector from "../components/RoleSelector";
@@ -26,8 +27,8 @@ type Props = {
createPermission: (permission: PermissionCreateEntry) => void,
loading: boolean,
currentPermissions: PermissionCollection,
groupAutoCompleteLink: string,
userAutoCompleteLink: string,
groupAutocompleteLink: string,
userAutocompleteLink: string,
// Context props
t: string => string
@@ -68,65 +69,27 @@ class CreatePermissionForm extends React.Component<Props, State> {
});
};
loadUserAutocompletion = (inputValue: string) => {
return this.loadAutocompletion(this.props.userAutoCompleteLink, inputValue);
};
loadGroupAutocompletion = (inputValue: string) => {
return this.loadAutocompletion(
this.props.groupAutoCompleteLink,
inputValue
);
};
loadAutocompletion(url: string, inputValue: string) {
const link = url + "?q=";
return fetch(link + inputValue)
.then(response => response.json())
.then(json => {
return json.map(element => {
const label = element.displayName
? `${element.displayName} (${element.id})`
: element.id;
return {
value: element,
label
};
});
});
}
renderAutocompletionField = () => {
const { t } = this.props;
if (this.state.groupPermission) {
const group = this.state.groupPermission;
if (group) {
return (
<Autocomplete
loadSuggestions={this.loadGroupAutocompletion}
valueSelected={this.groupOrUserSelected}
<GroupAutocomplete
groupAutocompleteLink={this.props.groupAutocompleteLink}
valueSelected={this.selectName}
value={this.state.value ? this.state.value : ""}
label={t("permission.group")}
noOptionsMessage={t("permission.autocomplete.no-group-options")}
loadingMessage={t("permission.autocomplete.loading")}
placeholder={t("permission.autocomplete.group-placeholder")}
creatable={true}
/>
);
}
return (
<Autocomplete
loadSuggestions={this.loadUserAutocompletion}
valueSelected={this.groupOrUserSelected}
<UserAutocomplete
userAutocompleteLink={this.props.userAutocompleteLink}
valueSelected={this.selectName}
value={this.state.value ? this.state.value : ""}
label={t("permission.user")}
noOptionsMessage={t("permission.autocomplete.no-user-options")}
loadingMessage={t("permission.autocomplete.loading")}
placeholder={t("permission.autocomplete.user-placeholder")}
creatable={true}
/>
);
};
groupOrUserSelected = (value: SelectValue) => {
selectName = (value: SelectValue) => {
this.setState({
value,
name: value.value.id,
@@ -150,15 +113,17 @@ class CreatePermissionForm extends React.Component<Props, State> {
<AdvancedPermissionsDialog
availableVerbs={availableVerbs}
selectedVerbs={selectedVerbs}
onClose={this.closeAdvancedPermissionsDialog}
onClose={this.toggleAdvancedPermissionsDialog}
onSubmit={this.submitAdvancedPermissionsDialog}
/>
) : null;
return (
<div>
<>
<hr />
<Subtitle subtitle={t("permission.add-permission.add-permission-heading")} />
<Subtitle
subtitle={t("permission.add-permission.add-permission-heading")}
/>
{advancedDialog}
<form onSubmit={this.submit}>
<div className="field is-grouped">
@@ -201,7 +166,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
/>
<Button
label={t("permission.advanced-button.label")}
action={this.handleDetailedPermissionsPressed}
action={this.toggleAdvancedPermissionsDialog}
/>
</div>
</div>
@@ -217,16 +182,14 @@ class CreatePermissionForm extends React.Component<Props, State> {
</div>
</div>
</form>
</div>
</>
);
}
handleDetailedPermissionsPressed = () => {
this.setState({ showAdvancedDialog: true });
};
closeAdvancedPermissionsDialog = () => {
this.setState({ showAdvancedDialog: false });
toggleAdvancedPermissionsDialog = () => {
this.setState(prevState => ({
showAdvancedDialog: !prevState.showAdvancedDialog
}));
};
submitAdvancedPermissionsDialog = (newVerbs: string[]) => {

View File

@@ -60,8 +60,8 @@ type Props = {
repositoryRolesLink: string,
repositoryVerbsLink: string,
permissionsLink: string,
groupAutoCompleteLink: string,
userAutoCompleteLink: string,
groupAutocompleteLink: string,
userAutocompleteLink: string,
//dispatch functions
fetchAvailablePermissionsIfNeeded: (
@@ -129,8 +129,8 @@ class Permissions extends React.Component<Props> {
repoName,
loadingCreatePermission,
hasPermissionToCreate,
userAutoCompleteLink,
groupAutoCompleteLink
userAutocompleteLink,
groupAutocompleteLink
} = this.props;
if (error) {
return (
@@ -153,8 +153,8 @@ class Permissions extends React.Component<Props> {
createPermission={permission => this.createPermission(permission)}
loading={loadingCreatePermission}
currentPermissions={permissions}
userAutoCompleteLink={userAutoCompleteLink}
groupAutoCompleteLink={groupAutoCompleteLink}
userAutocompleteLink={userAutocompleteLink}
groupAutocompleteLink={groupAutocompleteLink}
/>
) : null;
@@ -163,41 +163,41 @@ class Permissions extends React.Component<Props> {
<Subtitle subtitle={t("permission.title")} />
<table className="card-table table is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<LabelWithHelpIcon
label={t("permission.name")}
helpText={t("permission.help.nameHelpText")}
/>
</th>
<th>
<LabelWithHelpIcon
label={t("permission.role")}
helpText={t("permission.help.roleHelpText")}
/>
</th>
<th>
<LabelWithHelpIcon
label={t("permission.permissions")}
helpText={t("permission.help.permissionsHelpText")}
/>
</th>
<th />
</tr>
<tr>
<th>
<LabelWithHelpIcon
label={t("permission.name")}
helpText={t("permission.help.nameHelpText")}
/>
</th>
<th>
<LabelWithHelpIcon
label={t("permission.role")}
helpText={t("permission.help.roleHelpText")}
/>
</th>
<th>
<LabelWithHelpIcon
label={t("permission.permissions")}
helpText={t("permission.help.permissionsHelpText")}
/>
</th>
<th />
</tr>
</thead>
<tbody>
{permissions.map(permission => {
return (
<SinglePermission
availableRepositoryRoles={availableRepositoryRoles}
availableRepositoryVerbs={availableVerbs}
key={permission.name + permission.groupPermission.toString()}
namespace={namespace}
repoName={repoName}
permission={permission}
/>
);
})}
{permissions.map(permission => {
return (
<SinglePermission
availableRepositoryRoles={availableRepositoryRoles}
availableRepositoryVerbs={availableVerbs}
key={permission.name + permission.groupPermission.toString()}
namespace={namespace}
repoName={repoName}
permission={permission}
/>
);
})}
</tbody>
</table>
{createPermissionForm}
@@ -228,8 +228,8 @@ const mapStateToProps = (state, ownProps) => {
const repositoryRolesLink = getRepositoryRolesLink(state);
const repositoryVerbsLink = getRepositoryVerbsLink(state);
const permissionsLink = getPermissionsLink(state, namespace, repoName);
const groupAutoCompleteLink = getGroupAutoCompleteLink(state);
const userAutoCompleteLink = getUserAutoCompleteLink(state);
const groupAutocompleteLink = getGroupAutoCompleteLink(state);
const userAutocompleteLink = getUserAutoCompleteLink(state);
const availablePermissions = getAvailablePermissions(state);
const availableRepositoryRoles = getAvailableRepositoryRoles(state);
const availableVerbs = getAvailableRepositoryVerbs(state);
@@ -248,8 +248,8 @@ const mapStateToProps = (state, ownProps) => {
hasPermissionToCreate,
loadingCreatePermission,
permissionsLink,
groupAutoCompleteLink,
userAutoCompleteLink
groupAutocompleteLink,
userAutocompleteLink
};
};