mirror of https://github.com/portainer/portainer
feat(app): introduce form framework [EE-1946] (#6272)
parent
c5fe994cd2
commit
4f7b432f44
|
@ -6,6 +6,7 @@ import settingsModule from './settings';
|
|||
import featureFlagModule from './feature-flags';
|
||||
import userActivityModule from './user-activity';
|
||||
import servicesModule from './services';
|
||||
import teamsModule from './teams';
|
||||
|
||||
async function initAuthentication(authManager, Authentication, $rootScope, $state) {
|
||||
authManager.checkAuthOnRefresh();
|
||||
|
@ -32,6 +33,7 @@ angular
|
|||
userActivityModule,
|
||||
'portainer.shared.datatable',
|
||||
servicesModule,
|
||||
teamsModule,
|
||||
])
|
||||
.config([
|
||||
'$stateRegistryProvider',
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
type Type = 'submit' | 'reset' | 'button';
|
||||
type Type = 'submit' | 'button' | 'reset';
|
||||
type Color = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'link';
|
||||
type Size = 'xsmall' | 'small' | 'medium' | 'large';
|
||||
|
||||
export interface Props {
|
||||
type?: Type;
|
||||
color?: Color;
|
||||
size?: Size;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
className?: string;
|
||||
onClick: () => void;
|
||||
dataCy?: string;
|
||||
type?: Type;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function Button({
|
||||
|
@ -20,12 +22,14 @@ export function Button({
|
|||
size = 'small',
|
||||
disabled = false,
|
||||
className,
|
||||
dataCy,
|
||||
onClick,
|
||||
title,
|
||||
children,
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<button
|
||||
data-cy={dataCy}
|
||||
/* eslint-disable-next-line react/button-has-type */
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
|
||||
import { LoadingButton } from './LoadingButton';
|
||||
|
||||
export default {
|
||||
component: LoadingButton,
|
||||
title: 'Components/Buttons/LoadingButton',
|
||||
} as Meta;
|
||||
|
||||
interface Args {
|
||||
loadingText: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
function Template({ loadingText, isLoading }: Args) {
|
||||
return (
|
||||
<LoadingButton loadingText={loadingText} isLoading={isLoading}>
|
||||
<i className="fa fa-download" aria-hidden="true" /> Download
|
||||
</LoadingButton>
|
||||
);
|
||||
}
|
||||
|
||||
Template.args = {
|
||||
loadingText: 'loading',
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
export const Example = Template.bind({});
|
||||
|
||||
export function IsLoading() {
|
||||
return (
|
||||
<LoadingButton loadingText="loading" isLoading>
|
||||
<i className="fa fa-download" aria-hidden="true" /> Download
|
||||
</LoadingButton>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import { render } from '@/react-tools/test-utils';
|
||||
|
||||
import { LoadingButton } from './LoadingButton';
|
||||
|
||||
test('when isLoading is true should show spinner and loading text', async () => {
|
||||
const loadingText = 'loading';
|
||||
const children = 'not visible';
|
||||
|
||||
const { findByLabelText, queryByText, findByText } = render(
|
||||
<LoadingButton loadingText={loadingText} isLoading>
|
||||
{children}
|
||||
</LoadingButton>
|
||||
);
|
||||
|
||||
const buttonLabel = queryByText(children);
|
||||
expect(buttonLabel).toBeNull();
|
||||
|
||||
const spinner = await findByLabelText('loading');
|
||||
expect(spinner).toBeVisible();
|
||||
|
||||
const loadingTextElem = await findByText(loadingText);
|
||||
expect(loadingTextElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show children when false', async () => {
|
||||
const loadingText = 'loading';
|
||||
const children = 'visible';
|
||||
|
||||
const { queryByLabelText, queryByText } = render(
|
||||
<LoadingButton loadingText={loadingText} isLoading={false}>
|
||||
{children}
|
||||
</LoadingButton>
|
||||
);
|
||||
|
||||
const buttonLabel = queryByText(children);
|
||||
expect(buttonLabel).toBeVisible();
|
||||
|
||||
const spinner = queryByLabelText('loading');
|
||||
expect(spinner).toBeNull();
|
||||
|
||||
const loadingTextElem = queryByText(loadingText);
|
||||
expect(loadingTextElem).toBeNull();
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { type Props as ButtonProps, Button } from './Button';
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
loadingText: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export function LoadingButton({
|
||||
loadingText,
|
||||
isLoading,
|
||||
disabled,
|
||||
type = 'submit',
|
||||
children,
|
||||
...buttonProps
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<Button
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...buttonProps}
|
||||
type={type}
|
||||
disabled={disabled || isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<i
|
||||
className="fa fa-circle-notch fa-spin space-right"
|
||||
aria-label="loading"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{loadingText}
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { UserViewModel } from '@/portainer/models/user';
|
||||
|
||||
export function createMockUser(id: number, username: string): UserViewModel {
|
||||
return {
|
||||
Id: id,
|
||||
Username: username,
|
||||
Role: 2,
|
||||
UserTheme: '',
|
||||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
PortainerEndpointList: true,
|
||||
PortainerMOTD: true,
|
||||
PortainerRoleList: true,
|
||||
PortainerTeamList: true,
|
||||
PortainerTemplateInspect: true,
|
||||
PortainerTemplateList: true,
|
||||
PortainerUserInspect: true,
|
||||
PortainerUserList: true,
|
||||
PortainerUserMemberships: true,
|
||||
},
|
||||
RoleName: 'user',
|
||||
Checked: false,
|
||||
AuthenticationMethod: '',
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { UsersSelector } from './UsersSelector';
|
||||
import { createMockUser } from './UsersSelector.mocks';
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Components/UsersSelector',
|
||||
component: UsersSelector,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export function Example() {
|
||||
const [selectedUsers, setSelectedUsers] = useState([10]);
|
||||
|
||||
const users = [createMockUser(1, 'user1'), createMockUser(2, 'user2')];
|
||||
|
||||
return (
|
||||
<UsersSelector
|
||||
value={selectedUsers}
|
||||
onChange={setSelectedUsers}
|
||||
users={users}
|
||||
placeholder="Select one or more users"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import Select from 'react-select';
|
||||
|
||||
import { UserViewModel } from '@/portainer/models/user';
|
||||
|
||||
type UserId = number;
|
||||
|
||||
interface Props {
|
||||
value: UserId[];
|
||||
onChange(value: UserId[]): void;
|
||||
users: UserViewModel[];
|
||||
dataCy?: string;
|
||||
inputId?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export function UsersSelector({
|
||||
value,
|
||||
onChange,
|
||||
users,
|
||||
dataCy,
|
||||
inputId,
|
||||
placeholder,
|
||||
}: Props) {
|
||||
return (
|
||||
<Select
|
||||
isMulti
|
||||
getOptionLabel={(user) => user.Username}
|
||||
getOptionValue={(user) => user.Id}
|
||||
options={users}
|
||||
value={users.filter((user) => value.includes(user.Id))}
|
||||
closeMenuOnSelect={false}
|
||||
onChange={(selectedUsers) =>
|
||||
onChange(selectedUsers.map((user) => user.Id))
|
||||
}
|
||||
data-cy={dataCy}
|
||||
inputId={inputId}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { UsersSelector } from './UsersSelector';
|
|
@ -1,8 +1,5 @@
|
|||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.space-right {
|
||||
margin-right: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { TextInput, Select } from '../Input';
|
||||
import { Input, Select } from '../Input';
|
||||
|
||||
import { FormControl } from './FormControl';
|
||||
|
||||
|
@ -21,7 +21,12 @@ function TextField({ label, tooltip = '' }: TextFieldProps) {
|
|||
const inputId = 'input';
|
||||
return (
|
||||
<FormControl inputId={inputId} label={label} tooltip={tooltip}>
|
||||
<TextInput id={inputId} type="text" value={value} onChange={setValue} />
|
||||
<Input
|
||||
id={inputId}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
@ -43,7 +48,7 @@ function SelectField({ label, tooltip = '' }: TextFieldProps) {
|
|||
<Select
|
||||
className="form-control"
|
||||
value={value}
|
||||
onChange={(value) => setValue(value)}
|
||||
onChange={(e) => setValue(parseInt(e.target.value, 10))}
|
||||
options={options}
|
||||
/>
|
||||
</FormControl>
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import clsx from 'clsx';
|
||||
import { HTMLInputTypeAttribute } from 'react';
|
||||
|
||||
import { InputProps } from './types';
|
||||
|
||||
interface Props extends InputProps {
|
||||
type?: HTMLInputTypeAttribute;
|
||||
onChange(value: string): void;
|
||||
value: number | string;
|
||||
component?: 'input' | 'textarea';
|
||||
rows?: number;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
export function BaseInput({
|
||||
component = 'input',
|
||||
value,
|
||||
disabled,
|
||||
id,
|
||||
readonly,
|
||||
required,
|
||||
type,
|
||||
className,
|
||||
rows,
|
||||
onChange,
|
||||
}: Props) {
|
||||
const Component = component;
|
||||
return (
|
||||
<Component
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
readOnly={readonly}
|
||||
required={required}
|
||||
type={type}
|
||||
className={clsx('form-control', className)}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
rows={rows}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { TextInput } from './TextInput';
|
||||
import { Input } from './Input';
|
||||
|
||||
export default {
|
||||
title: 'Components/Form/TextInput',
|
||||
title: 'Components/Form/Input',
|
||||
args: {
|
||||
disabled: false,
|
||||
},
|
||||
|
@ -16,7 +16,14 @@ interface Args {
|
|||
|
||||
export function TextField({ disabled }: Args) {
|
||||
const [value, setValue] = useState('');
|
||||
return <TextInput value={value} onChange={setValue} disabled={disabled} />;
|
||||
return (
|
||||
<Input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const DisabledTextField: Story<Args> = TextField.bind({});
|
|
@ -0,0 +1,15 @@
|
|||
import clsx from 'clsx';
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
|
||||
export function Input({
|
||||
className,
|
||||
...props
|
||||
}: InputHTMLAttributes<HTMLInputElement>) {
|
||||
return (
|
||||
<input
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
className={clsx('form-control', className)}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { NumberInput } from './NumberInput';
|
||||
|
||||
export default {
|
||||
title: 'Components/Form/NumberInput',
|
||||
args: {
|
||||
disabled: false,
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
interface Args {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function Example({ disabled }: Args) {
|
||||
const [value, setValue] = useState(0);
|
||||
return <NumberInput value={value} onChange={setValue} disabled={disabled} />;
|
||||
}
|
||||
|
||||
export const DisabledNumberInput: Story<Args> = Example.bind({});
|
||||
DisabledNumberInput.args = {
|
||||
disabled: true,
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { BaseInput } from './BaseInput';
|
||||
import { InputProps } from './types';
|
||||
|
||||
interface Props extends InputProps {
|
||||
value: number;
|
||||
readonly?: boolean;
|
||||
onChange(value: number): void;
|
||||
}
|
||||
|
||||
export function NumberInput({
|
||||
disabled,
|
||||
required,
|
||||
id,
|
||||
value,
|
||||
className,
|
||||
readonly,
|
||||
placeholder,
|
||||
onChange,
|
||||
}: Props) {
|
||||
return (
|
||||
<BaseInput
|
||||
id={id}
|
||||
type="number"
|
||||
className={clsx(className, 'form-control')}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
readonly={readonly}
|
||||
required={required}
|
||||
placeholder={placeholder}
|
||||
onChange={(value) => onChange(parseFloat(value))}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -21,9 +21,9 @@ export function Example({ disabled }: Args) {
|
|||
{ value: 2, label: 'two' },
|
||||
];
|
||||
return (
|
||||
<Select<number>
|
||||
<Select
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
onChange={(e) => setValue(parseInt(e.target.value, 10))}
|
||||
disabled={disabled}
|
||||
options={options}
|
||||
/>
|
||||
|
|
|
@ -1,36 +1,25 @@
|
|||
import clsx from 'clsx';
|
||||
import { FormEvent } from 'react';
|
||||
|
||||
import { ChangeProps, InputProps } from './types';
|
||||
import { SelectHTMLAttributes } from 'react';
|
||||
|
||||
interface Option<T extends string | number> {
|
||||
value: T;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Props<T extends string | number> extends InputProps, ChangeProps<T> {
|
||||
interface Props<T extends string | number> {
|
||||
options: Option<T>[];
|
||||
}
|
||||
|
||||
export function Select<T extends number | string>({
|
||||
options,
|
||||
onChange,
|
||||
value,
|
||||
className,
|
||||
disabled,
|
||||
id,
|
||||
required,
|
||||
placeholder,
|
||||
}: Props<T>) {
|
||||
...props
|
||||
}: Props<T> & SelectHTMLAttributes<HTMLSelectElement>) {
|
||||
return (
|
||||
<select
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
required={required}
|
||||
className={clsx(className, 'form-control')}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
className={clsx('form-control', className)}
|
||||
>
|
||||
{options.map((item) => (
|
||||
<option value={item.value} key={item.value}>
|
||||
|
@ -39,10 +28,4 @@ export function Select<T extends number | string>({
|
|||
))}
|
||||
</select>
|
||||
);
|
||||
|
||||
function handleChange(e: FormEvent<HTMLSelectElement>) {
|
||||
const { selectedIndex } = e.currentTarget;
|
||||
const option = options[selectedIndex];
|
||||
onChange(option.value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import { HTMLInputTypeAttribute } from 'react';
|
||||
|
||||
import { BaseInput } from './BaseInput';
|
||||
import { ChangeProps, InputProps } from './types';
|
||||
|
||||
interface TextInputProps extends InputProps, ChangeProps<string> {
|
||||
type?: HTMLInputTypeAttribute;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
export function TextInput({
|
||||
id,
|
||||
type = 'text',
|
||||
value,
|
||||
className,
|
||||
onChange,
|
||||
disabled,
|
||||
readonly,
|
||||
required,
|
||||
placeholder,
|
||||
}: TextInputProps) {
|
||||
return (
|
||||
<BaseInput
|
||||
id={id}
|
||||
type={type}
|
||||
className={className}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
readonly={readonly}
|
||||
required={required}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,31 +1,15 @@
|
|||
import { BaseInput } from './BaseInput';
|
||||
import { ChangeProps, InputProps } from './types';
|
||||
import clsx from 'clsx';
|
||||
import { TextareaHTMLAttributes } from 'react';
|
||||
|
||||
interface Props extends InputProps, ChangeProps<string> {
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
export function Textarea({
|
||||
rows,
|
||||
export function TextArea({
|
||||
className,
|
||||
onChange,
|
||||
value,
|
||||
id,
|
||||
placeholder,
|
||||
disabled,
|
||||
required,
|
||||
}: Props & InputProps) {
|
||||
...props
|
||||
}: TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
||||
return (
|
||||
<BaseInput
|
||||
component="textarea"
|
||||
id={id}
|
||||
rows={rows}
|
||||
className={className}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
<textarea
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
className={clsx('form-control', className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
export { NumberInput } from './NumberInput';
|
||||
export { TextInput } from './TextInput';
|
||||
export { Input } from './Input';
|
||||
export { Select } from './Select';
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
export interface InputProps {
|
||||
id?: string;
|
||||
className?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface ChangeProps<T> {
|
||||
value: T;
|
||||
onChange(value: T): void;
|
||||
}
|
|
@ -20,7 +20,7 @@ function BasicExample() {
|
|||
<InputGroup.Addon>@</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
onChange={setValue1}
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
placeholder="Username"
|
||||
aria-describedby="basic-addon1"
|
||||
/>
|
||||
|
@ -29,7 +29,7 @@ function BasicExample() {
|
|||
<InputGroup>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
onChange={setValue1}
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
placeholder="Recipient's username"
|
||||
aria-describedby="basic-addon2"
|
||||
/>
|
||||
|
@ -38,9 +38,10 @@ function BasicExample() {
|
|||
|
||||
<InputGroup>
|
||||
<InputGroup.Addon>$</InputGroup.Addon>
|
||||
<InputGroup.NumberInput
|
||||
<InputGroup.Input
|
||||
type="number"
|
||||
value={valueNumber}
|
||||
onChange={setValueNumber}
|
||||
onChange={(e) => setValueNumber(parseInt(e.target.value, 10))}
|
||||
aria-label="Amount (to the nearest dollar)"
|
||||
/>
|
||||
<InputGroup.Addon>.00</InputGroup.Addon>
|
||||
|
@ -51,7 +52,7 @@ function BasicExample() {
|
|||
<InputGroup.Addon>https://example.com/users/</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
onChange={setValue1}
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
id="basic-url"
|
||||
aria-describedby="basic-addon3"
|
||||
/>
|
||||
|
@ -72,12 +73,18 @@ function Addons() {
|
|||
Go!
|
||||
</button>
|
||||
</InputGroup.ButtonWrapper>
|
||||
<InputGroup.Input value={value1} onChange={setValue1} />
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<InputGroup>
|
||||
<InputGroup.Input value={value2} onChange={setValue2} />
|
||||
<InputGroup.Input
|
||||
value={value2}
|
||||
onChange={(e) => setValue2(e.target.value)}
|
||||
/>
|
||||
<InputGroup.Addon>
|
||||
<input type="checkbox" />
|
||||
</InputGroup.Addon>
|
||||
|
@ -93,17 +100,26 @@ function Sizing() {
|
|||
<div className="space-y-8">
|
||||
<InputGroup size="small">
|
||||
<InputGroup.Addon>Small</InputGroup.Addon>
|
||||
<InputGroup.Input value={value} onChange={setValue} />
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
<InputGroup>
|
||||
<InputGroup.Addon>Default</InputGroup.Addon>
|
||||
<InputGroup.Input value={value} onChange={setValue} />
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
<InputGroup size="large">
|
||||
<InputGroup.Addon>Large</InputGroup.Addon>
|
||||
<InputGroup.Input value={value} onChange={setValue} />
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { NumberInput, TextInput } from '../Input';
|
||||
import { Input } from '../Input';
|
||||
|
||||
import { InputGroup as MainComponent } from './InputGroup';
|
||||
import { InputGroupAddon } from './InputGroupAddon';
|
||||
|
@ -7,15 +7,13 @@ import { InputGroupButtonWrapper } from './InputGroupButtonWrapper';
|
|||
interface InputGroupSubComponents {
|
||||
Addon: typeof InputGroupAddon;
|
||||
ButtonWrapper: typeof InputGroupButtonWrapper;
|
||||
Input: typeof TextInput;
|
||||
NumberInput: typeof NumberInput;
|
||||
Input: typeof Input;
|
||||
}
|
||||
|
||||
const InputGroup: typeof MainComponent & InputGroupSubComponents = MainComponent as typeof MainComponent & InputGroupSubComponents;
|
||||
|
||||
InputGroup.Addon = InputGroupAddon;
|
||||
InputGroup.ButtonWrapper = InputGroupButtonWrapper;
|
||||
InputGroup.Input = TextInput;
|
||||
InputGroup.NumberInput = NumberInput;
|
||||
InputGroup.Input = Input;
|
||||
|
||||
export { InputGroup };
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { NumberInput, Select } from '../Input';
|
||||
import { Input, Select } from '../Input';
|
||||
|
||||
import { DefaultType, InputList } from './InputList';
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'InputList',
|
||||
title: 'Components/Form/InputList',
|
||||
component: InputList,
|
||||
};
|
||||
|
||||
|
@ -75,12 +75,15 @@ function SelectAndInputItem({
|
|||
}) {
|
||||
return (
|
||||
<div>
|
||||
<NumberInput
|
||||
<Input
|
||||
type="number"
|
||||
value={item.value}
|
||||
onChange={(value: number) => onChange({ ...item, value })}
|
||||
onChange={(e) =>
|
||||
onChange({ ...item, value: parseInt(e.target.value, 10) })
|
||||
}
|
||||
/>
|
||||
<Select
|
||||
onChange={(select: string) => onChange({ ...item, select })}
|
||||
onChange={(e) => onChange({ ...item, select: e.target.value })}
|
||||
options={[
|
||||
{ label: 'option1', value: 'option1' },
|
||||
{ label: 'option2', value: 'option2' },
|
||||
|
|
|
@ -4,7 +4,7 @@ import clsx from 'clsx';
|
|||
import { AddButton, Button } from '@/portainer/components/Button';
|
||||
import { Tooltip } from '@/portainer/components/Tip/Tooltip';
|
||||
|
||||
import { TextInput } from '../Input';
|
||||
import { Input } from '../Input';
|
||||
|
||||
import styles from './InputList.module.css';
|
||||
import { arrayMove } from './utils';
|
||||
|
@ -174,9 +174,9 @@ function defaultItemBuilder(): DefaultType {
|
|||
|
||||
function DefaultItem({ item, onChange }: ItemProps<DefaultType>) {
|
||||
return (
|
||||
<TextInput
|
||||
<Input
|
||||
value={item.value}
|
||||
onChange={(value: string) => onChange({ value })}
|
||||
onChange={(e) => onChange({ value: e.target.value })}
|
||||
className={styles.defaultItem}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -11,6 +11,7 @@ export function UserViewModel(data) {
|
|||
this.AuthenticationMethod = data.AuthenticationMethod;
|
||||
this.Checked = false;
|
||||
this.EndpointAuthorizations = null;
|
||||
this.PortainerAuthorizations = null;
|
||||
}
|
||||
|
||||
export function UserTokenModel(data) {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import { TeamViewModel } from '@/portainer/models/team';
|
||||
import { UserViewModel } from '@/portainer/models/user';
|
||||
|
||||
export function mockExampleData() {
|
||||
const teams: TeamViewModel[] = [
|
||||
{
|
||||
Id: 3,
|
||||
Name: 'Team 1',
|
||||
Checked: false,
|
||||
},
|
||||
{
|
||||
Id: 4,
|
||||
Name: 'Team 2',
|
||||
Checked: false,
|
||||
},
|
||||
];
|
||||
|
||||
const users: UserViewModel[] = [
|
||||
{
|
||||
Id: 10,
|
||||
Username: 'user1',
|
||||
Role: 2,
|
||||
UserTheme: '',
|
||||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
PortainerEndpointList: true,
|
||||
PortainerMOTD: true,
|
||||
PortainerRoleList: true,
|
||||
PortainerTeamList: true,
|
||||
PortainerTemplateInspect: true,
|
||||
PortainerTemplateList: true,
|
||||
PortainerUserInspect: true,
|
||||
PortainerUserList: true,
|
||||
PortainerUserMemberships: true,
|
||||
},
|
||||
RoleName: 'user',
|
||||
Checked: false,
|
||||
AuthenticationMethod: '',
|
||||
},
|
||||
{
|
||||
Id: 13,
|
||||
Username: 'user2',
|
||||
Role: 2,
|
||||
UserTheme: '',
|
||||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
PortainerEndpointList: true,
|
||||
PortainerMOTD: true,
|
||||
PortainerRoleList: true,
|
||||
PortainerTeamList: true,
|
||||
PortainerTemplateInspect: true,
|
||||
PortainerTemplateList: true,
|
||||
PortainerUserInspect: true,
|
||||
PortainerUserList: true,
|
||||
PortainerUserMemberships: true,
|
||||
},
|
||||
RoleName: 'user',
|
||||
Checked: false,
|
||||
AuthenticationMethod: '',
|
||||
},
|
||||
];
|
||||
|
||||
return { users, teams };
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { CreateTeamForm, FormValues } from './CreateTeamForm';
|
||||
import { mockExampleData } from './CreateTeamForm.mocks';
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'teams/CreateTeamForm',
|
||||
component: CreateTeamForm,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export function Example() {
|
||||
const [message, setMessage] = useState('');
|
||||
const { teams, users } = mockExampleData();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CreateTeamForm users={users} teams={teams} onSubmit={handleSubmit} />
|
||||
<div>{message}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function handleSubmit(values: FormValues) {
|
||||
return new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
setMessage(
|
||||
`created team ${values.name} with ${values.leaders.length} leaders`
|
||||
);
|
||||
resolve();
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { render, waitFor } from '@/react-tools/test-utils';
|
||||
|
||||
import { CreateTeamForm } from './CreateTeamForm';
|
||||
|
||||
test('filling the name should make the submit button clickable and emptying it should make it disabled', async () => {
|
||||
const { findByLabelText, findByText } = render(
|
||||
<CreateTeamForm users={[]} teams={[]} onSubmit={() => {}} />
|
||||
);
|
||||
|
||||
const button = await findByText('Create team');
|
||||
expect(button).toBeVisible();
|
||||
|
||||
const nameField = await findByLabelText('Name');
|
||||
expect(nameField).toBeVisible();
|
||||
expect(nameField).toHaveDisplayValue('');
|
||||
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
const newValue = 'name';
|
||||
userEvent.type(nameField, newValue);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(nameField).toHaveDisplayValue(newValue);
|
||||
expect(button).toBeEnabled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
import { Formik, Field, Form } from 'formik';
|
||||
|
||||
import { FormControl } from '@/portainer/components/form-components/FormControl';
|
||||
import { Widget, WidgetBody, WidgetTitle } from '@/portainer/components/widget';
|
||||
import { UserViewModel } from '@/portainer/models/user';
|
||||
import { TeamViewModel } from '@/portainer/models/team';
|
||||
import { Input } from '@/portainer/components/form-components/Input';
|
||||
import { UsersSelector } from '@/portainer/components/UsersSelector';
|
||||
import { LoadingButton } from '@/portainer/components/Button/LoadingButton';
|
||||
|
||||
import { validationSchema } from './CreateTeamForm.validation';
|
||||
|
||||
export interface FormValues {
|
||||
name: string;
|
||||
leaders: number[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
users: UserViewModel[];
|
||||
teams: TeamViewModel[];
|
||||
onSubmit(values: FormValues): void;
|
||||
}
|
||||
|
||||
export function CreateTeamForm({ users, teams, onSubmit }: Props) {
|
||||
const initialValues = {
|
||||
name: '',
|
||||
leaders: [],
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-md-12 col-xs-12">
|
||||
<Widget>
|
||||
<WidgetTitle icon="fa-plus" title="Add a new team" />
|
||||
<WidgetBody>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={() => validationSchema(teams)}
|
||||
onSubmit={onSubmit}
|
||||
validateOnMount
|
||||
>
|
||||
{({
|
||||
values,
|
||||
errors,
|
||||
handleSubmit,
|
||||
setFieldValue,
|
||||
isSubmitting,
|
||||
isValid,
|
||||
}) => (
|
||||
<Form
|
||||
className="form-horizontal"
|
||||
onSubmit={handleSubmit}
|
||||
noValidate
|
||||
>
|
||||
<FormControl
|
||||
inputId="team_name"
|
||||
label="Name"
|
||||
errors={errors.name}
|
||||
>
|
||||
<Field
|
||||
as={Input}
|
||||
name="name"
|
||||
id="team_name"
|
||||
required
|
||||
placeholder="e.g. development"
|
||||
data-cy="team-teamNameInput"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
{users.length > 0 && (
|
||||
<FormControl
|
||||
inputId="users-input"
|
||||
label="Select team leader(s)"
|
||||
tooltip="You can assign one or more leaders to this team. Team leaders can manage their teams users and resources."
|
||||
errors={errors.leaders}
|
||||
>
|
||||
<UsersSelector
|
||||
value={values.leaders}
|
||||
onChange={(leaders) =>
|
||||
setFieldValue('leaders', leaders)
|
||||
}
|
||||
users={users}
|
||||
dataCy="team-teamLeaderSelect"
|
||||
inputId="users-input"
|
||||
placeholder="Select one or more team leaders"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
<LoadingButton
|
||||
disabled={!isValid}
|
||||
dataCy="team-createTeamButton"
|
||||
isLoading={isSubmitting}
|
||||
loadingText="Creating team..."
|
||||
>
|
||||
<i
|
||||
className="fa fa-plus space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create team
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { object, string, array, number } from 'yup';
|
||||
|
||||
import { TeamViewModel } from '@/portainer/models/team';
|
||||
|
||||
export function validationSchema(teams: TeamViewModel[]) {
|
||||
return object().shape({
|
||||
name: string()
|
||||
.required('This field is required.')
|
||||
.test('is-unique', 'This team already exists.', (name) => !!name && teams.every((team) => team.Name !== name)),
|
||||
leaders: array().of(number()),
|
||||
});
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { r2a } from '@/react-tools/react2angular';
|
||||
|
||||
import { CreateTeamForm } from './CreateTeamForm';
|
||||
|
||||
export { CreateTeamForm };
|
||||
|
||||
export const CreateTeamFormAngular = r2a(CreateTeamForm, ['users', 'actionInProgress', 'onSubmit', 'teams']);
|
|
@ -0,0 +1,8 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { CreateTeamFormAngular } from './CreateTeamForm';
|
||||
|
||||
export default angular
|
||||
.module('portainer.app.teams', [])
|
||||
|
||||
.component('createTeamForm', CreateTeamFormAngular).name;
|
|
@ -3,12 +3,12 @@ import { useRouter } from '@uirouter/react';
|
|||
|
||||
import { Widget, WidgetBody } from '@/portainer/components/widget';
|
||||
import { FormControl } from '@/portainer/components/form-components/FormControl';
|
||||
import { TextInput } from '@/portainer/components/form-components/Input';
|
||||
import { Button } from '@/portainer/components/Button';
|
||||
import { FormSectionTitle } from '@/portainer/components/form-components/FormSectionTitle';
|
||||
import { TextTip } from '@/portainer/components/Tip/TextTip';
|
||||
import { Code } from '@/portainer/components/Code';
|
||||
import { CopyButton } from '@/portainer/components/Button/CopyButton';
|
||||
import { Input } from '@/portainer/components/form-components/Input';
|
||||
|
||||
import styles from './CreateAccessToken.module.css';
|
||||
|
||||
|
@ -64,16 +64,15 @@ export function CreateAccessToken({
|
|||
<WidgetBody>
|
||||
<div>
|
||||
<FormControl inputId="input" label="Description" errors={errorText}>
|
||||
<TextInput
|
||||
<Input
|
||||
id="input"
|
||||
onChange={(value) => setDescription(value)}
|
||||
type="text"
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
value={description}
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
disabled={!!errorText || !!accessToken}
|
||||
onClick={generateAccessToken}
|
||||
onClick={() => generateAccessToken()}
|
||||
className={styles.addButton}
|
||||
>
|
||||
Add access token
|
||||
|
|
|
@ -7,87 +7,7 @@
|
|||
<rd-header-content>Teams management</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="isAdmin">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plus" title-text="Add a new team"> </rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="teamCreationForm" ng-submit="addTeam()">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="team_name" class="col-sm-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="team_name"
|
||||
name="team_name"
|
||||
ng-model="formValues.Name"
|
||||
ng-change="checkNameValidity(teamCreationForm)"
|
||||
placeholder="e.g. development"
|
||||
auto-focus
|
||||
required
|
||||
data-cy="team-teamNameInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="teamCreationForm.team_name.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="teamCreationForm.team_name.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
<p ng-message="validName"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This team already exists.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- team-leaders -->
|
||||
<div class="form-group" ng-if="users.length > 0">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label text-left">
|
||||
Select team leader(s)
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="You can assign one or more leaders to this team. Team leaders can manage their teams users and resources."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<span
|
||||
isteven-multi-select
|
||||
ng-if="users.length > 0"
|
||||
input-model="users"
|
||||
output-model="formValues.Leaders"
|
||||
button-label="Username"
|
||||
item-label="Username"
|
||||
tick-property="ticked"
|
||||
helper-elements="filter"
|
||||
search-property="Username"
|
||||
translation="{nothingSelected: 'Select one or more team leaders', search: 'Search...'}"
|
||||
style="margin-left: 20px;"
|
||||
data-cy="team-teamLeaderSelect"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !team-leaders -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !teamCreationForm.$valid"
|
||||
ng-click="addTeam()"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="team-createTeamButton"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Create team</span>
|
||||
<span ng-show="state.actionInProgress">Creating team...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<create-team-form ng-if="isAdmin && users" users="users" action-in-progress="state.actionInProgress" teams="teams" on-submit="(addTeam)"></create-team-form>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -30,15 +30,11 @@ angular.module('portainer.app').controller('TeamsController', [
|
|||
form.team_name.$setValidity('validName', valid);
|
||||
};
|
||||
|
||||
$scope.addTeam = function () {
|
||||
var teamName = $scope.formValues.Name;
|
||||
var leaderIds = [];
|
||||
angular.forEach($scope.formValues.Leaders, function (user) {
|
||||
leaderIds.push(user.Id);
|
||||
});
|
||||
$scope.addTeam = function (formValues) {
|
||||
const teamName = formValues.name;
|
||||
|
||||
$scope.state.actionInProgress = true;
|
||||
TeamService.createTeam(teamName, leaderIds)
|
||||
TeamService.createTeam(teamName, formValues.leaders)
|
||||
.then(function success() {
|
||||
Notifications.success('Team successfully created', teamName);
|
||||
$state.reload();
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
"fast-json-patch": "^3.1.0",
|
||||
"filesize": "~3.3.0",
|
||||
"filesize-parser": "^1.5.0",
|
||||
"formik": "^2.2.9",
|
||||
"jquery": "^3.6.0",
|
||||
"js-base64": "^3.7.2",
|
||||
"js-yaml": "^3.14.0",
|
||||
|
@ -111,6 +112,7 @@
|
|||
"rc-slider": "^9.7.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-select": "^5.2.1",
|
||||
"react-tooltip": "^4.2.21",
|
||||
"sanitize-html": "^2.5.3",
|
||||
"spinkit": "^2.0.1",
|
||||
|
@ -121,7 +123,8 @@
|
|||
"uuid": "^3.3.2",
|
||||
"x256": "^0.0.2",
|
||||
"xterm": "^3.8.0",
|
||||
"yaml": "^1.10.2"
|
||||
"yaml": "^1.10.2",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-cli": "^4.0.4",
|
||||
|
@ -139,6 +142,7 @@
|
|||
"@storybook/react": "^6.4.9",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/angular": "^1.8.3",
|
||||
"@types/bootbox": "^5.2.2",
|
||||
"@types/jest": "^27.0.3",
|
||||
|
@ -232,4 +236,4 @@
|
|||
"pre-commit": "lint-staged"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
208
yarn.lock
208
yarn.lock
|
@ -1148,7 +1148,35 @@
|
|||
core-js-pure "^3.16.0"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6":
|
||||
version "7.15.4"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz"
|
||||
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1":
|
||||
version "7.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.0.tgz#e27b977f2e2088ba24748bf99b5e1dece64e4f0b"
|
||||
integrity sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.9.2":
|
||||
version "7.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
|
||||
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.7":
|
||||
version "7.16.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
|
||||
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.16.3", "@babel/runtime@^7.6.2", "@babel/runtime@^7.8.4":
|
||||
version "7.16.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a"
|
||||
integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==
|
||||
|
@ -1258,6 +1286,17 @@
|
|||
"@emotion/utils" "0.11.3"
|
||||
"@emotion/weak-memoize" "0.2.5"
|
||||
|
||||
"@emotion/cache@^11.4.0", "@emotion/cache@^11.6.0":
|
||||
version "11.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.6.0.tgz#65fbdbbe4382f1991d8b20853c38e63ecccec9a1"
|
||||
integrity sha512-ElbsWY1KMwEowkv42vGo0UPuLgtPYfIs9BxxVrmvsaJVvktknsHYYlx5NQ5g6zLDcOTyamlDc7FkRg2TAcQDKQ==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/sheet" "^1.1.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
stylis "^4.0.10"
|
||||
|
||||
"@emotion/core@^10.1.1":
|
||||
version "10.1.1"
|
||||
resolved "https://registry.npmjs.org/@emotion/core/-/core-10.1.1.tgz"
|
||||
|
@ -1279,7 +1318,7 @@
|
|||
"@emotion/utils" "0.11.3"
|
||||
babel-plugin-emotion "^10.0.27"
|
||||
|
||||
"@emotion/hash@0.8.0":
|
||||
"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz"
|
||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||
|
@ -1296,6 +1335,24 @@
|
|||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
|
||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||
|
||||
"@emotion/memoize@^0.7.4":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||
|
||||
"@emotion/react@^11.1.1":
|
||||
version "11.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.6.0.tgz#61fcb95c1e01255734c2c721cb9beabcf521eb0f"
|
||||
integrity sha512-23MnRZFBN9+D1lHXC5pD6z4X9yhPxxtHr6f+iTGz6Fv6Rda0GdefPrsHL7otsEf+//7uqCdT5QtHeRxHCERzuw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@emotion/cache" "^11.6.0"
|
||||
"@emotion/serialize" "^1.0.2"
|
||||
"@emotion/sheet" "^1.1.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
hoist-non-react-statics "^3.3.1"
|
||||
|
||||
"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16":
|
||||
version "0.11.16"
|
||||
resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz"
|
||||
|
@ -1307,11 +1364,27 @@
|
|||
"@emotion/utils" "0.11.3"
|
||||
csstype "^2.5.7"
|
||||
|
||||
"@emotion/serialize@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
|
||||
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/unitless" "^0.7.5"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@emotion/sheet@0.9.4":
|
||||
version "0.9.4"
|
||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz"
|
||||
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
|
||||
|
||||
"@emotion/sheet@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
|
||||
integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
|
||||
|
||||
"@emotion/styled-base@^10.0.27":
|
||||
version "10.0.31"
|
||||
resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.31.tgz"
|
||||
|
@ -1335,7 +1408,7 @@
|
|||
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
|
||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||
|
||||
"@emotion/unitless@0.7.5":
|
||||
"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.5":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||
|
@ -1345,7 +1418,12 @@
|
|||
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz"
|
||||
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
|
||||
|
||||
"@emotion/weak-memoize@0.2.5":
|
||||
"@emotion/utils@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
|
||||
integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
|
||||
|
||||
"@emotion/weak-memoize@0.2.5", "@emotion/weak-memoize@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
@ -2769,6 +2847,13 @@
|
|||
"@babel/runtime" "^7.12.5"
|
||||
"@testing-library/dom" "^8.0.0"
|
||||
|
||||
"@testing-library/user-event@^13.5.0":
|
||||
version "13.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295"
|
||||
integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz"
|
||||
|
@ -3017,6 +3102,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0"
|
||||
integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==
|
||||
|
||||
"@types/lodash@^4.14.175":
|
||||
version "4.14.177"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
||||
integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==
|
||||
|
||||
"@types/mdast@^3.0.0":
|
||||
version "3.0.10"
|
||||
resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz"
|
||||
|
@ -3116,6 +3206,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.0":
|
||||
version "4.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
|
||||
integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "17.0.27"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.27.tgz"
|
||||
|
@ -6619,6 +6716,11 @@ deep-object-diff@^1.1.0:
|
|||
resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.0.tgz"
|
||||
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
|
||||
|
||||
deepmerge@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
||||
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz"
|
||||
|
@ -6873,6 +6975,14 @@ dom-converter@^0.2.0:
|
|||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz"
|
||||
|
@ -8469,6 +8579,19 @@ format@^0.2.0:
|
|||
resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz"
|
||||
integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
|
||||
|
||||
formik@^2.2.9:
|
||||
version "2.2.9"
|
||||
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
|
||||
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==
|
||||
dependencies:
|
||||
deepmerge "^2.1.1"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
react-fast-compare "^2.0.1"
|
||||
tiny-warning "^1.0.2"
|
||||
tslib "^1.10.0"
|
||||
|
||||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz"
|
||||
|
@ -9499,7 +9622,7 @@ hmac-drbg@^1.0.0:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.3.0:
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
|
@ -11948,6 +12071,11 @@ memfs@^3.2.2:
|
|||
dependencies:
|
||||
fs-monkey "1.0.3"
|
||||
|
||||
memoize-one@^5.0.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||
|
||||
memoizerific@^1.11.3:
|
||||
version "1.11.3"
|
||||
resolved "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz"
|
||||
|
@ -12318,6 +12446,11 @@ nano-time@1.0.0:
|
|||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanoclone@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
||||
integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
|
||||
|
||||
nanocolors@^0.1.0, nanocolors@^0.1.5:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz"
|
||||
|
@ -14040,7 +14173,7 @@ prompts@^2.4.0:
|
|||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.7.2:
|
||||
prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -14049,6 +14182,11 @@ prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.7.2:
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
property-expr@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
|
||||
integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==
|
||||
|
||||
property-information@^5.0.0, property-information@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz"
|
||||
|
@ -14372,6 +14510,11 @@ react-error-overlay@^6.0.9:
|
|||
resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz"
|
||||
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
|
||||
|
||||
react-fast-compare@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||
|
||||
react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz"
|
||||
|
@ -14453,6 +14596,19 @@ react-router@6.1.1, react-router@^6.0.0:
|
|||
dependencies:
|
||||
history "^5.1.0"
|
||||
|
||||
react-select@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.2.1.tgz#416c25c6b79b94687702374e019c4f2ed9d159d6"
|
||||
integrity sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
"@emotion/cache" "^11.4.0"
|
||||
"@emotion/react" "^11.1.1"
|
||||
"@types/react-transition-group" "^4.4.0"
|
||||
memoize-one "^5.0.0"
|
||||
prop-types "^15.6.0"
|
||||
react-transition-group "^4.3.0"
|
||||
|
||||
react-shallow-renderer@^16.13.1:
|
||||
version "16.14.1"
|
||||
resolved "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz"
|
||||
|
@ -14509,6 +14665,16 @@ react-tooltip@^4.2.21:
|
|||
prop-types "^15.7.2"
|
||||
uuid "^7.0.3"
|
||||
|
||||
react-transition-group@^4.3.0:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
|
||||
integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
||||
|
@ -16146,6 +16312,11 @@ stylehacks@^4.0.0:
|
|||
postcss "^7.0.0"
|
||||
postcss-selector-parser "^3.0.0"
|
||||
|
||||
stylis@^4.0.10:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
|
||||
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
|
||||
|
@ -16454,6 +16625,11 @@ timsort@^0.3.0:
|
|||
resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-warning@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
title-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz"
|
||||
|
@ -16545,6 +16721,11 @@ toidentifier@1.0.0:
|
|||
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz"
|
||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||
|
||||
toposort@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
|
||||
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz"
|
||||
|
@ -16628,7 +16809,7 @@ tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0:
|
|||
minimist "^1.2.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^1.11.1, tslib@^1.8.1:
|
||||
tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
@ -17828,6 +18009,19 @@ yocto-queue@^0.1.0:
|
|||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
yup@^0.32.11:
|
||||
version "0.32.11"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
|
||||
integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
"@types/lodash" "^4.14.175"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
nanoclone "^0.2.1"
|
||||
property-expr "^2.0.4"
|
||||
toposort "^2.0.2"
|
||||
|
||||
z-schema@^5.0.1:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.2.tgz#f410394b2c9fcb9edaf6a7511491c0bb4e89a504"
|
||||
|
|
Loading…
Reference in New Issue