This repository has been archived by the owner on Oct 27, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: multi-project api access tokens (#857)
* fix: general select component typings * custom multi-select for projects * autocomplete element for token projects * project multi-select with error handling * projects in tokens list update * multi-project tokens - select all button * fix conflicting typescript changes * improve multi-projects tokens form after review * refactor multi-project select code structure * test api token list projects column element * simplify test renderer
- Loading branch information
Showing
18 changed files
with
494 additions
and
200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
.../admin/apiToken/ApiTokenForm/SelectProjectInput/SelectAllButton/SelectAllButton.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { makeStyles } from '@material-ui/core/styles'; | ||
|
||
export const useStyles = makeStyles(theme => ({ | ||
selectOptionsLink: { | ||
cursor: 'pointer', | ||
}, | ||
})); |
23 changes: 23 additions & 0 deletions
23
...ponent/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectAllButton/SelectAllButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React, { FC } from 'react'; | ||
import { Box, Link } from '@material-ui/core'; | ||
import { useStyles } from './SelectAllButton.styles'; | ||
|
||
type SelectAllButtonProps = { | ||
isAllSelected: boolean; | ||
onClick: () => void; | ||
}; | ||
|
||
export const SelectAllButton: FC<SelectAllButtonProps> = ({ | ||
isAllSelected, | ||
onClick, | ||
}) => { | ||
const styles = useStyles(); | ||
|
||
return ( | ||
<Box sx={{ ml: 3.5, my: 0.5 }}> | ||
<Link onClick={onClick} className={styles.selectOptionsLink}> | ||
{isAllSelected ? 'Deselect all' : 'Select all'} | ||
</Link> | ||
</Box> | ||
); | ||
}; |
7 changes: 7 additions & 0 deletions
7
src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { makeStyles } from '@material-ui/core/styles'; | ||
|
||
export const useStyles = makeStyles(theme => ({ | ||
selectOptionCheckbox: { | ||
marginRight: '0.2rem', | ||
}, | ||
})); |
148 changes: 148 additions & 0 deletions
148
src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import React, { Fragment, useState, ChangeEvent, VFC } from 'react'; | ||
import { | ||
Checkbox, | ||
FormControlLabel, | ||
TextField, | ||
Box, | ||
Paper, | ||
} from '@material-ui/core'; | ||
import { | ||
Autocomplete, | ||
AutocompleteRenderGroupParams, | ||
AutocompleteRenderInputParams, | ||
AutocompleteRenderOptionState, | ||
} from '@material-ui/lab'; | ||
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; | ||
import CheckBoxIcon from '@material-ui/icons/CheckBox'; | ||
import { IAutocompleteBoxOption } from 'component/common/AutocompleteBox/AutocompleteBox'; | ||
import { useStyles } from '../ApiTokenForm.styles'; | ||
import { SelectAllButton } from './SelectAllButton/SelectAllButton'; | ||
|
||
const ALL_PROJECTS = '*'; | ||
|
||
// Fix for shadow under Autocomplete - match with Select input | ||
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />; | ||
|
||
interface ISelectProjectInputProps { | ||
disabled?: boolean; | ||
options: IAutocompleteBoxOption[]; | ||
defaultValue: string[]; | ||
onChange: (value: string[]) => void; | ||
onFocus?: () => void; | ||
error?: string; | ||
} | ||
|
||
export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({ | ||
options, | ||
defaultValue = [ALL_PROJECTS], | ||
onChange, | ||
disabled, | ||
error, | ||
onFocus, | ||
}) => { | ||
const styles = useStyles(); | ||
const [projects, setProjects] = useState<string[]>( | ||
typeof defaultValue === 'string' ? [defaultValue] : defaultValue | ||
); | ||
const [isWildcardSelected, selectWildcard] = useState( | ||
typeof defaultValue === 'string' || defaultValue.includes(ALL_PROJECTS) | ||
); | ||
const isAllSelected = projects.length === options.length; | ||
|
||
const onAllProjectsChange = ( | ||
e: ChangeEvent<HTMLInputElement>, | ||
checked: boolean | ||
) => { | ||
if (checked) { | ||
selectWildcard(true); | ||
onChange([ALL_PROJECTS]); | ||
} else { | ||
selectWildcard(false); | ||
onChange(projects.includes(ALL_PROJECTS) ? [] : projects); | ||
} | ||
}; | ||
|
||
const renderOption = ( | ||
option: IAutocompleteBoxOption, | ||
{ selected }: AutocompleteRenderOptionState | ||
) => ( | ||
<> | ||
<Checkbox | ||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />} | ||
checkedIcon={<CheckBoxIcon fontSize="small" />} | ||
checked={selected} | ||
className={styles.selectOptionCheckbox} | ||
/> | ||
{option.label} | ||
</> | ||
); | ||
|
||
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => ( | ||
<Fragment key={key}> | ||
<SelectAllButton | ||
isAllSelected={isAllSelected} | ||
onClick={() => { | ||
setProjects( | ||
isAllSelected ? [] : options.map(({ value }) => value) | ||
); | ||
}} | ||
/> | ||
{children} | ||
</Fragment> | ||
); | ||
|
||
const renderInput = (params: AutocompleteRenderInputParams) => ( | ||
<TextField | ||
{...params} | ||
error={!!error} | ||
helperText={error} | ||
variant="outlined" | ||
label="Projects" | ||
placeholder="Select one or more projects" | ||
onFocus={onFocus} | ||
/> | ||
); | ||
|
||
return ( | ||
<Box sx={{ mt: -1, mb: 3 }}> | ||
<Box sx={{ mt: 1, mb: 0.25, ml: 1.5 }}> | ||
<FormControlLabel | ||
disabled={disabled} | ||
control={ | ||
<Checkbox | ||
checked={disabled || isWildcardSelected} | ||
onChange={onAllProjectsChange} | ||
/> | ||
} | ||
label="ALL current and future projects" | ||
/> | ||
</Box> | ||
<Autocomplete | ||
disabled={disabled || isWildcardSelected} | ||
multiple | ||
limitTags={2} | ||
options={options} | ||
disableCloseOnSelect | ||
getOptionLabel={({ label }) => label} | ||
groupBy={() => 'Select/Deselect all'} | ||
renderGroup={renderGroup} | ||
fullWidth | ||
PaperComponent={CustomPaper} | ||
renderOption={renderOption} | ||
renderInput={renderInput} | ||
value={ | ||
isWildcardSelected || disabled | ||
? options | ||
: options.filter(option => | ||
projects.includes(option.value) | ||
) | ||
} | ||
onChange={(_, input) => { | ||
const state = input.map(({ value }) => value); | ||
setProjects(state); | ||
onChange(state); | ||
}} | ||
/> | ||
</Box> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.