mirror of
https://github.com/portainer/portainer.git
synced 2025-07-25 00:09:40 +02:00
chore(data-cy): require data-cy attributes [EE-6880] (#11453)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
This commit is contained in:
parent
3cad13388c
commit
d38085a560
538 changed files with 2571 additions and 595 deletions
|
@ -11,6 +11,7 @@ import {
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { useDebounce } from '@/react/hooks/useDebounce';
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Option } from '@@/form-components/PortainerSelect';
|
||||
|
||||
|
@ -23,6 +24,7 @@ export function AutocompleteSelect({
|
|||
searchResults,
|
||||
readOnly,
|
||||
inputId,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: string;
|
||||
/**
|
||||
|
@ -35,7 +37,7 @@ export function AutocompleteSelect({
|
|||
searchResults?: Option<string>[];
|
||||
readOnly?: boolean;
|
||||
inputId: string;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
const [searchTerm, setSearchTerm] = useDebounce(value, onChange);
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
|
@ -54,6 +56,7 @@ export function AutocompleteSelect({
|
|||
readOnly={readOnly}
|
||||
id={inputId}
|
||||
autoComplete="off"
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
{!selected && searchResults && searchResults.length > 0 && (
|
||||
<ComboboxPopover>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { ButtonGroup, Size } from '@@/buttons/ButtonGroup';
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
|
@ -42,6 +44,7 @@ export function ButtonSelector<T extends string | number | boolean>({
|
|||
{options.map((option) => (
|
||||
<OptionItem
|
||||
key={option.value.toString()}
|
||||
data-cy={`button-selector-option-${option.value}`}
|
||||
selected={value === option.value}
|
||||
onChange={() => onChange(option.value)}
|
||||
disabled={disabled || option.disabled}
|
||||
|
@ -67,7 +70,8 @@ function OptionItem({
|
|||
onChange,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: PropsWithChildren<OptionItemProps>) {
|
||||
'data-cy': dataCy,
|
||||
}: PropsWithChildren<OptionItemProps> & AutomationTestingProps) {
|
||||
return (
|
||||
<Button
|
||||
color="light"
|
||||
|
@ -79,10 +83,12 @@ function OptionItem({
|
|||
},
|
||||
'!static !z-auto'
|
||||
)}
|
||||
data-cy={dataCy}
|
||||
>
|
||||
{children}
|
||||
<input
|
||||
type="radio"
|
||||
data-cy={`${dataCy}-radio-input`}
|
||||
checked={selected}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -18,6 +18,7 @@ interface Props extends HTMLProps<HTMLInputElement> {
|
|||
role?: string;
|
||||
onChange?: ChangeEventHandler<HTMLInputElement>;
|
||||
bold?: boolean;
|
||||
'data-cy': string;
|
||||
}
|
||||
|
||||
export const Checkbox = forwardRef<HTMLInputElement, Props>(
|
||||
|
@ -30,6 +31,7 @@ export const Checkbox = forwardRef<HTMLInputElement, Props>(
|
|||
checked,
|
||||
onChange,
|
||||
bold = true,
|
||||
'data-cy': dataCy,
|
||||
...props
|
||||
}: Props,
|
||||
ref
|
||||
|
@ -58,6 +60,7 @@ export const Checkbox = forwardRef<HTMLInputElement, Props>(
|
|||
ref={resolvedRef}
|
||||
onChange={onChange}
|
||||
checked={checked}
|
||||
data-cy={dataCy}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { List } from 'lucide-react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { CodeEditor } from '@@/CodeEditor';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { Button } from '@@/buttons';
|
||||
|
@ -11,11 +13,12 @@ export function AdvancedMode({
|
|||
value,
|
||||
onChange,
|
||||
onSimpleModeClick,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: Values;
|
||||
onChange: (value: Values) => void;
|
||||
onSimpleModeClick: () => void;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
const editorValue = convertToArrayOfStrings(value).join('\n');
|
||||
|
||||
return (
|
||||
|
@ -26,6 +29,7 @@ export function AdvancedMode({
|
|||
icon={List}
|
||||
className="!ml-0 p-0 hover:no-underline"
|
||||
onClick={onSimpleModeClick}
|
||||
data-cy="env-simple-mode-button"
|
||||
>
|
||||
Simple mode
|
||||
</Button>
|
||||
|
@ -40,6 +44,7 @@ export function AdvancedMode({
|
|||
value={editorValue}
|
||||
onChange={handleEditorChange}
|
||||
placeholder="e.g. key=value"
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -18,6 +18,7 @@ export function EnvironmentVariableItem({
|
|||
<div className="w-1/2">
|
||||
<InputLabeled
|
||||
className="w-full"
|
||||
data-cy={`env-name_${index}`}
|
||||
label="name"
|
||||
required
|
||||
value={item.name}
|
||||
|
@ -31,7 +32,7 @@ export function EnvironmentVariableItem({
|
|||
/>
|
||||
{error && (
|
||||
<div>
|
||||
<FormError className="mt-1 !mb-0">
|
||||
<FormError className="!mb-0 mt-1">
|
||||
{Object.values(error)[0]}
|
||||
</FormError>
|
||||
</div>
|
||||
|
@ -39,6 +40,7 @@ export function EnvironmentVariableItem({
|
|||
</div>
|
||||
<InputLabeled
|
||||
className="w-1/2"
|
||||
data-cy={`env-value_${index}`}
|
||||
label="value"
|
||||
value={item.value}
|
||||
onChange={(e) => handleChange({ value: e.target.value })}
|
||||
|
|
|
@ -34,6 +34,7 @@ export function EnvironmentVariablesFieldset({
|
|||
) : (
|
||||
<AdvancedMode
|
||||
onSimpleModeClick={() => setSimpleMode(true)}
|
||||
data-cy="env-var-advanced-mode"
|
||||
onChange={onChange}
|
||||
value={values}
|
||||
/>
|
||||
|
|
|
@ -34,6 +34,7 @@ export function SimpleMode({
|
|||
icon={Edit}
|
||||
className="!ml-0 p-0 hover:no-underline"
|
||||
onClick={onAdvancedModeClick}
|
||||
data-cy="environment-variables-advanced-mode-button"
|
||||
>
|
||||
Advanced mode
|
||||
</Button>
|
||||
|
@ -50,6 +51,7 @@ export function SimpleMode({
|
|||
item={EnvironmentVariableItem}
|
||||
errors={errors}
|
||||
canUndoDelete={canUndoDelete}
|
||||
data-cy="simple-environment-variable-fieldset"
|
||||
/>
|
||||
|
||||
<div className="flex gap-2">
|
||||
|
@ -60,6 +62,7 @@ export function SimpleMode({
|
|||
className="!ml-0"
|
||||
color="default"
|
||||
icon={Plus}
|
||||
data-cy="add-environment-variable-button"
|
||||
>
|
||||
Add an environment variable
|
||||
</Button>
|
||||
|
@ -84,6 +87,7 @@ function FileEnv({ onChooseFile }: { onChooseFile: (file: Values) => void }) {
|
|||
accept=".env"
|
||||
value={file}
|
||||
color="default"
|
||||
data-cy="load-environment-variables-from-file-button"
|
||||
/>
|
||||
|
||||
{fileTooBig && (
|
||||
|
|
|
@ -28,6 +28,7 @@ function Example({ title }: Args) {
|
|||
value={value}
|
||||
title={title}
|
||||
inputId="file-field"
|
||||
data-cy="file-upload-field"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ test('render should make the file button clickable and fire onChange event after
|
|||
const { findByText, findByLabelText } = render(
|
||||
<FileUploadField
|
||||
title="test button"
|
||||
data-cy="file-input"
|
||||
onChange={onClick}
|
||||
inputId="file-field"
|
||||
/>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { ChangeEvent, ComponentProps, createRef } from 'react';
|
||||
import { Upload, XCircle } from 'lucide-react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './FileUploadField.module.css';
|
||||
|
||||
export interface Props {
|
||||
export interface Props extends AutomationTestingProps {
|
||||
onChange(value: File): void;
|
||||
value?: File | null;
|
||||
accept?: string;
|
||||
|
@ -26,6 +28,7 @@ export function FileUploadField({
|
|||
inputId,
|
||||
color = 'primary',
|
||||
name,
|
||||
'data-cy': dataCy,
|
||||
}: Props) {
|
||||
const fileRef = createRef<HTMLInputElement>();
|
||||
|
||||
|
@ -47,6 +50,7 @@ export function FileUploadField({
|
|||
color={color}
|
||||
onClick={handleButtonClick}
|
||||
className={styles.fileButton}
|
||||
data-cy={dataCy}
|
||||
icon={Upload}
|
||||
>
|
||||
{title}
|
||||
|
|
|
@ -31,6 +31,7 @@ function Example({ title }: Args) {
|
|||
description={
|
||||
<span>You can upload a Compose file from your computer.</span>
|
||||
}
|
||||
data-cy="file-upload-form"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@ test('render should include description', async () => {
|
|||
title="test button"
|
||||
onChange={onClick}
|
||||
description={<span>test description</span>}
|
||||
data-cy="test"
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { FormSectionTitle } from '@@/form-components/FormSectionTitle';
|
||||
import { FileUploadField } from '@@/form-components/FileUpload/FileUploadField';
|
||||
|
||||
|
@ -17,7 +19,8 @@ export function FileUploadForm({
|
|||
title = 'Select a file',
|
||||
required = false,
|
||||
description,
|
||||
}: PropsWithChildren<Props>) {
|
||||
'data-cy': dataCy,
|
||||
}: PropsWithChildren<Props> & AutomationTestingProps) {
|
||||
return (
|
||||
<div className="file-upload-form">
|
||||
<FormSectionTitle>Upload</FormSectionTitle>
|
||||
|
@ -28,6 +31,7 @@ export function FileUploadForm({
|
|||
<div className="col-sm-12">
|
||||
<FileUploadField
|
||||
inputId="file-upload-field"
|
||||
data-cy={dataCy}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
title={title}
|
||||
|
|
|
@ -42,6 +42,7 @@ function TextField({
|
|||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
data-cy="input"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
@ -79,6 +80,7 @@ function SelectField({
|
|||
>
|
||||
<Select
|
||||
className="form-control"
|
||||
data-cy="select"
|
||||
value={value}
|
||||
onChange={(e) => setValue(parseInt(e.target.value, 10))}
|
||||
options={options}
|
||||
|
|
|
@ -22,6 +22,7 @@ export function TextField({ disabled }: Args) {
|
|||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
disabled={disabled}
|
||||
data-cy="docker-logging-options-input"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import clsx from 'clsx';
|
||||
import { forwardRef, InputHTMLAttributes, Ref } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
export const InputWithRef = forwardRef<
|
||||
HTMLInputElement,
|
||||
InputHTMLAttributes<HTMLInputElement>
|
||||
InputHTMLAttributes<HTMLInputElement> & AutomationTestingProps
|
||||
>(
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
(props, ref) => <Input {...props} mRef={ref} />
|
||||
|
@ -14,10 +16,11 @@ export function Input({
|
|||
mRef: ref,
|
||||
value,
|
||||
type,
|
||||
'data-cy': dataCy,
|
||||
...props
|
||||
}: InputHTMLAttributes<HTMLInputElement> & {
|
||||
mRef?: Ref<HTMLInputElement>;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
return (
|
||||
<input
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
|
@ -26,6 +29,7 @@ export function Input({
|
|||
value={type === 'number' && Number.isNaN(value) ? '' : value} // avoid the `"NaN" cannot be parsed, or is out of range.` error for an empty number input
|
||||
ref={ref}
|
||||
className={clsx('form-control', className)}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ function TextInput() {
|
|||
return (
|
||||
<InputLabeled
|
||||
label="label"
|
||||
data-cy="input"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
|
@ -28,6 +29,7 @@ function NumberInput() {
|
|||
return (
|
||||
<InputLabeled
|
||||
label="label"
|
||||
data-cy="input"
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.valueAsNumber)}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { ComponentProps, InputHTMLAttributes } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { InputGroup } from '../InputGroup';
|
||||
|
||||
export function InputLabeled({
|
||||
|
@ -17,7 +19,8 @@ export function InputLabeled({
|
|||
className?: string;
|
||||
size?: ComponentProps<typeof InputGroup>['size'];
|
||||
needsDeletion?: boolean;
|
||||
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'children'>) {
|
||||
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'children'> &
|
||||
AutomationTestingProps) {
|
||||
return (
|
||||
<InputGroup
|
||||
className={clsx(className, needsDeletion && 'striked')}
|
||||
|
|
|
@ -23,6 +23,7 @@ export function Example({ disabled }: Args) {
|
|||
return (
|
||||
<Select
|
||||
value={value}
|
||||
data-cy="select"
|
||||
onChange={(e) => setValue(parseInt(e.target.value, 10))}
|
||||
disabled={disabled}
|
||||
options={options}
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
import clsx from 'clsx';
|
||||
import { SelectHTMLAttributes } from 'react';
|
||||
|
||||
export interface Option<T extends string | number> {
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
export interface Option<T extends string | number>
|
||||
extends Partial<AutomationTestingProps> {
|
||||
value: T;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface Props<T extends string | number> {
|
||||
interface Props<T extends string | number> extends AutomationTestingProps {
|
||||
options: Option<T>[];
|
||||
}
|
||||
|
||||
export function Select<T extends number | string>({
|
||||
options,
|
||||
className,
|
||||
'data-cy': dataCy,
|
||||
...props
|
||||
}: Props<T> & SelectHTMLAttributes<HTMLSelectElement>) {
|
||||
return (
|
||||
|
@ -20,9 +25,15 @@ export function Select<T extends number | string>({
|
|||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
className={clsx('form-control', className)}
|
||||
data-cy={dataCy}
|
||||
>
|
||||
{options.map((item) => (
|
||||
<option value={item.value} key={item.value}>
|
||||
<option
|
||||
value={item.value}
|
||||
key={item.value}
|
||||
disabled={item.disabled}
|
||||
data-cy={`${dataCy}-${item.value}`}
|
||||
>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
|
|
10
app/react/components/form-components/Input/Select.tsx.rej
Normal file
10
app/react/components/form-components/Input/Select.tsx.rej
Normal file
|
@ -0,0 +1,10 @@
|
|||
diff a/app/react/components/form-components/Input/Select.tsx b/app/react/components/form-components/Input/Select.tsx (rejected hunks)
|
||||
@@ -10,7 +10,7 @@ export interface Option<T extends string | number>
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
-interface Props<T extends string | number> {
|
||||
+interface Props<T extends string | number> extends AutomationTestingProps {
|
||||
options: Option<T>[];
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import clsx from 'clsx';
|
||||
import { TextareaHTMLAttributes } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
export function TextArea({
|
||||
className,
|
||||
...props
|
||||
}: TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
||||
}: TextareaHTMLAttributes<HTMLTextAreaElement> & AutomationTestingProps) {
|
||||
return (
|
||||
<textarea
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
|
|
|
@ -20,6 +20,7 @@ function BasicExample() {
|
|||
<InputGroup.Addon>@</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
placeholder="Username"
|
||||
aria-describedby="basic-addon1"
|
||||
|
@ -29,6 +30,7 @@ function BasicExample() {
|
|||
<InputGroup>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
placeholder="Recipient's username"
|
||||
aria-describedby="basic-addon2"
|
||||
|
@ -40,6 +42,7 @@ function BasicExample() {
|
|||
<InputGroup.Addon>$</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
type="number"
|
||||
data-cy="input"
|
||||
value={valueNumber}
|
||||
onChange={(e) => setValueNumber(parseInt(e.target.value, 10))}
|
||||
aria-label="Amount (to the nearest dollar)"
|
||||
|
@ -52,6 +55,7 @@ function BasicExample() {
|
|||
<InputGroup.Addon>https://example.com/users/</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
id="basic-url"
|
||||
aria-describedby="basic-addon3"
|
||||
|
@ -75,6 +79,7 @@ function Addons() {
|
|||
</InputGroup.ButtonWrapper>
|
||||
<InputGroup.Input
|
||||
value={value1}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue1(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
@ -83,10 +88,11 @@ function Addons() {
|
|||
<InputGroup>
|
||||
<InputGroup.Input
|
||||
value={value2}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue2(e.target.value)}
|
||||
/>
|
||||
<InputGroup.Addon>
|
||||
<input type="checkbox" />
|
||||
<input type="checkbox" data-cy="checkbox" />
|
||||
</InputGroup.Addon>
|
||||
</InputGroup>
|
||||
</div>
|
||||
|
@ -102,6 +108,7 @@ function Sizing() {
|
|||
<InputGroup.Addon>Small</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
@ -110,6 +117,7 @@ function Sizing() {
|
|||
<InputGroup.Addon>Default</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
@ -118,6 +126,7 @@ function Sizing() {
|
|||
<InputGroup.Addon>Large</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={value}
|
||||
data-cy="input"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
|
|
@ -22,6 +22,7 @@ function Defaults() {
|
|||
label="default example"
|
||||
value={values}
|
||||
onChange={(value) => setValues(value)}
|
||||
data-cy="input-list-default-example"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -37,6 +38,7 @@ function ListWithUndoDeletion() {
|
|||
value={values}
|
||||
onChange={(value) => setValues(value)}
|
||||
canUndoDelete
|
||||
data-cy="input-list-with-undo-deletion"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -71,6 +73,7 @@ function ListWithInputAndSelect({
|
|||
movable={movable}
|
||||
itemBuilder={() => ({ value: 0, select: '', id: values.length })}
|
||||
tooltip={tooltip}
|
||||
data-cy="input-list-with-select-and-input"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -96,9 +99,11 @@ function SelectAndInputItem({
|
|||
onChange={(e) =>
|
||||
onChange({ ...item, value: parseInt(e.target.value, 10) })
|
||||
}
|
||||
data-cy="input"
|
||||
/>
|
||||
<Select
|
||||
onChange={(e) => onChange({ ...item, select: e.target.value })}
|
||||
data-cy="select"
|
||||
options={[
|
||||
{ label: 'option1', value: 'option1' },
|
||||
{ label: 'option2', value: 'option2' },
|
||||
|
|
|
@ -3,6 +3,8 @@ import { FormikErrors } from 'formik';
|
|||
import { ArrowDown, ArrowUp, Plus, RotateCw, Trash2 } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
|
@ -53,10 +55,11 @@ type RenderItemFunction<T> = (
|
|||
item: T,
|
||||
onChange: (value: T) => void,
|
||||
index: number,
|
||||
dataCy: string,
|
||||
error?: ItemError<T>
|
||||
) => React.ReactNode;
|
||||
|
||||
interface Props<T> {
|
||||
interface Props<T> extends AutomationTestingProps {
|
||||
label?: string;
|
||||
value: T[];
|
||||
onChange(value: T[], e: OnChangeEvent<T>): void;
|
||||
|
@ -71,9 +74,7 @@ interface Props<T> {
|
|||
errors?: ArrayError<T[]>;
|
||||
textTip?: string;
|
||||
isAddButtonHidden?: boolean;
|
||||
addButtonDataCy?: string;
|
||||
isDeleteButtonHidden?: boolean;
|
||||
deleteButtonDataCy?: string;
|
||||
disabled?: boolean;
|
||||
addButtonError?: string;
|
||||
readOnly?: boolean;
|
||||
|
@ -95,9 +96,8 @@ export function InputList<T = DefaultType>({
|
|||
errors,
|
||||
textTip,
|
||||
isAddButtonHidden = false,
|
||||
addButtonDataCy,
|
||||
isDeleteButtonHidden = false,
|
||||
deleteButtonDataCy,
|
||||
'data-cy': dataCy,
|
||||
disabled,
|
||||
addButtonError,
|
||||
readOnly,
|
||||
|
@ -161,6 +161,7 @@ export function InputList<T = DefaultType>({
|
|||
item,
|
||||
(value: T) => handleChangeItem(key, value),
|
||||
index,
|
||||
dataCy,
|
||||
error
|
||||
)
|
||||
)}
|
||||
|
@ -173,6 +174,7 @@ export function InputList<T = DefaultType>({
|
|||
onClick={() => handleMoveUp(index)}
|
||||
className="vertical-center btn-only-icon"
|
||||
icon={ArrowUp}
|
||||
data-cy={`${dataCy}-move-up_${index}`}
|
||||
/>
|
||||
<Button
|
||||
size="medium"
|
||||
|
@ -181,6 +183,7 @@ export function InputList<T = DefaultType>({
|
|||
onClick={() => handleMoveDown(index)}
|
||||
className="vertical-center btn-only-icon"
|
||||
icon={ArrowDown}
|
||||
data-cy={`${dataCy}-move-down_${index}`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -190,7 +193,7 @@ export function InputList<T = DefaultType>({
|
|||
size="medium"
|
||||
onClick={() => handleRemoveItem(key, item)}
|
||||
className="vertical-center btn-only-icon"
|
||||
data-cy={`${deleteButtonDataCy}_${index}`}
|
||||
data-cy={`${dataCy}RemoveButton_${index}`}
|
||||
icon={Trash2}
|
||||
/>
|
||||
)}
|
||||
|
@ -203,7 +206,7 @@ export function InputList<T = DefaultType>({
|
|||
initialItemsCount={initialItemsCount.current}
|
||||
handleRemoveItem={handleRemoveItem}
|
||||
handleToggleNeedsDeletion={toggleNeedsDeletion}
|
||||
dataCy={`${deleteButtonDataCy}_${index}`}
|
||||
dataCy={`${dataCy}RemoveButton_${index}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -224,7 +227,7 @@ export function InputList<T = DefaultType>({
|
|||
className="!ml-0"
|
||||
size="small"
|
||||
icon={Plus}
|
||||
data-cy={addButtonDataCy}
|
||||
data-cy={`${dataCy}AddButton`}
|
||||
>
|
||||
{addLabel}
|
||||
</Button>
|
||||
|
@ -332,7 +335,9 @@ function DefaultItem({
|
|||
error,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: ItemProps<DefaultType>) {
|
||||
index,
|
||||
'data-cy': dataCy,
|
||||
}: ItemProps<DefaultType> & AutomationTestingProps) {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
|
@ -341,6 +346,7 @@ function DefaultItem({
|
|||
className={clsx('!w-full', item.needsDeletion && 'striked')}
|
||||
disabled={disabled || item.needsDeletion}
|
||||
readOnly={readOnly}
|
||||
data-cy={`${dataCy}RemoveButton_${index}`}
|
||||
/>
|
||||
{error && <FormError>{error}</FormError>}
|
||||
</>
|
||||
|
@ -351,10 +357,17 @@ function renderDefaultItem(
|
|||
item: DefaultType,
|
||||
onChange: (value: DefaultType) => void,
|
||||
index: number,
|
||||
dataCy: string,
|
||||
error?: ItemError<DefaultType>
|
||||
) {
|
||||
return (
|
||||
<DefaultItem item={item} onChange={onChange} error={error} index={index} />
|
||||
<DefaultItem
|
||||
item={item}
|
||||
onChange={onChange}
|
||||
error={error}
|
||||
index={index}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ export interface Props {
|
|||
step: number;
|
||||
value: number;
|
||||
onChange: (value: number | number[]) => void;
|
||||
dataCy?: string;
|
||||
dataCy: string;
|
||||
// true if you want to always show the tooltip
|
||||
visibleTooltip?: boolean;
|
||||
disabled?: boolean;
|
||||
|
|
|
@ -13,7 +13,15 @@ export function Example() {
|
|||
setIsChecked(!isChecked);
|
||||
}
|
||||
|
||||
return <Switch name="name" checked={isChecked} onChange={onChange} id="id" />;
|
||||
return (
|
||||
<Switch
|
||||
name="name"
|
||||
data-cy="switch"
|
||||
checked={isChecked}
|
||||
onChange={onChange}
|
||||
id="id"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
interface Args {
|
||||
|
@ -21,7 +29,15 @@ interface Args {
|
|||
}
|
||||
|
||||
function Template({ checked }: Args) {
|
||||
return <Switch name="name" checked={checked} onChange={() => {}} id="id" />;
|
||||
return (
|
||||
<Switch
|
||||
name="name"
|
||||
data-cy="switch"
|
||||
checked={checked}
|
||||
onChange={() => {}}
|
||||
id="id"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const Checked: Story<Args> = Template.bind({});
|
||||
|
|
|
@ -8,7 +8,13 @@ function renderDefault({
|
|||
checked = false,
|
||||
}: Partial<PropsWithChildren<Props>> = {}) {
|
||||
return render(
|
||||
<Switch id="id" name={name} checked={checked} onChange={() => {}} />
|
||||
<Switch
|
||||
id="id"
|
||||
name={name}
|
||||
checked={checked}
|
||||
onChange={() => {}}
|
||||
data-cy="switch"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ export function Example() {
|
|||
return (
|
||||
<SwitchField
|
||||
name="name"
|
||||
data-cy="switch-field-example"
|
||||
checked={isChecked}
|
||||
onChange={onChange}
|
||||
label="Example"
|
||||
|
@ -33,6 +34,7 @@ function Template({ checked, label, labelClass }: Args) {
|
|||
return (
|
||||
<SwitchField
|
||||
name="name"
|
||||
data-cy="switch-field-example"
|
||||
checked={checked}
|
||||
onChange={() => {}}
|
||||
label={label}
|
||||
|
|
|
@ -12,6 +12,7 @@ function renderDefault({
|
|||
return render(
|
||||
<SwitchField
|
||||
label={label}
|
||||
data-cy="switch-field"
|
||||
name={name}
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue