mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
refactor(azure): migrate module to react [EE-2782] (#6689)
* refactor(azure): migrate module to react [EE-2782] * fix(azure): remove optional chain * feat(azure): apply new icons in dashboard * feat(azure): apply new icons in dashboard * feat(ui): allow single string for breadcrumbs * refactor(azure/containers): use Table.content * feat(azure/containers): implement new ui [EE-3538] * fix(azure/containers): use correct icon * chore(tests): mock svg as component * fix(azure): fix tests Co-authored-by: matias.spinarolli <matias.spinarolli@portainer.io>
This commit is contained in:
parent
b059641c80
commit
82b848af0c
97 changed files with 1723 additions and 1430 deletions
|
@ -10,16 +10,20 @@ export interface Crumb {
|
|||
linkParams?: Record<string, unknown>;
|
||||
}
|
||||
interface Props {
|
||||
breadcrumbs: (Crumb | string)[];
|
||||
breadcrumbs: (Crumb | string)[] | string;
|
||||
}
|
||||
|
||||
export function Breadcrumbs({ breadcrumbs }: Props) {
|
||||
const breadcrumbsArray = Array.isArray(breadcrumbs)
|
||||
? breadcrumbs
|
||||
: [breadcrumbs];
|
||||
|
||||
return (
|
||||
<div className="breadcrumb-links">
|
||||
{breadcrumbs.map((crumb, index) => (
|
||||
{breadcrumbsArray.map((crumb, index) => (
|
||||
<Fragment key={index}>
|
||||
{renderCrumb(crumb)}
|
||||
{index !== breadcrumbs.length - 1 ? ' > ' : ''}
|
||||
{index !== breadcrumbsArray.length - 1 ? ' > ' : ''}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ interface Props {
|
|||
reload?: boolean;
|
||||
loading?: boolean;
|
||||
onReload?(): Promise<void> | void;
|
||||
breadcrumbs?: Crumb[];
|
||||
breadcrumbs?: (Crumb | string)[] | string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@ import styles from './AddButton.module.css';
|
|||
export interface Props {
|
||||
className?: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function AddButton({ label, onClick, className }: Props) {
|
||||
export function AddButton({ label, onClick, className, disabled }: Props) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
|
@ -20,6 +21,7 @@ export function AddButton({ label, onClick, className }: Props) {
|
|||
)}
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
<i className="fa fa-plus-circle space-right" aria-hidden="true" /> {label}
|
||||
</button>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
import { Icon, IconProps } from '@@/Icon';
|
||||
|
||||
import { useTableContext } from './TableContainer';
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
interface Props extends IconProps {
|
||||
label: string;
|
||||
featherIcon?: boolean;
|
||||
}
|
||||
|
||||
export function TableTitle({
|
||||
|
|
|
@ -25,7 +25,7 @@ interface UseRowSelectTableInstance<D extends DefaultType = DefaultType>
|
|||
isAllRowSelected: boolean;
|
||||
selectSubRows: boolean;
|
||||
getSubRows(row: Row<D>): Row<D>[];
|
||||
isRowSelectable(row: Row<D>): boolean;
|
||||
isRowSelectable?(row: Row<D>): boolean;
|
||||
}
|
||||
|
||||
const pluginName = 'useRowSelect';
|
||||
|
@ -73,7 +73,10 @@ function defaultGetToggleRowSelectedProps<D extends DefaultType>(
|
|||
props: D,
|
||||
{ instance, row }: { instance: UseRowSelectTableInstance<D>; row: Row<D> }
|
||||
) {
|
||||
const { manualRowSelectedKey = 'isSelected' } = instance;
|
||||
const {
|
||||
manualRowSelectedKey = 'isSelected',
|
||||
isRowSelectable = defaultIsRowSelectable,
|
||||
} = instance;
|
||||
let checked = false;
|
||||
|
||||
if (row.original && row.original[manualRowSelectedKey]) {
|
||||
|
@ -94,7 +97,7 @@ function defaultGetToggleRowSelectedProps<D extends DefaultType>(
|
|||
checked,
|
||||
title: 'Toggle Row Selected',
|
||||
indeterminate: row.isSomeSelected,
|
||||
disabled: !instance.isRowSelectable(row),
|
||||
disabled: !isRowSelectable(row),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -317,7 +320,7 @@ function useInstance<D extends Record<string, unknown>>(
|
|||
dispatch,
|
||||
page,
|
||||
getSubRows,
|
||||
isRowSelectable,
|
||||
isRowSelectable = defaultIsRowSelectable,
|
||||
} = instance;
|
||||
|
||||
ensurePluginOrder(
|
||||
|
@ -474,5 +477,5 @@ function getRowIsSelected<D extends Record<string, unknown>>(
|
|||
}
|
||||
|
||||
function defaultIsRowSelectable<D extends DefaultType>(row: Row<D>) {
|
||||
return !!row.original.disabled;
|
||||
return !row.original.disabled;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ interface Props<T> {
|
|||
onChange(value: T): void;
|
||||
options: Option<T>[];
|
||||
size?: Size;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export function ButtonSelector<T extends string | number>({
|
||||
|
@ -22,6 +24,8 @@ export function ButtonSelector<T extends string | number>({
|
|||
onChange,
|
||||
size,
|
||||
options,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: Props<T>) {
|
||||
return (
|
||||
<ButtonGroup size={size} className={styles.group}>
|
||||
|
@ -30,6 +34,8 @@ export function ButtonSelector<T extends string | number>({
|
|||
key={option.value}
|
||||
selected={value === option.value}
|
||||
onChange={() => onChange(option.value)}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
>
|
||||
{option.label || option.value.toString()}
|
||||
</OptionItem>
|
||||
|
@ -41,17 +47,32 @@ export function ButtonSelector<T extends string | number>({
|
|||
interface OptionItemProps {
|
||||
selected: boolean;
|
||||
onChange(): void;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
function OptionItem({
|
||||
selected,
|
||||
children,
|
||||
onChange,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: PropsWithChildren<OptionItemProps>) {
|
||||
return (
|
||||
<label className={clsx('btn btn-primary', { active: selected })}>
|
||||
<label
|
||||
className={clsx('btn btn-primary', {
|
||||
active: selected,
|
||||
disabled: readOnly || disabled,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
<input type="radio" checked={selected} onChange={onChange} />
|
||||
<input
|
||||
type="radio"
|
||||
checked={selected}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ComponentType } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { FormikErrors } from 'formik';
|
||||
|
||||
import { AddButton, Button } from '@@/buttons';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
|
@ -11,12 +12,12 @@ import { FormError } from '../FormError';
|
|||
import styles from './InputList.module.css';
|
||||
import { arrayMove } from './utils';
|
||||
|
||||
export type InputListError<T> = Record<keyof T, string>;
|
||||
|
||||
export interface ItemProps<T> {
|
||||
item: T;
|
||||
onChange(value: T): void;
|
||||
error?: InputListError<T>;
|
||||
error?: string | FormikErrors<T>;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
type Key = string | number;
|
||||
type ChangeType = 'delete' | 'create' | 'update';
|
||||
|
@ -36,7 +37,7 @@ type OnChangeEvent<T> =
|
|||
type RenderItemFunction<T> = (
|
||||
item: T,
|
||||
onChange: (value: T) => void,
|
||||
error?: InputListError<T>
|
||||
error?: string | FormikErrors<T>
|
||||
) => React.ReactNode;
|
||||
|
||||
interface Props<T> {
|
||||
|
@ -50,9 +51,11 @@ interface Props<T> {
|
|||
addLabel?: string;
|
||||
itemKeyGetter?(item: T, index: number): Key;
|
||||
movable?: boolean;
|
||||
errors?: InputListError<T>[] | string;
|
||||
errors?: FormikErrors<T>[] | string | string[];
|
||||
textTip?: string;
|
||||
isAddButtonHidden?: boolean;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export function InputList<T = DefaultType>({
|
||||
|
@ -69,6 +72,8 @@ export function InputList<T = DefaultType>({
|
|||
errors,
|
||||
textTip,
|
||||
isAddButtonHidden = false,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: Props<T>) {
|
||||
return (
|
||||
<div className={clsx('form-group', styles.root)}>
|
||||
|
@ -77,11 +82,12 @@ export function InputList<T = DefaultType>({
|
|||
{label}
|
||||
{tooltip && <Tooltip message={tooltip} />}
|
||||
</div>
|
||||
{!isAddButtonHidden && (
|
||||
{!(isAddButtonHidden || readOnly) && (
|
||||
<AddButton
|
||||
label={addLabel}
|
||||
className="space-left"
|
||||
onClick={handleAdd}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -107,6 +113,8 @@ export function InputList<T = DefaultType>({
|
|||
item={item}
|
||||
onChange={(value: T) => handleChangeItem(key, value)}
|
||||
error={error}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
) : (
|
||||
renderItem(
|
||||
|
@ -116,11 +124,11 @@ export function InputList<T = DefaultType>({
|
|||
)
|
||||
)}
|
||||
<div className={clsx(styles.itemActions, 'items-start')}>
|
||||
{movable && (
|
||||
{!readOnly && movable && (
|
||||
<>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={index === 0}
|
||||
disabled={disabled || index === 0}
|
||||
onClick={() => handleMoveUp(index)}
|
||||
>
|
||||
<i className="fa fa-arrow-up" aria-hidden="true" />
|
||||
|
@ -128,20 +136,23 @@ export function InputList<T = DefaultType>({
|
|||
<Button
|
||||
size="small"
|
||||
type="button"
|
||||
disabled={index === value.length - 1}
|
||||
disabled={disabled || index === value.length - 1}
|
||||
onClick={() => handleMoveDown(index)}
|
||||
>
|
||||
<i className="fa fa-arrow-down" aria-hidden="true" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
color="danger"
|
||||
size="small"
|
||||
onClick={() => handleRemoveItem(key, item)}
|
||||
>
|
||||
<i className="fa fa-trash" aria-hidden="true" />
|
||||
</Button>
|
||||
{!readOnly && (
|
||||
<Button
|
||||
color="danger"
|
||||
size="small"
|
||||
onClick={() => handleRemoveItem(key, item)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<i className="fa fa-trash" aria-hidden="true" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -210,13 +221,21 @@ function defaultItemBuilder(): DefaultType {
|
|||
return { value: '' };
|
||||
}
|
||||
|
||||
function DefaultItem({ item, onChange, error }: ItemProps<DefaultType>) {
|
||||
function DefaultItem({
|
||||
item,
|
||||
onChange,
|
||||
error,
|
||||
disabled,
|
||||
readOnly,
|
||||
}: ItemProps<DefaultType>) {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
value={item.value}
|
||||
onChange={(e) => onChange({ value: e.target.value })}
|
||||
className={styles.defaultItem}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
{error && <FormError>{error}</FormError>}
|
||||
</>
|
||||
|
@ -226,7 +245,7 @@ function DefaultItem({ item, onChange, error }: ItemProps<DefaultType>) {
|
|||
function renderDefaultItem(
|
||||
item: DefaultType,
|
||||
onChange: (value: DefaultType) => void,
|
||||
error?: InputListError<DefaultType>
|
||||
error?: FormikErrors<DefaultType>
|
||||
) {
|
||||
return <DefaultItem item={item} onChange={onChange} error={error} />;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue