1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 07:49:41 +02:00

refactor(app): move react components to react codebase [EE-3179] (#6971)

This commit is contained in:
Chaim Lev-Ari 2022-06-17 19:18:42 +03:00 committed by GitHub
parent 212400c283
commit 18252ab854
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
346 changed files with 642 additions and 644 deletions

View file

@ -0,0 +1,68 @@
/* switch box */
.switch,
.bootbox-checkbox-list > .checkbox > label {
--switch-size: 24px;
}
.switch.small {
--switch-size: 12px;
}
.switch input {
display: none;
}
.switch i,
.bootbox-form .checkbox i {
display: inline-block;
vertical-align: middle;
cursor: pointer;
padding-right: var(--switch-size);
transition: all ease 0.2s;
-webkit-transition: all ease 0.2s;
-moz-transition: all ease 0.2s;
-o-transition: all ease 0.2s;
border-radius: var(--switch-size);
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.5);
}
.switch i:before,
.bootbox-form .checkbox i:before {
display: block;
content: '';
width: var(--switch-size);
height: var(--switch-size);
border-radius: var(--switch-size);
background: white;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.5);
}
.switch :checked + i,
.bootbox-form .checkbox :checked ~ i {
padding-right: 0;
padding-left: var(--switch-size);
-webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.5), inset 0 0 40px #337ab7;
-moz-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.5), inset 0 0 40px #337ab7;
box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.5), inset 0 0 40px #337ab7;
}
.switch :disabled + i {
opacity: 0.5;
cursor: not-allowed;
}
.switch.limited {
pointer-events: none;
touch-action: none;
}
.switch.limited i {
opacity: 1;
cursor: not-allowed;
}
.switch.business i {
background-color: var(--BE-only);
box-shadow: inset 0 0 1px rgb(0 0 0 / 50%), inset 0 0 40px var(--BE-only);
}

View file

@ -0,0 +1,3 @@
.root {
margin-bottom: 0;
}

View file

@ -0,0 +1,35 @@
import { Meta, Story } from '@storybook/react';
import { useState } from 'react';
import { Switch } from './Switch';
export default {
title: 'Components/Form/SwitchField/Switch',
} as Meta;
export function Example() {
const [isChecked, setIsChecked] = useState(false);
function onChange() {
setIsChecked(!isChecked);
}
return <Switch name="name" checked={isChecked} onChange={onChange} id="id" />;
}
interface Args {
checked: boolean;
}
function Template({ checked }: Args) {
return <Switch name="name" checked={checked} onChange={() => {}} id="id" />;
}
export const Checked: Story<Args> = Template.bind({});
Checked.args = {
checked: true,
};
export const Unchecked: Story<Args> = Template.bind({});
Unchecked.args = {
checked: false,
};

View file

@ -0,0 +1,20 @@
import { render } from '@testing-library/react';
import { PropsWithChildren } from 'react';
import { Switch, Props } from './Switch';
function renderDefault({
name = 'default name',
checked = false,
}: Partial<PropsWithChildren<Props>> = {}) {
return render(
<Switch id="id" name={name} checked={checked} onChange={() => {}} />
);
}
test('should display a Switch component', async () => {
const { findByRole } = renderDefault();
const switchElem = await findByRole('checkbox');
expect(switchElem).toBeTruthy();
});

View file

@ -0,0 +1,57 @@
import clsx from 'clsx';
import { isLimitedToBE } from '@/portainer/feature-flags/feature-flags.service';
import { FeatureId } from '@/portainer/feature-flags/enums';
import { BEFeatureIndicator } from '@@/BEFeatureIndicator';
import './Switch.css';
import styles from './Switch.module.css';
export interface Props {
checked: boolean;
id: string;
name: string;
onChange(checked: boolean): void;
className?: string;
dataCy?: string;
disabled?: boolean;
featureId?: FeatureId;
}
export function Switch({
name,
checked,
id,
disabled,
dataCy,
onChange,
featureId,
className,
}: Props) {
const limitedToBE = isLimitedToBE(featureId);
return (
<>
<label
className={clsx('switch', className, styles.root, {
business: limitedToBE,
limited: limitedToBE,
})}
>
<input
type="checkbox"
name={name}
id={id}
checked={checked}
disabled={disabled || limitedToBE}
onChange={({ target: { checked } }) => onChange(checked)}
/>
<i data-cy={dataCy} />
</label>
{limitedToBE && <BEFeatureIndicator featureId={featureId} />}
</>
);
}

View file

@ -0,0 +1,9 @@
.root {
display: flex;
align-items: center;
margin: 0;
}
.label {
padding: 0;
}

View file

@ -0,0 +1,56 @@
import { Meta, Story } from '@storybook/react';
import { useState } from 'react';
import { SwitchField } from './SwitchField';
export default {
title: 'Components/Form/SwitchField',
} as Meta;
export function Example() {
const [isChecked, setIsChecked] = useState(false);
function onChange() {
setIsChecked(!isChecked);
}
return (
<SwitchField
name="name"
checked={isChecked}
onChange={onChange}
label="Example"
/>
);
}
interface Args {
checked: boolean;
label: string;
labelClass: string;
}
function Template({ checked, label, labelClass }: Args) {
return (
<SwitchField
name="name"
checked={checked}
onChange={() => {}}
label={label}
labelClass={labelClass}
/>
);
}
export const Checked: Story<Args> = Template.bind({});
Checked.args = {
checked: true,
label: 'label',
labelClass: 'col-sm-6',
};
export const Unchecked: Story<Args> = Template.bind({});
Unchecked.args = {
checked: false,
label: 'label',
labelClass: 'col-sm-6',
};

View file

@ -0,0 +1,37 @@
import { render, fireEvent } from '@/react-tools/test-utils';
import { SwitchField, Props } from './SwitchField';
function renderDefault({
name = 'default name',
checked = false,
label = 'label',
onChange = jest.fn(),
}: Partial<Props> = {}) {
return render(
<SwitchField
label={label}
name={name}
checked={checked}
onChange={onChange}
/>
);
}
test('should display a Switch component', async () => {
const { findByRole } = renderDefault();
const switchElem = await findByRole('checkbox');
expect(switchElem).toBeTruthy();
});
test('clicking should emit on-change with the opposite value', async () => {
const onChange = jest.fn();
const checked = true;
const { findByRole } = renderDefault({ onChange, checked });
const switchElem = await findByRole('checkbox');
fireEvent.click(switchElem);
expect(onChange).toHaveBeenCalledWith(!checked);
});

View file

@ -0,0 +1,60 @@
import clsx from 'clsx';
import { FeatureId } from '@/portainer/feature-flags/enums';
import { Tooltip } from '@@/Tip/Tooltip';
import styles from './SwitchField.module.css';
import { Switch } from './Switch';
export interface Props {
label: string;
checked: boolean;
onChange(value: boolean): void;
name?: string;
tooltip?: string;
labelClass?: string;
dataCy?: string;
disabled?: boolean;
featureId?: FeatureId;
}
export function SwitchField({
tooltip,
checked,
label,
name,
labelClass,
dataCy,
disabled,
onChange,
featureId,
}: Props) {
const toggleName = name ? `toggle_${name}` : '';
return (
<label className={styles.root}>
<span
className={clsx(
'control-label text-left space-right',
styles.label,
labelClass
)}
>
{label}
{tooltip && <Tooltip position="bottom" message={tooltip} />}
</span>
<Switch
className="space-right"
name={toggleName}
id={toggleName}
checked={checked}
disabled={disabled}
onChange={onChange}
featureId={featureId}
dataCy={dataCy}
/>
</label>
);
}

View file

@ -0,0 +1 @@
export { SwitchField } from './SwitchField';