From a18e7a20547c9d63fb545019f49b64461fe7f4d9 Mon Sep 17 00:00:00 2001 From: Alessandro Menduni Date: Tue, 7 Mar 2023 11:38:16 +0100 Subject: [PATCH 1/3] Add CreateButtonDropdown --- frontend/locales/en/strings.json | 1 + .../CreateButtonDropdown.tsx | 63 +++++++++++++++++++ .../__tests__/CreateButtonDropdown.test.tsx | 51 +++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx create mode 100644 frontend/src/old-pages/Clusters/CreateButtonDropdown/__tests__/CreateButtonDropdown.test.tsx diff --git a/frontend/locales/en/strings.json b/frontend/locales/en/strings.json index 3f519ee8..b25eb62e 100644 --- a/frontend/locales/en/strings.json +++ b/frontend/locales/en/strings.json @@ -229,6 +229,7 @@ "actions": { "cancel": "Cancel", "create": "Create cluster", + "createFromWizard": "Use interface", "edit": "Edit", "start": "Start fleet", "stop": "Stop fleet", diff --git a/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx new file mode 100644 index 00000000..f91df59a --- /dev/null +++ b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx @@ -0,0 +1,63 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance +// with the License. A copy of the License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +// OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and +// limitations under the License. + +import { + ButtonDropdown, + ButtonDropdownProps, +} from '@cloudscape-design/components' +import {CancelableEventHandler} from '@cloudscape-design/components/internal/events' +import React, {useMemo} from 'react' +import {useTranslation} from 'react-i18next' +import {NavigateFunction, useNavigate} from 'react-router-dom' + +interface Props { + openWizard: (navigate: NavigateFunction) => void +} + +export const CreateButtonDropdown: React.FC = ({openWizard}) => { + const {t} = useTranslation() + + const navigate = useNavigate() + + const onCreateClick: CancelableEventHandler = + React.useCallback( + ({detail}) => { + switch (detail.id) { + case 'wizard': + openWizard(navigate) + return + } + }, + [navigate, openWizard], + ) + + const createDropdownItems: ButtonDropdownProps.Item[] = useMemo( + () => [ + { + id: 'wizard', + text: t('cluster.list.actions.createFromWizard'), + }, + ], + [t], + ) + + return ( + <> + + {t('cluster.list.actions.create')} + + + ) +} diff --git a/frontend/src/old-pages/Clusters/CreateButtonDropdown/__tests__/CreateButtonDropdown.test.tsx b/frontend/src/old-pages/Clusters/CreateButtonDropdown/__tests__/CreateButtonDropdown.test.tsx new file mode 100644 index 00000000..a5be7fff --- /dev/null +++ b/frontend/src/old-pages/Clusters/CreateButtonDropdown/__tests__/CreateButtonDropdown.test.tsx @@ -0,0 +1,51 @@ +import wrapper from '@cloudscape-design/components/test-utils/dom' +import {Store} from '@reduxjs/toolkit' +import {render, RenderResult} from '@testing-library/react' +import i18n from 'i18next' +import {mock} from 'jest-mock-extended' +import {I18nextProvider, initReactI18next} from 'react-i18next' +import {Provider} from 'react-redux' +import {BrowserRouter} from 'react-router-dom' +import {CreateButtonDropdown} from '../CreateButtonDropdown' + +i18n.use(initReactI18next).init({ + resources: {}, + lng: 'en', +}) + +const mockStore = mock() + +const MockProviders = (props: any) => ( + + + {props.children} + + +) + +describe('given a dropdown button to create a cluster', () => { + let screen: RenderResult + let mockOpenWizard: jest.Mock + + beforeEach(() => { + mockOpenWizard = jest.fn() + + screen = render( + + + , + ) + }) + + describe('when user selects the option to create a cluster using the wizard', () => { + beforeEach(() => { + const buttonDropdown = wrapper(screen.container).findButtonDropdown()! + buttonDropdown.openDropdown() + buttonDropdown.findItemById('wizard')?.click() + }) + + it('should open the cluster creation wizard', () => { + expect(mockOpenWizard).toHaveBeenCalledTimes(1) + }) + }) +}) From 0b97aa8c1fc481cc9ecafe6fbe75402878eb47f1 Mon Sep 17 00:00:00 2001 From: Alessandro Menduni Date: Tue, 7 Mar 2023 11:39:59 +0100 Subject: [PATCH 2/3] Add template option to Create cluster dropdown --- frontend/locales/en/strings.json | 1 + .../CreateButtonDropdown.tsx | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/frontend/locales/en/strings.json b/frontend/locales/en/strings.json index b25eb62e..ab9209bd 100644 --- a/frontend/locales/en/strings.json +++ b/frontend/locales/en/strings.json @@ -230,6 +230,7 @@ "cancel": "Cancel", "create": "Create cluster", "createFromWizard": "Use interface", + "createFromTemplate": "Upload a template", "edit": "Edit", "start": "Start fleet", "stop": "Stop fleet", diff --git a/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx index f91df59a..9debe12e 100644 --- a/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx +++ b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx @@ -14,9 +14,16 @@ import { ButtonDropdownProps, } from '@cloudscape-design/components' import {CancelableEventHandler} from '@cloudscape-design/components/internal/events' -import React, {useMemo} from 'react' +import React, {useCallback, useMemo} from 'react' import {useTranslation} from 'react-i18next' import {NavigateFunction, useNavigate} from 'react-router-dom' +import {setState} from '../../../store' +import loadTemplate from '../../Configure/util' +import {HiddenFileUpload} from '../../../components/HiddenFileUpload' +// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'js-y... Remove this comment to see the full error message +import jsyaml from 'js-yaml' + +const loadingPath = ['app', 'wizard', 'source', 'loading'] interface Props { openWizard: (navigate: NavigateFunction) => void @@ -24,9 +31,24 @@ interface Props { export const CreateButtonDropdown: React.FC = ({openWizard}) => { const {t} = useTranslation() + const [isFileDialogOpen, setIsFileDialogOpen] = React.useState(false) const navigate = useNavigate() + const onFileSelectorDismiss = useCallback(() => { + setIsFileDialogOpen(false) + }, []) + + const onFileChange = useCallback( + (data: string) => { + setIsFileDialogOpen(false) + setState(loadingPath, true) + loadTemplate(jsyaml.load(data), () => setState(loadingPath, false)) + openWizard(navigate) + }, + [navigate, openWizard], + ) + const onCreateClick: CancelableEventHandler = React.useCallback( ({detail}) => { @@ -34,6 +56,9 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { case 'wizard': openWizard(navigate) return + case 'template': + setIsFileDialogOpen(true) + return } }, [navigate, openWizard], @@ -45,6 +70,10 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { id: 'wizard', text: t('cluster.list.actions.createFromWizard'), }, + { + id: 'template', + text: t('cluster.list.actions.createFromTemplate'), + }, ], [t], ) @@ -58,6 +87,11 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { > {t('cluster.list.actions.create')} + ) } From 3b171733d7bd5224f1509c688e4319b67bcc9d85 Mon Sep 17 00:00:00 2001 From: Alessandro Menduni Date: Tue, 7 Mar 2023 11:42:28 +0100 Subject: [PATCH 3/3] Add from cluster option to Create cluster dropdown --- frontend/locales/en/strings.json | 1 + .../CreateButtonDropdown.tsx | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/frontend/locales/en/strings.json b/frontend/locales/en/strings.json index ab9209bd..9fe35c68 100644 --- a/frontend/locales/en/strings.json +++ b/frontend/locales/en/strings.json @@ -231,6 +231,7 @@ "create": "Create cluster", "createFromWizard": "Use interface", "createFromTemplate": "Upload a template", + "createFromCluster": "From another cluster", "edit": "Edit", "start": "Start fleet", "stop": "Stop fleet", diff --git a/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx index 9debe12e..c54e119d 100644 --- a/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx +++ b/frontend/src/old-pages/Clusters/CreateButtonDropdown/CreateButtonDropdown.tsx @@ -17,14 +17,23 @@ import {CancelableEventHandler} from '@cloudscape-design/components/internal/eve import React, {useCallback, useMemo} from 'react' import {useTranslation} from 'react-i18next' import {NavigateFunction, useNavigate} from 'react-router-dom' +import {GetConfiguration} from '../../../model' import {setState} from '../../../store' import loadTemplate from '../../Configure/util' import {HiddenFileUpload} from '../../../components/HiddenFileUpload' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'js-y... Remove this comment to see the full error message import jsyaml from 'js-yaml' +import {FromClusterModal} from '../FromClusterModal/FromClusterModal' const loadingPath = ['app', 'wizard', 'source', 'loading'] +function copyFrom(sourceClusterName: any) { + setState(loadingPath, true) + GetConfiguration(sourceClusterName, (configuration: any) => { + loadTemplate(jsyaml.load(configuration), () => setState(loadingPath, false)) + }) +} + interface Props { openWizard: (navigate: NavigateFunction) => void } @@ -32,9 +41,22 @@ interface Props { export const CreateButtonDropdown: React.FC = ({openWizard}) => { const {t} = useTranslation() const [isFileDialogOpen, setIsFileDialogOpen] = React.useState(false) + const [isModalVisible, setIsModalVisible] = React.useState(false) const navigate = useNavigate() + const onModalDismiss = useCallback(() => { + setIsModalVisible(false) + }, []) + + const onCreate = useCallback( + (name: string) => { + copyFrom(name) + openWizard(navigate) + }, + [navigate, openWizard], + ) + const onFileSelectorDismiss = useCallback(() => { setIsFileDialogOpen(false) }, []) @@ -59,6 +81,9 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { case 'template': setIsFileDialogOpen(true) return + case 'from-cluster': + setIsModalVisible(true) + return } }, [navigate, openWizard], @@ -74,6 +99,10 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { id: 'template', text: t('cluster.list.actions.createFromTemplate'), }, + { + id: 'from-cluster', + text: t('cluster.list.actions.createFromCluster'), + }, ], [t], ) @@ -92,6 +121,11 @@ export const CreateButtonDropdown: React.FC = ({openWizard}) => { onDismiss={onFileSelectorDismiss} onChange={onFileChange} /> + ) }