1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-28 17:55:15 +02:00
unleash.unleash/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx
andreas-unleash 350b55644a
Feat/project api token permissions (#3065)
<!-- Thanks for creating a PR! To make it easier for reviewers and
everyone else to understand what your changes relate to, please add some
relevant content to the headings below. Feel free to ignore or delete
sections that you don't think are relevant. Thank you! ❤️ -->

## About the changes
<!-- Describe the changes introduced. What are they and why are they
being introduced? Feel free to also add screenshots or steps to view the
changes if they're visual. -->
Define and implements Project api token permissions
Assign permissions to existing roles
Adjust UI to support them 
Adjust BE to implement

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com>
2023-02-17 12:15:36 +02:00

198 lines
6.1 KiB
TypeScript

import React, { ChangeEvent, Fragment, useState, VFC } from 'react';
import { IAutocompleteBoxOption } from '../../../common/AutocompleteBox/AutocompleteBox';
import {
AutocompleteRenderGroupParams,
AutocompleteRenderInputParams,
AutocompleteRenderOptionState,
} from '@mui/material/Autocomplete';
import { styled } from '@mui/system';
import {
capitalize,
Checkbox,
Paper,
TextField,
Autocomplete,
Typography,
} from '@mui/material';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
import { SelectAllButton } from '../../../admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectAllButton/SelectAllButton';
import {
StyledHelpText,
StyledSelectAllFormControlLabel,
StyledTitle,
} from '../AddonForm.styles';
export interface IAddonMultiSelectorProps {
options: IAutocompleteBoxOption[];
selectedItems: string[];
onChange: (value: string[]) => void;
error?: string;
onFocus?: () => void;
entityName: string;
selectAllEnabled: boolean;
description?: string;
required?: boolean;
}
const ALL_OPTIONS = '*';
const StyledCheckbox = styled(Checkbox)(() => ({
marginRight: '0.2em',
}));
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
export const AddonMultiSelector: VFC<IAddonMultiSelectorProps> = ({
options,
selectedItems,
onChange,
error,
onFocus,
entityName,
selectAllEnabled = true,
description,
required,
}) => {
const [isWildcardSelected, selectWildcard] = useState(
selectedItems.includes(ALL_OPTIONS)
);
const renderInput = (params: AutocompleteRenderInputParams) => (
<TextField
{...params}
error={Boolean(error)}
helperText={error}
variant="outlined"
label={`${capitalize(entityName)}s`}
placeholder={`Select ${entityName}s to filter by`}
onFocus={onFocus}
data-testid={`select-${entityName}-input`}
/>
);
const isAllSelected =
selectedItems.length > 0 &&
selectedItems.length === options.length &&
selectedItems[0] !== ALL_OPTIONS;
const onAllItemsChange = (
e: ChangeEvent<HTMLInputElement>,
checked: boolean
) => {
if (checked) {
selectWildcard(true);
onChange([ALL_OPTIONS]);
} else {
selectWildcard(false);
onChange(selectedItems.includes(ALL_OPTIONS) ? [] : selectedItems);
}
};
const onSelectAllClick = () => {
const newItems = isAllSelected ? [] : options.map(({ value }) => value);
onChange(newItems);
};
const renderOption = (
props: object,
option: IAutocompleteBoxOption,
{ selected }: AutocompleteRenderOptionState
) => {
return (
<li {...props}>
<StyledCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
checkedIcon={<CheckBoxIcon fontSize="small" />}
checked={selected}
/>
{option.label}
</li>
);
};
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
<Fragment key={key}>
<ConditionallyRender
condition={options.length > 2 && selectAllEnabled}
show={
<SelectAllButton
isAllSelected={isAllSelected}
onClick={onSelectAllClick}
/>
}
/>
{children}
</Fragment>
);
const SelectAllFormControl = () => (
<StyledSelectAllFormControlLabel
data-testid={`select-all-${entityName}s`}
control={
<Checkbox
checked={isWildcardSelected}
onChange={onAllItemsChange}
/>
}
label={`ALL current and future ${entityName}s`}
/>
);
const DefaultHelpText = () => (
<StyledHelpText>
Selecting {entityName}(s) here will filter events so that your addon
will only receive events that are tagged with one of your{' '}
{entityName}s.
</StyledHelpText>
);
return (
<React.Fragment>
<StyledTitle>
{capitalize(entityName)}s
{required ? (
<Typography component="span" color="error">
*
</Typography>
) : null}
</StyledTitle>
<ConditionallyRender
condition={description !== undefined}
show={<StyledHelpText>{description}</StyledHelpText>}
/>
<ConditionallyRender
condition={selectAllEnabled}
show={<DefaultHelpText />}
/>
<ConditionallyRender
condition={selectAllEnabled}
show={<SelectAllFormControl />}
/>
<Autocomplete
sx={{ mb: 8 }}
disabled={isWildcardSelected}
multiple
limitTags={2}
options={options}
disableCloseOnSelect
getOptionLabel={({ label }) => label}
fullWidth
groupBy={() => 'Select/Deselect all'}
renderGroup={renderGroup}
PaperComponent={CustomPaper}
renderOption={renderOption}
renderInput={renderInput}
value={
isWildcardSelected
? options
: options.filter(option =>
selectedItems.includes(option.value)
)
}
onChange={(_, input) => {
const state = input.map(({ value }) => value);
onChange(state);
}}
/>
</React.Fragment>
);
};