From e640804c04337d2f4ce49c875f005407ce14de3c Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Thu, 17 Feb 2022 22:05:18 +0200
Subject: [PATCH 1/9] feat: create base prev conf editor layout
---
.../PreviewConfigurationEditor.tsx | 20 ++++++++++++++++-
.../PreviewConfigurationEditor/styled.tsx | 22 +++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 src/components/organisms/PreviewConfigurationEditor/styled.tsx
diff --git a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
index 403d3614a6..82ccdd23ff 100644
--- a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
+++ b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
@@ -1,5 +1,23 @@
+import {Input} from 'antd';
+
+import * as S from './styled';
+
const PreviewConfigurationEditor = () => {
- return
;
+ return (
+
+
+ Name your configuration:
+
+
+
+ Select which values files to use:
+ Drag and drop to specify order
+
+
+ Specify options:
+
+
+ );
};
export default PreviewConfigurationEditor;
diff --git a/src/components/organisms/PreviewConfigurationEditor/styled.tsx b/src/components/organisms/PreviewConfigurationEditor/styled.tsx
new file mode 100644
index 0000000000..68a7db3edb
--- /dev/null
+++ b/src/components/organisms/PreviewConfigurationEditor/styled.tsx
@@ -0,0 +1,22 @@
+import styled from 'styled-components';
+
+import Colors from '@styles/Colors';
+
+export const Label = styled.p`
+ margin: 0;
+ font-size: 14px;
+ font-weight: 600;
+ color: ${Colors.grey9};
+ margin-bottom: 8px;
+`;
+
+export const Field = styled.div`
+ margin-top: 6px;
+ margin-bottom: 30px;
+`;
+
+export const Description = styled.p`
+ margin: 0;
+ font-size: 14px;
+ color: ${Colors.grey7};
+`;
From 0c3163ceaed4d44c2dad7e819135143afcfec180 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 11:48:56 +0200
Subject: [PATCH 2/9] feat: helm install/template options constants
---
src/constants/helmOptions.ts | 53 ++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 src/constants/helmOptions.ts
diff --git a/src/constants/helmOptions.ts b/src/constants/helmOptions.ts
new file mode 100644
index 0000000000..9b7e88ddf9
--- /dev/null
+++ b/src/constants/helmOptions.ts
@@ -0,0 +1,53 @@
+const helmCommonOptions = {
+ '--atomic': 'boolean',
+ '--ca-file': 'string',
+ '--create-namespace': 'boolean',
+ '--dependency-update': 'boolean',
+ '--description': 'string',
+ '--devel': 'boolean',
+ '--disable-openapi-validation': 'boolean',
+ '--dry-run': 'boolean',
+ '--generate-name': 'boolean',
+ '--help': 'boolean',
+ '--insecure-skip-tls-verify': 'boolean',
+ '--key-file': 'string',
+ '--keyring': 'string',
+ '--name-template': 'string',
+ '--no-hooks': 'boolean',
+ '--pass-credentials': 'boolean',
+ '--password': 'string',
+ '--post-renderer': 'string',
+ '--render-subchart-notes': 'boolean',
+ '--replace': 'boolean',
+ '--repo': 'string',
+ '--set': 'stringArray',
+ '--set-file': 'stringArray',
+ '--set-string': 'stringArray',
+ '--skip-crds': 'boolean',
+ '--timeout': 'duration',
+ '--username': 'string',
+ '--values': 'strings',
+ '--verify': 'boolean',
+ '--version': 'string',
+ '--wait': 'boolean',
+ '--wait-for-jobs': 'boolean',
+};
+
+export const helmInstallOptions = {
+ ...helmCommonOptions,
+ '--output': 'string', // ['table', 'json', 'yaml'],
+};
+
+export const helmTemplateOptions = {
+ ...helmCommonOptions,
+ '--api-versions': 'stringArray',
+ '--cert-file': 'string',
+ '--include-crds': 'boolean',
+ '--is-upgrade': 'boolean',
+ '--kube-version': 'string',
+ '--output-dir': 'string',
+ '--release-name': 'boolean',
+ '--show-only': 'stringArray',
+ '--skip-tests': 'boolean',
+ '--validate': 'boolean',
+};
From 5fe74cb947012d7ea48ac9e17b8b269c81c9d085 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 13:00:24 +0200
Subject: [PATCH 3/9] refactor: KeyValueInput to accept schema and different
value types
---
.../KeyValueInput/KeyValueEntryRenderer.tsx | 93 +++++++++++
.../atoms/KeyValueInput/KeyValueInput.tsx | 158 ++++++------------
.../atoms/KeyValueInput/constants.ts | 1 +
src/components/atoms/KeyValueInput/styled.tsx | 34 ++++
src/components/atoms/KeyValueInput/types.ts | 2 +
.../ResourceFilter/ResourceFilter.tsx | 13 +-
6 files changed, 193 insertions(+), 108 deletions(-)
create mode 100644 src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
create mode 100644 src/components/atoms/KeyValueInput/constants.ts
create mode 100644 src/components/atoms/KeyValueInput/styled.tsx
create mode 100644 src/components/atoms/KeyValueInput/types.ts
diff --git a/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx b/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
new file mode 100644
index 0000000000..83c44169ca
--- /dev/null
+++ b/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
@@ -0,0 +1,93 @@
+import React from 'react';
+
+import {Input, Select} from 'antd';
+
+import {MinusOutlined} from '@ant-design/icons';
+
+import Colors from '@styles/Colors';
+
+import {ANY_VALUE} from './constants';
+import {KeyValueEntry} from './types';
+
+import * as S from './styled';
+
+type KeyValueEntryRendererProps = {
+ entry: KeyValueEntry;
+ valueType?: string;
+ onKeyChange: (newKey: string) => void;
+ onValueChange: (newValue: string) => void;
+ onEntryRemove: (entryId: string) => void;
+ disabled?: boolean;
+ availableKeys: string[];
+ availableValues?: string[];
+};
+
+type ValueInputProps = {
+ value?: string;
+ valueType: string;
+ availableValues?: string[];
+ onChange: (newValue: string) => void;
+ disabled?: boolean;
+};
+
+const ValueInput: React.FC = props => {
+ const {value, valueType, availableValues, disabled, onChange} = props;
+
+ if (valueType === 'string') {
+ if (availableValues?.length) {
+ return (
+
+
+ {ANY_VALUE}
+
+ {availableValues?.map((valueOption: string) => (
+
+ {valueOption}
+
+ ))}
+
+ );
+ }
+ return onChange(e.target.value)} disabled={disabled} />;
+ }
+
+ // TODO: decide if we want to implement more value types
+ return null;
+};
+
+const KeyValueEntryRenderer: React.FC = props => {
+ const {entry, valueType, onKeyChange, onValueChange, onEntryRemove, disabled, availableKeys, availableValues} = props;
+
+ return (
+
+
+
+ {availableKeys.map(key => (
+
+ {key}
+
+ ))}
+
+
+ {entry.key && valueType && valueType !== 'boolean' && (
+
+ )}
+
+
+ onEntryRemove(entry.id)}
+ color={Colors.redError}
+ size="small"
+ icon={ }
+ />
+
+ );
+};
+
+export default KeyValueEntryRenderer;
diff --git a/src/components/atoms/KeyValueInput/KeyValueInput.tsx b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
index a6cc6d73f5..75f7d6ef70 100644
--- a/src/components/atoms/KeyValueInput/KeyValueInput.tsx
+++ b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
@@ -1,62 +1,30 @@
-import React, {useEffect, useState} from 'react';
+import React, {useCallback, useEffect, useState} from 'react';
-import {Button, Select} from 'antd';
+import {Button} from 'antd';
-import {MinusOutlined, PlusOutlined} from '@ant-design/icons';
+import {PlusOutlined} from '@ant-design/icons';
import isDeepEqual from 'fast-deep-equal/es6/react';
-import styled from 'styled-components';
import {v4 as uuidv4} from 'uuid';
-import Colors from '@styles/Colors';
-
-const Container = styled.div`
- max-height: 800px;
- overflow-y: auto;
-`;
-
-const TitleContainer = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-const TitleLabel = styled.span``;
-
-const KeyValueContainer = styled.div`
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- grid-gap: 8px;
- align-items: center;
- margin: 10px 0;
-`;
-
-const KeyValueRemoveButtonContainer = styled.div`
- display: grid;
- grid-template-columns: 1fr max-content;
- grid-gap: 8px;
- align-items: center;
-`;
-
-const StyledRemoveButton = styled(Button)`
- min-width: 24px;
-`;
-
-type KeyValueEntry = {id: string; key?: string; value?: string};
-type KeyValue = Record;
+import KeyValueEntryRenderer from './KeyValueEntryRenderer';
+import {ANY_VALUE} from './constants';
+import {KeyValueData, KeyValueEntry} from './types';
+
+import * as S from './styled';
type KeyValueInputProps = {
disabled?: boolean;
label: string;
labelStyle?: React.CSSProperties;
+ schema: Record;
data: Record;
- value: KeyValue;
- onChange: (keyValues: KeyValue) => void;
+ value: KeyValueData;
+ onChange: (keyValueData: KeyValueData) => void;
};
-export const ANY_VALUE = '';
-
-function makeKeyValueFromEntries(keyValueEntries: KeyValueEntry[]): KeyValue {
- const keyValue: KeyValue = {};
+function makeKeyValueDataFromEntries(keyValueEntries: KeyValueEntry[]): KeyValueData {
+ const keyValue: KeyValueData = {};
keyValueEntries.forEach(({key, value}) => {
if (!key || !value) {
return;
@@ -71,15 +39,15 @@ function makeKeyValueFromEntries(keyValueEntries: KeyValueEntry[]): KeyValue {
}
function KeyValueInput(props: KeyValueInputProps) {
- const {disabled = false, label, labelStyle, data, value: keyValue, onChange} = props;
+ const {disabled = false, label, labelStyle, data, value: parentKeyValueData, schema, onChange} = props;
const [entries, setEntries] = useState([]);
- const [currentKeyValue, setCurrentKeyValue] = useState(keyValue);
+ const [currentKeyValueData, setCurrentKeyValueData] = useState(parentKeyValueData);
useEffect(() => {
- if (!isDeepEqual(keyValue, currentKeyValue)) {
- setCurrentKeyValue(keyValue);
+ if (!isDeepEqual(parentKeyValueData, currentKeyValueData)) {
+ setCurrentKeyValueData(parentKeyValueData);
const newEntries: KeyValueEntry[] = [];
- Object.entries(keyValue).forEach(([key, value]) => {
+ Object.entries(parentKeyValueData).forEach(([key, value]) => {
if (newEntries.some(e => e.key === key)) {
return;
}
@@ -100,12 +68,12 @@ function KeyValueInput(props: KeyValueInputProps) {
});
setEntries(newEntries);
}
- }, [keyValue, currentKeyValue, data]);
+ }, [parentKeyValueData, currentKeyValueData, data]); // do we need "data" as dep?
const updateKeyValue = (newEntries: KeyValueEntry[]) => {
- const newKeyValue = makeKeyValueFromEntries(newEntries);
- setCurrentKeyValue(newKeyValue);
- onChange(newKeyValue);
+ const newKeyValueData = makeKeyValueDataFromEntries(newEntries);
+ setCurrentKeyValueData(newKeyValueData);
+ onChange(newKeyValueData);
};
const createEntry = () => {
@@ -145,62 +113,44 @@ function KeyValueInput(props: KeyValueInputProps) {
updateKeyValue(newEntries);
};
+ const getEntryAvailableKeys = useCallback(
+ (entry: KeyValueEntry) => {
+ return Object.keys(schema).filter(key => key === entry.key || !entries.some(e => e.key === key));
+ },
+ [schema, entries]
+ );
+
+ const getEntryAvailableValues = useCallback(
+ (entry: KeyValueEntry) => {
+ if (entry.key && data[entry.key]) {
+ return data[entry.key];
+ }
+ return undefined;
+ },
+ [data]
+ );
+
return (
-
-
- {label}
+
+
+ {label}
} disabled={disabled}>
Add
-
+
{entries.map(entry => (
-
-
- updateEntryKey(entry.id, newKey)}
- showSearch
- disabled={disabled}
- >
- {Object.keys(data)
- .filter(key => key === entry.key || !entries.some(e => e.key === key))
- .map(key => (
-
- {key}
-
- ))}
-
-
- {entry.key && (
- updateEntryValue(entry.id, newValue)}
- showSearch
- disabled={disabled}
- >
-
- {ANY_VALUE}
-
- {data[entry.key] &&
- data[entry.key].map((value: string) => (
-
- {value}
-
- ))}
-
- )}
-
-
- removeEntry(entry.id)}
- color={Colors.redError}
- size="small"
- icon={ }
- />
-
+ updateEntryKey(entry.id, newKey)}
+ onValueChange={newValue => updateEntryValue(entry.id, newValue)}
+ onEntryRemove={removeEntry}
+ availableKeys={getEntryAvailableKeys(entry)}
+ availableValues={getEntryAvailableValues(entry)}
+ />
))}
-
+
);
}
diff --git a/src/components/atoms/KeyValueInput/constants.ts b/src/components/atoms/KeyValueInput/constants.ts
new file mode 100644
index 0000000000..e6edb94c08
--- /dev/null
+++ b/src/components/atoms/KeyValueInput/constants.ts
@@ -0,0 +1 @@
+export const ANY_VALUE = '';
diff --git a/src/components/atoms/KeyValueInput/styled.tsx b/src/components/atoms/KeyValueInput/styled.tsx
new file mode 100644
index 0000000000..df52b620e8
--- /dev/null
+++ b/src/components/atoms/KeyValueInput/styled.tsx
@@ -0,0 +1,34 @@
+import {Button} from 'antd';
+
+import styled from 'styled-components';
+
+export const Container = styled.div`
+ max-height: 800px;
+ overflow-y: auto;
+`;
+
+export const TitleContainer = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`;
+export const TitleLabel = styled.span``;
+
+export const KeyValueContainer = styled.div`
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ grid-gap: 8px;
+ align-items: center;
+ margin: 10px 0;
+`;
+
+export const KeyValueRemoveButtonContainer = styled.div`
+ display: grid;
+ grid-template-columns: 1fr max-content;
+ grid-gap: 8px;
+ align-items: center;
+`;
+
+export const StyledRemoveButton = styled(Button)`
+ min-width: 24px;
+`;
diff --git a/src/components/atoms/KeyValueInput/types.ts b/src/components/atoms/KeyValueInput/types.ts
new file mode 100644
index 0000000000..77de45f4f5
--- /dev/null
+++ b/src/components/atoms/KeyValueInput/types.ts
@@ -0,0 +1,2 @@
+export type KeyValueEntry = {id: string; key?: string; value?: string};
+export type KeyValueData = Record;
diff --git a/src/components/molecules/ResourceFilter/ResourceFilter.tsx b/src/components/molecules/ResourceFilter/ResourceFilter.tsx
index 224d12f5f9..9c45369491 100644
--- a/src/components/molecules/ResourceFilter/ResourceFilter.tsx
+++ b/src/components/molecules/ResourceFilter/ResourceFilter.tsx
@@ -3,6 +3,7 @@ import {useDebounce} from 'react-use';
import {Button, Input, Select} from 'antd';
+import {mapValues} from 'lodash';
import styled from 'styled-components';
import {DEFAULT_EDITOR_DEBOUNCE} from '@constants/constants';
@@ -104,13 +105,15 @@ const ResourceFilter = () => {
].sort();
}, [knownResourceKinds, resourceMap]);
- const allLabels = useMemo>(() => {
+ const allLabelsData = useMemo>(() => {
return makeKeyValuesFromObjectList(Object.values(resourceMap), resource => resource.content?.metadata?.labels);
}, [resourceMap]);
+ const allLabelsSchema = useMemo(() => mapValues(allLabelsData, () => 'string'), [allLabelsData]);
- const allAnnotations = useMemo>(() => {
+ const allAnnotationsData = useMemo>(() => {
return makeKeyValuesFromObjectList(Object.values(resourceMap), resource => resource.content?.metadata?.annotations);
}, [resourceMap]);
+ const allAnnotationsSchema = useMemo(() => mapValues(allAnnotationsData, () => 'string'), [allAnnotationsData]);
const fileOrFolderContainedInOptions = useMemo(() => {
return Object.keys(fileMap).map(option => (
@@ -275,7 +278,8 @@ const ResourceFilter = () => {
{
From fa65358f0780f3bb989195b1e8c96652f47d5ce7 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 13:23:35 +0200
Subject: [PATCH 4/9] refactor: KeyValueInput improvements
---
.../KeyValueInput/KeyValueEntryRenderer.tsx | 37 +-----------------
.../atoms/KeyValueInput/KeyValueInput.tsx | 9 ++++-
.../atoms/KeyValueInput/ValueInput.tsx | 39 +++++++++++++++++++
3 files changed, 48 insertions(+), 37 deletions(-)
create mode 100644 src/components/atoms/KeyValueInput/ValueInput.tsx
diff --git a/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx b/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
index 83c44169ca..f81466386c 100644
--- a/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
+++ b/src/components/atoms/KeyValueInput/KeyValueEntryRenderer.tsx
@@ -1,12 +1,12 @@
import React from 'react';
-import {Input, Select} from 'antd';
+import {Select} from 'antd';
import {MinusOutlined} from '@ant-design/icons';
import Colors from '@styles/Colors';
-import {ANY_VALUE} from './constants';
+import ValueInput from './ValueInput';
import {KeyValueEntry} from './types';
import * as S from './styled';
@@ -22,39 +22,6 @@ type KeyValueEntryRendererProps = {
availableValues?: string[];
};
-type ValueInputProps = {
- value?: string;
- valueType: string;
- availableValues?: string[];
- onChange: (newValue: string) => void;
- disabled?: boolean;
-};
-
-const ValueInput: React.FC = props => {
- const {value, valueType, availableValues, disabled, onChange} = props;
-
- if (valueType === 'string') {
- if (availableValues?.length) {
- return (
-
-
- {ANY_VALUE}
-
- {availableValues?.map((valueOption: string) => (
-
- {valueOption}
-
- ))}
-
- );
- }
- return onChange(e.target.value)} disabled={disabled} />;
- }
-
- // TODO: decide if we want to implement more value types
- return null;
-};
-
const KeyValueEntryRenderer: React.FC = props => {
const {entry, valueType, onKeyChange, onValueChange, onEntryRemove, disabled, availableKeys, availableValues} = props;
diff --git a/src/components/atoms/KeyValueInput/KeyValueInput.tsx b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
index 75f7d6ef70..d94e7e5343 100644
--- a/src/components/atoms/KeyValueInput/KeyValueInput.tsx
+++ b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
@@ -52,11 +52,13 @@ function KeyValueInput(props: KeyValueInputProps) {
return;
}
+ const availableValues: string[] | undefined = data[key];
+
if (value === null) {
newEntries.push({
id: uuidv4(),
key,
- value: ANY_VALUE,
+ value: availableValues?.length ? ANY_VALUE : undefined,
});
} else {
newEntries.push({
@@ -93,10 +95,13 @@ function KeyValueInput(props: KeyValueInputProps) {
const updateEntryKey = (entryId: string, key: string) => {
const newEntries = Array.from(entries);
const entryIndex = newEntries.findIndex(e => e.id === entryId);
+
+ const availableValues: string[] | undefined = data[key];
+
newEntries[entryIndex] = {
id: entryId,
key,
- value: ANY_VALUE,
+ value: availableValues?.length ? ANY_VALUE : undefined,
};
setEntries(newEntries);
updateKeyValue(newEntries);
diff --git a/src/components/atoms/KeyValueInput/ValueInput.tsx b/src/components/atoms/KeyValueInput/ValueInput.tsx
new file mode 100644
index 0000000000..62982b84f7
--- /dev/null
+++ b/src/components/atoms/KeyValueInput/ValueInput.tsx
@@ -0,0 +1,39 @@
+import {Input, Select} from 'antd';
+
+import {ANY_VALUE} from './constants';
+
+type ValueInputProps = {
+ value?: string;
+ valueType: string;
+ availableValues?: string[];
+ onChange: (newValue: string) => void;
+ disabled?: boolean;
+};
+
+const ValueInput: React.FC = props => {
+ const {value, valueType, availableValues, disabled, onChange} = props;
+
+ // TODO: decide if we need a custom input for the stringArray value type
+ if (valueType === 'string' || valueType === 'stringArray') {
+ if (availableValues?.length) {
+ return (
+
+
+ {ANY_VALUE}
+
+ {availableValues?.map((valueOption: string) => (
+
+ {valueOption}
+
+ ))}
+
+ );
+ }
+ return onChange(e.target.value)} disabled={disabled} />;
+ }
+
+ // TODO: decide if we want to implement more value types
+ return null;
+};
+
+export default ValueInput;
From fe427a91b74c7435bb6cddf3ff2a4e64f4f1fea5 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 13:25:10 +0200
Subject: [PATCH 5/9] feat: add KeyValueInput for preview conf opts
---
.../PreviewConfigurationEditor.tsx | 34 +++++++++++++++++--
src/constants/helmOptions.ts | 2 +-
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
index 82ccdd23ff..5a5451b657 100644
--- a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
+++ b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
@@ -1,8 +1,25 @@
-import {Input} from 'antd';
+import {useState} from 'react';
+
+import {Input, Select} from 'antd';
+
+import {helmInstallOptions, helmTemplateOptions} from '@constants/helmOptions';
+
+import {useAppSelector} from '@redux/hooks';
+
+import {KeyValueInput} from '@components/atoms';
import * as S from './styled';
const PreviewConfigurationEditor = () => {
+ const helmPreviewMode = useAppSelector(
+ state => state.config.projectConfig?.settings?.helmPreviewMode || state.config.settings.helmPreviewMode
+ );
+
+ const [helmOptions, setHelmOptions] = useState({});
+ const [helmCommand, setHelmCommand] = useState<'template' | 'install'>(helmPreviewMode || 'template');
+
+ const keyValueInputSchema = helmCommand === 'template' ? helmTemplateOptions : helmInstallOptions;
+
return (
@@ -14,7 +31,20 @@ const PreviewConfigurationEditor = () => {
Drag and drop to specify order
- Specify options:
+ Select which helm command to use for this Preview:
+
+ Template
+ Install
+
+
+
+
);
diff --git a/src/constants/helmOptions.ts b/src/constants/helmOptions.ts
index 9b7e88ddf9..cd8698d875 100644
--- a/src/constants/helmOptions.ts
+++ b/src/constants/helmOptions.ts
@@ -26,7 +26,7 @@ const helmCommonOptions = {
'--skip-crds': 'boolean',
'--timeout': 'duration',
'--username': 'string',
- '--values': 'strings',
+ '--values': 'string',
'--verify': 'boolean',
'--version': 'string',
'--wait': 'boolean',
From 4958395edf9d217981cfd089fba278aa33c05e38 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 15:43:28 +0200
Subject: [PATCH 6/9] feat: OrderedList atom component
---
src/App.tsx | 10 +--
.../atoms/OrderedList/OrderedList.tsx | 66 +++++++++++++++++++
src/components/atoms/OrderedList/index.ts | 2 +
src/components/atoms/OrderedList/styled.tsx | 12 ++++
src/components/atoms/index.ts | 1 +
.../PreviewConfigurationEditor.tsx | 28 +++++++-
src/models/appstate.ts | 4 ++
src/models/ui.ts | 1 -
.../PreviewConfigurationQuickAction.tsx | 4 +-
src/redux/initialState.ts | 4 +-
src/redux/reducers/main.ts | 15 +++++
src/redux/reducers/ui.ts | 8 ---
src/utils/array.ts | 23 +++++++
13 files changed, 158 insertions(+), 20 deletions(-)
create mode 100644 src/components/atoms/OrderedList/OrderedList.tsx
create mode 100644 src/components/atoms/OrderedList/index.ts
create mode 100644 src/components/atoms/OrderedList/styled.tsx
create mode 100644 src/utils/array.ts
diff --git a/src/App.tsx b/src/App.tsx
index 40cf71f7b7..f21e79c3ab 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -20,12 +20,8 @@ import {useAppSelector} from '@redux/hooks';
import {setAlert} from '@redux/reducers/alert';
import {setCreateProject, setLoadingProject, setOpenProject} from '@redux/reducers/appConfig';
import {closePluginsDrawer} from '@redux/reducers/extension';
-import {
- closeFolderExplorer,
- closePreviewConfigurationEditor,
- toggleNotifications,
- toggleSettings,
-} from '@redux/reducers/ui';
+import {closePreviewConfigurationEditor} from '@redux/reducers/main';
+import {closeFolderExplorer, toggleNotifications, toggleSettings} from '@redux/reducers/ui';
import {isInClusterModeSelector, kubeConfigContextSelector, kubeConfigPathSelector} from '@redux/selectors';
import {loadContexts} from '@redux/thunks/loadKubeConfig';
@@ -78,7 +74,7 @@ const App = () => {
const dispatch = useDispatch();
const isChangeFiltersConfirmModalVisible = useAppSelector(state => state.main.filtersToBeChanged);
const isClusterDiffModalVisible = useAppSelector(state => state.ui.isClusterDiffVisible);
- const isPreviewConfigurationEditorOpen = useAppSelector(state => state.ui.isPreviewConfigurationEditorOpen);
+ const isPreviewConfigurationEditorOpen = useAppSelector(state => state.main.prevConfEditor.isOpen);
const isClusterSelectorVisible = useAppSelector(state => state.config.isClusterSelectorVisible);
const isCreateFolderModalVisible = useAppSelector(state => state.ui.createFolderModal.isOpen);
const isCreateProjectModalVisible = useAppSelector(state => state.ui.createProjectModal.isOpen);
diff --git a/src/components/atoms/OrderedList/OrderedList.tsx b/src/components/atoms/OrderedList/OrderedList.tsx
new file mode 100644
index 0000000000..db06271ac8
--- /dev/null
+++ b/src/components/atoms/OrderedList/OrderedList.tsx
@@ -0,0 +1,66 @@
+import {useCallback} from 'react';
+
+import {Checkbox} from 'antd';
+
+import {ArrowDownOutlined, ArrowUpOutlined} from '@ant-design/icons';
+
+import {arrayMove} from '@utils/array';
+
+import * as S from './styled';
+
+export type OrderedListItem = {
+ id: string;
+ text: string;
+ isChecked: boolean;
+};
+
+type OrderedListProps = {
+ items: OrderedListItem[];
+ onChange: (items: OrderedListItem[]) => void;
+};
+
+const OrderedList: React.FC = props => {
+ const {items, onChange} = props;
+
+ const checkItem = useCallback(
+ (itemId: string) => {
+ onChange(items.slice().map(item => (item.id === itemId ? {...item, isChecked: !item.isChecked} : item)));
+ },
+ [items, onChange]
+ );
+
+ const moveItem = useCallback(
+ (itemId: string, direction: 'up' | 'down') => {
+ const coefficient = direction === 'up' ? -1 : 1;
+ const itemIndex = items.findIndex(i => i.id === itemId);
+ if (!itemIndex) {
+ return;
+ }
+ onChange(arrayMove(items, itemIndex, itemIndex + coefficient));
+ },
+ [items, onChange]
+ );
+
+ return (
+
+ {items.map((item, index) => (
+ // eslint-disable-next-line react/no-array-index-key
+
+
+ {index + 1}.
+ checkItem(item.id)}>
+
+ {item.text}
+
+
+
+ moveItem(item.id, 'up')} />
+ moveItem(item.id, 'down')} />
+
+
+ ))}
+
+ );
+};
+
+export default OrderedList;
diff --git a/src/components/atoms/OrderedList/index.ts b/src/components/atoms/OrderedList/index.ts
new file mode 100644
index 0000000000..79b097ae5c
--- /dev/null
+++ b/src/components/atoms/OrderedList/index.ts
@@ -0,0 +1,2 @@
+export {default} from './OrderedList';
+export type {OrderedListItem} from './OrderedList';
diff --git a/src/components/atoms/OrderedList/styled.tsx b/src/components/atoms/OrderedList/styled.tsx
new file mode 100644
index 0000000000..0c3c0dae0c
--- /dev/null
+++ b/src/components/atoms/OrderedList/styled.tsx
@@ -0,0 +1,12 @@
+import styled from 'styled-components';
+
+export const List = styled.ol`
+ padding: 0;
+ margin-top: 8px;
+`;
+
+export const ListItem = styled.li`
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+`;
diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts
index a342a545d7..2eff6925a9 100644
--- a/src/components/atoms/index.ts
+++ b/src/components/atoms/index.ts
@@ -15,3 +15,4 @@ export {default as Dots} from './Dots';
export {default as Spinner} from './Spinner';
export {default as Icon} from './Icon';
export {default as TabHeader} from './TabHeader';
+export {default as OrderedList} from './OrderedList';
diff --git a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
index 5a5451b657..7e7725373f 100644
--- a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
+++ b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
@@ -4,9 +4,12 @@ import {Input, Select} from 'antd';
import {helmInstallOptions, helmTemplateOptions} from '@constants/helmOptions';
+import {HelmValuesFile} from '@models/helm';
+
import {useAppSelector} from '@redux/hooks';
-import {KeyValueInput} from '@components/atoms';
+import {KeyValueInput, OrderedList} from '@components/atoms';
+import {OrderedListItem} from '@components/atoms/OrderedList';
import * as S from './styled';
@@ -15,11 +18,33 @@ const PreviewConfigurationEditor = () => {
state => state.config.projectConfig?.settings?.helmPreviewMode || state.config.settings.helmPreviewMode
);
+ const helmChart = useAppSelector(state => {
+ const helmChartId = state.main.prevConfEditor.helmChartId;
+ if (!helmChartId) {
+ return undefined;
+ }
+ return state.main.helmChartMap[helmChartId];
+ });
+
+ const valuesFiles = useAppSelector(
+ state =>
+ helmChart?.valueFileIds
+ .map(id => state.main.helmValuesMap[id])
+ .filter((v): v is HelmValuesFile => v !== undefined) || []
+ );
+
+ const [valuesFileItems, setValuesFileItems] = useState(
+ valuesFiles.map(vf => ({id: vf.id, text: vf.name, isChecked: false}))
+ );
const [helmOptions, setHelmOptions] = useState({});
const [helmCommand, setHelmCommand] = useState<'template' | 'install'>(helmPreviewMode || 'template');
const keyValueInputSchema = helmCommand === 'template' ? helmTemplateOptions : helmInstallOptions;
+ if (!helmChart) {
+ return Something went wrong, could not find the helm chart.
;
+ }
+
return (
@@ -29,6 +54,7 @@ const PreviewConfigurationEditor = () => {
Select which values files to use:
Drag and drop to specify order
+
Select which helm command to use for this Preview:
diff --git a/src/models/appstate.ts b/src/models/appstate.ts
index 7fc4a4164c..a2f55b9de0 100644
--- a/src/models/appstate.ts
+++ b/src/models/appstate.ts
@@ -146,6 +146,10 @@ interface AppState {
/** type/value of filters that will be changed */
filtersToBeChanged?: ResourceFilterType;
registeredKindHandlers: string[];
+ prevConfEditor: {
+ isOpen: boolean;
+ helmChartId?: string;
+ };
}
export type {
diff --git a/src/models/ui.ts b/src/models/ui.ts
index c7ff8104e0..10a77df9de 100644
--- a/src/models/ui.ts
+++ b/src/models/ui.ts
@@ -60,7 +60,6 @@ export type UiState = {
isSettingsOpen: boolean;
isClusterDiffVisible: boolean;
isNotificationsOpen: boolean;
- isPreviewConfigurationEditorOpen: boolean;
newResourceWizard: {
isOpen: boolean;
defaultInput?: NewResourceWizardInput;
diff --git a/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx b/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
index 5d9e8459f4..0b80169927 100644
--- a/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
+++ b/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
@@ -9,7 +9,7 @@ import styled from 'styled-components';
import {SectionCustomComponentProps} from '@models/navigator';
import {useAppDispatch, useAppSelector} from '@redux/hooks';
-import {openPreviewConfigurationEditor} from '@redux/reducers/ui';
+import {openPreviewConfigurationEditor} from '@redux/reducers/main';
import Colors from '@styles/Colors';
@@ -35,7 +35,7 @@ const PreviewConfigurationNameSuffix: React.FC = pr
const dispatch = useAppDispatch();
const onClick = () => {
- dispatch(openPreviewConfigurationEditor());
+ dispatch(openPreviewConfigurationEditor(sectionInstance.id.replace('-configurations', '')));
};
return (
diff --git a/src/redux/initialState.ts b/src/redux/initialState.ts
index d5d92731cf..8e8c1870f6 100644
--- a/src/redux/initialState.ts
+++ b/src/redux/initialState.ts
@@ -46,6 +46,9 @@ const initialAppState: AppState = {
shouldEditorReloadSelectedPath: false,
checkedResourceIds: [],
registeredKindHandlers: [],
+ prevConfEditor: {
+ isOpen: false,
+ },
};
const initialAppConfigState: AppConfig = {
@@ -101,7 +104,6 @@ const initialUiState: UiState = {
isClusterDiffVisible: false,
isNotificationsOpen: false,
isFolderLoading: false,
- isPreviewConfigurationEditorOpen: false,
quickSearchActionsPopup: {
isOpen: false,
},
diff --git a/src/redux/reducers/main.ts b/src/redux/reducers/main.ts
index fa7fc370e9..f97ea37718 100644
--- a/src/redux/reducers/main.ts
+++ b/src/redux/reducers/main.ts
@@ -696,6 +696,19 @@ export const mainSlice = createSlice({
notification.hasSeen = true;
});
},
+ openPreviewConfigurationEditor: (state: Draft, action: PayloadAction) => {
+ const helmChartId = action.payload;
+ state.prevConfEditor = {
+ helmChartId,
+ isOpen: true,
+ };
+ },
+ closePreviewConfigurationEditor: (state: Draft) => {
+ state.prevConfEditor = {
+ isOpen: false,
+ helmChartId: undefined,
+ };
+ },
},
extraReducers: builder => {
builder.addCase(setAlert, (state, action) => {
@@ -1141,5 +1154,7 @@ export const {
addMultipleKindHandlers,
addKindHandler,
seenNotifications,
+ openPreviewConfigurationEditor,
+ closePreviewConfigurationEditor,
} = mainSlice.actions;
export default mainSlice.reducer;
diff --git a/src/redux/reducers/ui.ts b/src/redux/reducers/ui.ts
index 9285a4dbc7..c43d94bbb6 100644
--- a/src/redux/reducers/ui.ts
+++ b/src/redux/reducers/ui.ts
@@ -223,12 +223,6 @@ export const uiSlice = createSlice({
state.highlightedItems.browseTemplates = action.payload === HighlightItems.BROWSE_TEMPLATES;
state.highlightedItems.connectToCluster = action.payload === HighlightItems.CONNECT_TO_CLUSTER;
},
- openPreviewConfigurationEditor: (state: Draft) => {
- state.isPreviewConfigurationEditorOpen = true;
- },
- closePreviewConfigurationEditor: (state: Draft) => {
- state.isPreviewConfigurationEditorOpen = false;
- },
},
extraReducers: builder => {
builder
@@ -294,7 +288,5 @@ export const {
closeSaveResourcesToFileFolderModal,
zoomIn,
zoomOut,
- openPreviewConfigurationEditor,
- closePreviewConfigurationEditor,
} = uiSlice.actions;
export default uiSlice.reducer;
diff --git a/src/utils/array.ts b/src/utils/array.ts
new file mode 100644
index 0000000000..db6bb40273
--- /dev/null
+++ b/src/utils/array.ts
@@ -0,0 +1,23 @@
+/**
+ * Mutates an array by moving an element from an index to another
+ * @param array
+ * @param fromIndex
+ * @param toIndex
+ */
+export function arrayMoveMutate(array: T[], fromIndex: number, toIndex: number) {
+ const startIndex = fromIndex < 0 ? array.length + fromIndex : fromIndex;
+ if (startIndex >= 0 && startIndex < array.length) {
+ const endIndex = toIndex < 0 ? array.length + toIndex : toIndex;
+ const [element] = array.splice(fromIndex, 1);
+ array.splice(endIndex, 0, element);
+ }
+}
+
+/**
+ * Returns a copy of the array with an element moved from an index to another
+ */
+export function arrayMove(array: T[], fromIndex: number, toIndex: number) {
+ const arrayCopy = array.slice();
+ arrayMoveMutate(arrayCopy, fromIndex, toIndex);
+ return arrayCopy;
+}
From c814d1cfb49d97b807b83f113deaed28545e1155 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Fri, 18 Feb 2022 15:47:23 +0200
Subject: [PATCH 7/9] feat: add prev conf action buttons
---
.../PreviewConfigurationEditor.tsx | 11 ++++++++++-
.../organisms/PreviewConfigurationEditor/styled.tsx | 7 +++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
index 7e7725373f..3fd0d3262d 100644
--- a/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
+++ b/src/components/organisms/PreviewConfigurationEditor/PreviewConfigurationEditor.tsx
@@ -1,6 +1,6 @@
import {useState} from 'react';
-import {Input, Select} from 'antd';
+import {Button, Input, Select} from 'antd';
import {helmInstallOptions, helmTemplateOptions} from '@constants/helmOptions';
@@ -72,6 +72,15 @@ const PreviewConfigurationEditor = () => {
onChange={setHelmOptions}
/>
+
+
+ Discard
+
+
+ Save
+
+ Save and Preview
+
);
};
diff --git a/src/components/organisms/PreviewConfigurationEditor/styled.tsx b/src/components/organisms/PreviewConfigurationEditor/styled.tsx
index 68a7db3edb..6b30367f77 100644
--- a/src/components/organisms/PreviewConfigurationEditor/styled.tsx
+++ b/src/components/organisms/PreviewConfigurationEditor/styled.tsx
@@ -20,3 +20,10 @@ export const Description = styled.p`
font-size: 14px;
color: ${Colors.grey7};
`;
+
+export const ActionsContainer = styled.div`
+ margin-top: 12px;
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+`;
From 7fbfcce52ba70a8d24eced785cdfe08b4f133f35 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Sat, 19 Feb 2022 20:19:58 +0200
Subject: [PATCH 8/9] chore: add new prev conf tooltip constant
---
src/constants/tooltips.ts | 1 +
.../PreviewConfigurationQuickAction.tsx | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/constants/tooltips.ts b/src/constants/tooltips.ts
index 8b772b4635..f4ee38df3c 100644
--- a/src/constants/tooltips.ts
+++ b/src/constants/tooltips.ts
@@ -56,3 +56,4 @@ export const SearchProjectTooltip = 'Search for project by name or path';
export const PluginDrawerTooltip = 'Open Plugins Manager';
export const QuickFilterTooltip = `Filter results – Hint: quick-filter using ${KEY_CTRL_CMD} + P`;
export const NewResourceTooltip = `Create new resource (${KEY_CTRL_CMD} + N)`;
+export const NewPreviewConfigurationTooltip = 'Create a new Preview Configuration';
diff --git a/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx b/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
index 0b80169927..3e521b98e0 100644
--- a/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
+++ b/src/navsections/HelmChartSectionBlueprint/PreviewConfigurationQuickAction.tsx
@@ -6,6 +6,8 @@ import {PlusOutlined} from '@ant-design/icons';
import styled from 'styled-components';
+import {NewPreviewConfigurationTooltip} from '@constants/tooltips';
+
import {SectionCustomComponentProps} from '@models/navigator';
import {useAppDispatch, useAppSelector} from '@redux/hooks';
@@ -41,7 +43,7 @@ const PreviewConfigurationNameSuffix: React.FC = pr
return (
-
+
}
type="link"
From a31cfabe353db96ea53457ef75ac7dbe4d7956c4 Mon Sep 17 00:00:00 2001
From: Catalin <20538711+devcatalin@users.noreply.github.com>
Date: Sat, 19 Feb 2022 20:30:43 +0200
Subject: [PATCH 9/9] feat: add docs url to prev conf options
---
.../atoms/KeyValueInput/KeyValueInput.tsx | 10 +++++++++-
.../PreviewConfigurationEditor.tsx | 14 ++++++++++++--
src/constants/constants.ts | 2 ++
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/src/components/atoms/KeyValueInput/KeyValueInput.tsx b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
index d94e7e5343..d22efba80d 100644
--- a/src/components/atoms/KeyValueInput/KeyValueInput.tsx
+++ b/src/components/atoms/KeyValueInput/KeyValueInput.tsx
@@ -7,6 +7,8 @@ import {PlusOutlined} from '@ant-design/icons';
import isDeepEqual from 'fast-deep-equal/es6/react';
import {v4 as uuidv4} from 'uuid';
+import {openUrlInExternalBrowser} from '@utils/shell';
+
import KeyValueEntryRenderer from './KeyValueEntryRenderer';
import {ANY_VALUE} from './constants';
import {KeyValueData, KeyValueEntry} from './types';
@@ -20,6 +22,7 @@ type KeyValueInputProps = {
schema: Record;
data: Record;
value: KeyValueData;
+ docsUrl?: string;
onChange: (keyValueData: KeyValueData) => void;
};
@@ -39,7 +42,7 @@ function makeKeyValueDataFromEntries(keyValueEntries: KeyValueEntry[]): KeyValue
}
function KeyValueInput(props: KeyValueInputProps) {
- const {disabled = false, label, labelStyle, data, value: parentKeyValueData, schema, onChange} = props;
+ const {disabled = false, label, labelStyle, data, value: parentKeyValueData, schema, docsUrl, onChange} = props;
const [entries, setEntries] = useState([]);
const [currentKeyValueData, setCurrentKeyValueData] = useState(parentKeyValueData);
@@ -143,6 +146,11 @@ function KeyValueInput(props: KeyValueInputProps) {
Add
+ {docsUrl && (
+ openUrlInExternalBrowser(docsUrl)} style={{padding: 0}}>
+ Documentation
+
+ )}
{entries.map(entry => (
{
const [helmOptions, setHelmOptions] = useState({});
const [helmCommand, setHelmCommand] = useState<'template' | 'install'>(helmPreviewMode || 'template');
- const keyValueInputSchema = helmCommand === 'template' ? helmTemplateOptions : helmInstallOptions;
+ const keyValueInputSchema = useMemo(
+ () => (helmCommand === 'template' ? helmTemplateOptions : helmInstallOptions),
+ [helmCommand]
+ );
+
+ const helmOptionsDocsUrl = useMemo(
+ () => (helmCommand === 'template' ? HELM_TEMPLATE_OPTIONS_DOCS_URL : HELM_INSTALL_OPTIONS_DOCS_URL),
+ [helmCommand]
+ );
if (!helmChart) {
return Something went wrong, could not find the helm chart.
;
@@ -69,6 +78,7 @@ const PreviewConfigurationEditor = () => {
value={helmOptions}
schema={keyValueInputSchema}
data={{}}
+ docsUrl={helmOptionsDocsUrl}
onChange={setHelmOptions}
/>
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index 48b78b77e0..b7295ffd84 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -30,3 +30,5 @@ export const DEFAULT_PLUGINS = [
export const PLUGIN_DOCS_URL = 'https://kubeshop.github.io/monokle/plugins/';
export const HELM_CHART_ENTRY_FILE = 'Chart.yaml';
export const HELM_CHART_SECTION_NAME = 'Helm Charts';
+export const HELM_TEMPLATE_OPTIONS_DOCS_URL = 'https://helm.sh/docs/helm/helm_template/#options';
+export const HELM_INSTALL_OPTIONS_DOCS_URL = 'https://helm.sh/docs/helm/helm_install/#options';