Closed cell-kibet closed 1 year ago
Am I understanding it right, that you refer primarily to essentially a little edit box that when you start typing, it filters the possible hits to those that match what's entered? That makes sense! Would you be able to contribute that? 🙏
Yes, that's right.. kindly point me in the right direction please..
Could this be potentially related or fixed by #15063?
Hmm I think that this is more that the dropdown would be replaced with the Autocomplete
component from @material-ui/lab
instead, so that it has a list of options that you can choose from. That wouldn't be fixed with #15063 but it's interesting that it could also be powered by an Autocomplete
That is absolutely right @benjdlambert ..
Seems like a nice addition, is this something you would like to contribute @cell-kibet?
Let me go through the code and docs and give it a try. Any input and direction is welcome
I think this is the component that you want to use instead: https://v4.mui.com/components/autocomplete
And I think for GitLab you'd want to edit this file: https://github.com/backstage/backstage/blob/master/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx#L46-L57 and replace that Select
component with a Autocomplete
component.
It might be possible to always render an Autocomplete
but turn off freeSolo
if there is allowedOwners
so that you can freely type something like the Input
does underneath. But feel free to experiment.
Once you have something working, the allowedOwners
prop is also used in the other files too that you could probably update pretty safely, like the GitHub
Azure
and Bitbucket
ones too. Or wherever allowedOwners
is used. :)
Seems GitlabRepoPicker.tsx
imports "Select" from core-components file : https://github.com/backstage/backstage/blob/master/packages/core-components/src/components/Select/Select.tsx which uses the @material-ui/lab
Select
component.
After swapping "Select
" with "Autocomplete
" in file: https://github.com/backstage/backstage/blob/master/packages/core-components/src/components/Select/Select.tsx, am stuck here:
import Box from '@material-ui/core/Box';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import FormControl from '@material-ui/core/FormControl';
import InputBase from '@material-ui/core/InputBase';
import InputLabel from '@material-ui/core/InputLabel';
import Autocomplete from '@material-ui/lab/Autocomplete';
import TextField from '@material-ui/core/TextField';
import {
createStyles,
makeStyles,
Theme,
withStyles,
} from '@material-ui/core/styles';
import React, { useEffect, useState } from 'react';
/** @public */
export type SelectInputBaseClassKey = 'root' | 'input';
const BootstrapInput = withStyles(
(theme: Theme) =>
createStyles({
root: {
'label + &': {
marginTop: theme.spacing(3),
},
},
input: {
borderRadius: theme.shape.borderRadius,
position: 'relative',
backgroundColor: theme.palette.background.paper,
border: '1px solid #ced4da',
fontSize: theme.typography.body1.fontSize,
padding: theme.spacing(1.25, 3.25, 1.25, 1.5),
transition: theme.transitions.create(['border-color', 'box-shadow']),
fontFamily: 'Helvetica Neue',
'&:focus': {
background: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
},
},
}),
{ name: 'BackstageSelectInputBase' },
)(InputBase);
/** @public */
export type SelectClassKey =
| 'formControl'
| 'label'
| 'chips'
| 'chip'
| 'checkbox'
| 'root';
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
formControl: {
margin: `${theme.spacing(1)} 0px`,
maxWidth: 300,
},
label: {
transform: 'initial',
fontWeight: 'bold',
fontSize: theme.typography.body2.fontSize,
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
'&.Mui-focused': {
color: theme.palette.text.primary,
},
},
formLabel: {
transform: 'initial',
fontWeight: 'bold',
fontSize: theme.typography.body2.fontSize,
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
'&.Mui-focused': {
color: theme.palette.text.primary,
},
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
checkbox: {},
root: {
display: 'flex',
flexDirection: 'column',
},
}),
{ name: 'BackstageSelect' },
);
/** @public */
export type SelectItem = {
label: string;
value: string | number;
};
/** @public */
export type SelectedItems = string | string[] | number | number[];
export type SelectProps = {
multiple?: boolean;
items: SelectItem[];
label: string;
placeholder?: string;
selected?: SelectedItems;
onChange: (arg: SelectedItems) => void;
triggerReset?: boolean;
native?: boolean;
disabled?: boolean;
margin?: 'dense' | 'none';
};
/** @public */
export function SelectComponent(props: SelectProps) {
const {
multiple,
items,
label,
placeholder,
selected,
onChange,
triggerReset,
native = false,
disabled = false,
margin,
} = props;
const classes = useStyles();
const [value, setValue] = React.useState<SelectedItems>(
selected || (multiple ? [] : ''),
);
const [isOpen, setOpen] = useState(false);
useEffect(() => {
setValue(multiple ? [] : '');
}, [triggerReset, multiple]);
useEffect(() => {
if (selected !== undefined) {
setValue(selected);
}
}, [selected]);
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
setValue(event.target.value as SelectedItems);
onChange(event.target.value as SelectedItems);
};
const handleClick = (event: React.ChangeEvent<any>) => {
if (disabled) {
event.preventDefault();
return;
}
setOpen(previous => {
if (multiple && !(event.target instanceof HTMLElement)) {
return true;
}
return !previous;
});
};
const handleClickAway = () => {
setOpen(false);
};
const handleDelete = (selectedValue: string | number) => () => {
const newValue = (value as any[]).filter(chip => chip !== selectedValue);
setValue(newValue);
onChange(newValue);
};
return (
<Box className={classes.root}>
<ClickAwayListener onClickAway={handleClickAway}>
<FormControl className={classes.formControl}>
<InputLabel className={classes.formLabel}>{label}</InputLabel>
<Autocomplete
aria-label={label}
disabled={disabled}
data-testid="select"
multiple={multiple}
options={items}
onClick={handleClick}
open={isOpen}
tabIndex={0}
renderInput={params => (
<TextField
{...params}
label={label}
margin="dense"
FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}
variant="outlined"
InputProps={params.InputProps}
/>
)}
/>
</FormControl>
</ClickAwayListener>
</Box>
);
}
@cell-kibet so I actually think that we might not want to adjust the @backstage/core-components/Select
to be an Autocomplete
I think it's fine to continue using that Select
component. I think that we should remove the usage of that Select
component inside the picker itself and use an Autocomplete
instead.
Thank you @benjdlambert . Managed to get the Interface working by editing https://github.com/backstage/backstage/blob/master/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx to below:
import React from 'react';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import { Select, SelectItem } from '@backstage/core-components';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { RepoUrlPickerState } from './types';
import TextField from '@material-ui/core/TextField';
export const GitlabRepoPicker = (props: {
allowedOwners?: string[];
allowedRepos?: string[];
state: RepoUrlPickerState;
onChange: (state: RepoUrlPickerState) => void;
rawErrors: string[];
}) => {
const { allowedOwners = [], state, onChange, rawErrors } = props;
const ownerItems: SelectItem[] = allowedOwners
? allowedOwners.map(i => ({ label: i, value: i }))
: [{ label: 'Loading...', value: 'loading' }];
const { owner } = state;
return (
<>
<FormControl
margin="normal"
required
error={rawErrors?.length > 0 && !owner}
>
{allowedOwners?.length ? (
<Autocomplete
aria-label="Owner Available"
options={ownerItems}
getOptionLabel={(option) => option.label || "error getting label"}
disabled={allowedOwners.length === 1}
data-testid="select"
onChange={selected =>
onChange({
owner: String(Array.isArray(selected) ? selected[0] : selected),
})
}
tabIndex={0}
renderInput={params => (
<TextField
{...params}
placeholder="Owner Available"
margin="dense"
FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}
variant="outlined"
InputProps={params.InputProps}
/>
)}
/>
) : (
<>
<InputLabel htmlFor="ownerInput">Owner</InputLabel>
<Input
id="ownerInput"
onChange={e => onChange({ owner: e.target.value })}
value={owner}
/>
</>
)}
<FormHelperText>
GitLab namespace where this repository will belong to. It can be the
name of organization, group, subgroup, user, or the project.
</FormHelperText>
</FormControl>
</>
);
};
Kindly test
@cell-kibet nice - do you want to raise a PR with these changes in the other places too? Like the GithubRepoUrlPicker
and the other pickers that also live alongside it for the other providers too and we can do a bigger test then on the branch?
Nice work so far! :pray:
@cell-kibet nice - do you want to raise a PR with these changes in the other places too? Like the
GithubRepoUrlPicker
and the other pickers that also live alongside it for the other providers too and we can do a bigger test then on the branch?Nice work so far! 🙏
Hey @benjdlambert kindly add my account to the repo to push to a new branch
@cell-kibet we encourage you to fork the project, and push to your own branches there and then raise a PR back to the original project :)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
🔖 Feature description
Expected Behavior
First of all, amazing work you guys are doing! When multiple allowedOwners are in the template, the user has to scroll through a long list before selecting one while filling out the rest of the template form. The user would like to search and easily pick one from the long list.
Actual Behavior
Searching for one value of GitLab allowedOwners for the RepoUrlPicker I have a very long list of GitLab allowedOwners for the RepoUrlPicker and requesting if a search feature can be added to quicken picking of GitLab groups.
Steps to Reproduce
Ability to search the allowedOwners during creation of template
🎤 Context
In case there is a very long list of allowedOwners, it is cumbersome to scroll theough the number of options or list applied on the Template's RepoUrlPicker allowedOwners. A filter feature would really be useful to easily pick out one or two from the long drop-down list.
Your Environment
This behaviour happens in Safari and Firefox, though I mainly work in Firefox.
Output of yarn backstage-cli info: yarn run v1.22.19
OS: Darwin 22.1.0 - darwin/arm64 node: v16.17.1 yarn: 1.22.19 cli: 0.21.1 (installed) backstage: 1.8.2
Dependencies: @backstage/app-defaults 1.0.8 @backstage/backend-common 0.16.0 @backstage/backend-plugin-api 0.1.4 @backstage/backend-tasks 0.3.7 @backstage/catalog-client 1.1.2 @backstage/catalog-model 1.1.3 @backstage/cli-common 0.1.10 @backstage/cli 0.21.1 @backstage/config-loader 1.1.6 @backstage/config 1.0.4 @backstage/core-app-api 1.2.0 @backstage/core-components 0.12.0, 0.9.5 @backstage/core-plugin-api 1.1.0 @backstage/errors 1.1.3 @backstage/integration-react 1.1.6 @backstage/integration 1.4.0 @backstage/plugin-api-docs 0.8.11 @backstage/plugin-app-backend 0.3.38 @backstage/plugin-auth-backend 0.17.1 @backstage/plugin-auth-node 0.2.7 @backstage/plugin-catalog-backend-module-gitlab 0.1.9 @backstage/plugin-catalog-backend 1.5.1 @backstage/plugin-catalog-common 1.0.8 @backstage/plugin-catalog-graph 0.2.23 @backstage/plugin-catalog-import 0.9.1 @backstage/plugin-catalog-node 1.2.1 @backstage/plugin-catalog-react 1.2.1 @backstage/plugin-catalog 1.6.1 @backstage/plugin-github-actions 0.5.11 @backstage/plugin-kubernetes-backend 0.8.0 @backstage/plugin-kubernetes-common 0.4.4 @backstage/plugin-kubernetes 0.7.4 @backstage/plugin-newrelic 0.3.29 @backstage/plugin-org 0.6.0 @backstage/plugin-permission-common 0.7.1 @backstage/plugin-permission-node 0.7.1 @backstage/plugin-permission-react 0.4.7 @backstage/plugin-proxy-backend 0.2.32 @backstage/plugin-scaffolder-backend 1.8.0 @backstage/plugin-scaffolder-common 1.2.2 @backstage/plugin-scaffolder 1.8.0 @backstage/plugin-search-backend-module-pg 0.4.2 @backstage/plugin-search-backend-node 1.0.4 @backstage/plugin-search-backend 1.1.1 @backstage/plugin-search-common 1.1.1 @backstage/plugin-search-react 1.2.1 @backstage/plugin-search 1.0.4 @backstage/plugin-sonarqube-backend 0.1.3 @backstage/plugin-sonarqube 0.5.0 @backstage/plugin-tech-radar 0.5.18 @backstage/plugin-techdocs-backend 1.4.1 @backstage/plugin-techdocs-module-addons-contrib 1.0.6 @backstage/plugin-techdocs-node 1.4.2 @backstage/plugin-techdocs-react 1.0.6 @backstage/plugin-techdocs 1.4.0 @backstage/plugin-user-settings 0.5.1 @backstage/release-manifests 0.0.7 @backstage/test-utils 1.2.2 @backstage/theme 0.2.16 @backstage/types 1.0.1 @backstage/version-bridge 1.0.2
✌️ Possible Implementation
Enable ability to search a word from the drop-down list generated
👀 Have you spent some time to check if this feature request has been raised before?
🏢 Have you read the Code of Conduct?
Are you willing to submit PR?
None