mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
refactor(edge/stacks): migrate edit view to react [EE-2222] (#11648)
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
27e309754e
commit
cd5f342da0
31 changed files with 847 additions and 499 deletions
|
@ -3,7 +3,7 @@ import { StreamLanguage, LanguageSupport } from '@codemirror/language';
|
|||
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
|
||||
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
|
||||
import { shell } from '@codemirror/legacy-modes/mode/shell';
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
import { tags as highlightTags } from '@lezer/highlight';
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { CopyButton } from '@@/buttons/CopyButton';
|
|||
|
||||
import styles from './CodeEditor.module.css';
|
||||
import { TextTip } from './Tip/TextTip';
|
||||
import { StackVersionSelector } from './StackVersionSelector';
|
||||
|
||||
interface Props extends AutomationTestingProps {
|
||||
id: string;
|
||||
|
@ -24,6 +25,8 @@ interface Props extends AutomationTestingProps {
|
|||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
height?: string;
|
||||
versions?: number[];
|
||||
onVersionChange?: (version: number) => void;
|
||||
}
|
||||
|
||||
const theme = createTheme({
|
||||
|
@ -65,12 +68,16 @@ export function CodeEditor({
|
|||
placeholder,
|
||||
readonly,
|
||||
value,
|
||||
versions,
|
||||
onVersionChange,
|
||||
height = '500px',
|
||||
yaml: isYaml,
|
||||
dockerFile: isDockerFile,
|
||||
shell: isShell,
|
||||
'data-cy': dataCy,
|
||||
}: Props) {
|
||||
const [isRollback, setIsRollback] = useState(false);
|
||||
|
||||
const extensions = useMemo(() => {
|
||||
const extensions = [];
|
||||
if (isYaml) {
|
||||
|
@ -85,30 +92,56 @@ export function CodeEditor({
|
|||
return extensions;
|
||||
}, [isYaml, isDockerFile, isShell]);
|
||||
|
||||
function handleVersionChange(version: number) {
|
||||
if (versions && versions.length > 1) {
|
||||
if (version < versions[0]) {
|
||||
setIsRollback(true);
|
||||
} else {
|
||||
setIsRollback(false);
|
||||
}
|
||||
}
|
||||
|
||||
onVersionChange?.(version);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div className="flex flex-1 items-center">
|
||||
{!!placeholder && <TextTip color="blue">{placeholder}</TextTip>}
|
||||
</div>
|
||||
<div className="mb-2 flex flex-col">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
{!!placeholder && <TextTip color="blue">{placeholder}</TextTip>}
|
||||
</div>
|
||||
|
||||
<CopyButton
|
||||
data-cy={`copy-code-button-${id}`}
|
||||
fadeDelay={2500}
|
||||
copyText={value}
|
||||
color="link"
|
||||
className="!pr-0 !text-sm !font-medium hover:no-underline focus:no-underline"
|
||||
indicatorPosition="left"
|
||||
>
|
||||
Copy to clipboard
|
||||
</CopyButton>
|
||||
<div className="flex-2 ml-auto mr-2 flex items-center gap-x-2">
|
||||
<CopyButton
|
||||
data-cy={`copy-code-button-${id}`}
|
||||
fadeDelay={2500}
|
||||
copyText={value}
|
||||
color="link"
|
||||
className="!pr-0 !text-sm !font-medium hover:no-underline focus:no-underline"
|
||||
indicatorPosition="left"
|
||||
>
|
||||
Copy to clipboard
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
{versions && (
|
||||
<div className="mt-2 flex">
|
||||
<div className="ml-auto mr-2">
|
||||
<StackVersionSelector
|
||||
versions={versions}
|
||||
onChange={handleVersionChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CodeMirror
|
||||
className={styles.root}
|
||||
theme={theme}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
readOnly={readonly}
|
||||
readOnly={readonly || isRollback}
|
||||
id={id}
|
||||
extensions={extensions}
|
||||
height={height}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { ReactNode } from 'react';
|
||||
import { ComponentProps, ReactNode } from 'react';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
import styles from './NavTabs.module.css';
|
||||
|
||||
|
@ -8,10 +10,11 @@ export interface Option<T extends string | number = string> {
|
|||
children?: ReactNode;
|
||||
id: T;
|
||||
hidden?: boolean;
|
||||
icon?: ComponentProps<typeof Button>['icon'];
|
||||
}
|
||||
|
||||
interface Props<T extends string | number> {
|
||||
options: Option<T>[];
|
||||
options: Array<Option<T>> | ReadonlyArray<Option<T>>;
|
||||
selectedId?: T;
|
||||
onSelect?(id: T): void;
|
||||
disabled?: boolean;
|
||||
|
@ -47,18 +50,16 @@ export function NavTabs<T extends string | number = string>({
|
|||
>
|
||||
{/* rule disabled because `nav-tabs` requires an anchor */}
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a
|
||||
<Button
|
||||
color="none"
|
||||
onClick={() => handleSelect(option)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleSelect(option);
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
as="a"
|
||||
data-cy="nav-tab-button"
|
||||
className="!flex"
|
||||
icon={option.icon}
|
||||
>
|
||||
{option.label}
|
||||
</a>
|
||||
</Button>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
interface Props {
|
||||
versions?: number[];
|
||||
onChange(value: number): void;
|
||||
}
|
||||
|
||||
export function StackVersionSelector({ versions, onChange }: Props) {
|
||||
if (!versions || versions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showSelector = versions.length > 1;
|
||||
|
||||
const versionOptions = versions.map((version) => ({
|
||||
value: version,
|
||||
label: version.toString(),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
{!showSelector && (
|
||||
<>
|
||||
<label className="text-muted mr-2" htmlFor="version_id">
|
||||
<span>Version:</span>
|
||||
</label>
|
||||
<span className="text-muted" id="version_id">
|
||||
{versions[0]}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showSelector && (
|
||||
<div className="text-muted">
|
||||
<label className="mr-2" htmlFor="version_id">
|
||||
<span>Version:</span>
|
||||
</label>
|
||||
<select
|
||||
className="form-select"
|
||||
data-cy="version-selector"
|
||||
style={{
|
||||
width: '60px',
|
||||
height: '24px',
|
||||
borderRadius: '4px',
|
||||
borderColor: 'hsl(0, 0%, 80%)',
|
||||
padding: '2px 8px',
|
||||
}}
|
||||
onChange={(e) => onChange(parseInt(e.target.value, 10))}
|
||||
>
|
||||
{versionOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
1
app/react/components/StackVersionSelector/index.ts
Normal file
1
app/react/components/StackVersionSelector/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { StackVersionSelector } from './StackVersionSelector';
|
|
@ -16,7 +16,7 @@ import { buildConfirmButton } from './modals/utils';
|
|||
const otherEditorConfig = {
|
||||
tooltip: (
|
||||
<>
|
||||
<div>CtrlF - Start searching</div>
|
||||
<div>Ctrl+F - Start searching</div>
|
||||
<div>Ctrl+G - Find next</div>
|
||||
<div>Ctrl+Shift+G - Find previous</div>
|
||||
<div>Ctrl+Shift+F - Replace</div>
|
||||
|
@ -63,6 +63,8 @@ interface Props extends AutomationTestingProps {
|
|||
titleContent?: React.ReactNode;
|
||||
hideTitle?: boolean;
|
||||
error?: string;
|
||||
versions?: number[];
|
||||
onVersionChange?: (version: number) => void;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
|
@ -77,6 +79,8 @@ export function WebEditorForm({
|
|||
yaml,
|
||||
children,
|
||||
error,
|
||||
versions,
|
||||
onVersionChange,
|
||||
height,
|
||||
'data-cy': dataCy,
|
||||
}: PropsWithChildren<Props>) {
|
||||
|
@ -106,6 +110,8 @@ export function WebEditorForm({
|
|||
yaml={yaml}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
versions={versions}
|
||||
onVersionChange={(v) => onVersionChange && onVersionChange(v)}
|
||||
height={height}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue