diff --git a/src/studio/src/designer/frontend/packages/shared/src/pure/utils.ts b/src/studio/src/designer/frontend/packages/shared/src/pure/utils.ts index f06871db869..fb95d520bcc 100644 --- a/src/studio/src/designer/frontend/packages/shared/src/pure/utils.ts +++ b/src/studio/src/designer/frontend/packages/shared/src/pure/utils.ts @@ -1,2 +1,7 @@ export const isNumeric = (str: string) => parseInt(str).toString() === str; export const deepCopy = (value: any) => JSON.parse(JSON.stringify(value)); +export const removeKey = (obj: any, key: string) => { + const copy = deepCopy(obj); + delete copy[key]; + return copy; +}; diff --git a/src/studio/src/designer/frontend/packages/ux-editor/src/App.tsx b/src/studio/src/designer/frontend/packages/ux-editor/src/App.tsx index 1402b516702..e9509728862 100644 --- a/src/studio/src/designer/frontend/packages/ux-editor/src/App.tsx +++ b/src/studio/src/designer/frontend/packages/ux-editor/src/App.tsx @@ -1,8 +1,8 @@ import React, { useEffect } from 'react'; import postMessages from 'app-shared/utils/postMessages'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { ErrorMessageComponent } from './components/message/ErrorMessageComponent'; -import FormDesigner from './containers/FormDesigner'; +import { FormDesigner } from './containers/FormDesigner'; import { FormLayoutActions } from './features/formDesigner/formLayout/formLayoutSlice'; import { loadTextResources } from './features/appData/textResources/textResourcesSlice'; import { fetchWidgets, fetchWidgetSettings } from './features/widgets/widgetsSlice'; @@ -10,8 +10,10 @@ import { fetchDataModel } from './features/appData/dataModel/dataModelSlice'; import { fetchLanguage } from './features/appData/language/languageSlice'; import { fetchRuleModel } from './features/appData/ruleModel/ruleModelSlice'; import { fetchServiceConfiguration } from './features/serviceConfigurations/serviceConfigurationSlice'; -import { useParams } from 'react-router-dom'; +import { useParams, useSearchParams } from 'react-router-dom'; import { textResourcesPath } from 'app-shared/api-paths'; +import type { IAppState } from './types/global'; +import { deepCopy } from 'app-shared/pure'; /** * This is the main React component responsible for controlling @@ -22,11 +24,31 @@ import { textResourcesPath } from 'app-shared/api-paths'; export function App() { const dispatch = useDispatch(); const { org, app } = useParams(); + const languageCode = 'nb'; + const [searchParams, setSearchParams] = useSearchParams(); + const layoutOrder = useSelector( + (state: IAppState) => state.formDesigner.layout.layoutSettings.pages.order + ); + const selectedLayout = useSelector( + (state: IAppState) => state.formDesigner.layout.selectedLayout + ); + + // Set Layout to first layout in the page set if none is selected. + useEffect(() => { + if (!searchParams.has('layout') && layoutOrder[0]) { + setSearchParams({ ...deepCopy(searchParams), layout: layoutOrder[0] }); + } + if (selectedLayout === 'default' && searchParams.has('layout')) { + dispatch( + FormLayoutActions.updateSelectedLayout({ selectedLayout: searchParams.get('layout') }) + ); + } + }, [dispatch, layoutOrder, searchParams, setSearchParams, selectedLayout]); + useEffect(() => { const fetchFiles = () => { dispatch(fetchDataModel()); dispatch(FormLayoutActions.fetchFormLayout()); - const languageCode = 'nb'; dispatch(loadTextResources({ url: textResourcesPath(org, app, languageCode) })); dispatch(fetchServiceConfiguration()); dispatch(fetchRuleModel()); diff --git a/src/studio/src/designer/frontend/packages/ux-editor/src/components/rightMenu/RightMenu.tsx b/src/studio/src/designer/frontend/packages/ux-editor/src/components/rightMenu/RightMenu.tsx index dd2941505cf..bbce051dd1f 100644 --- a/src/studio/src/designer/frontend/packages/ux-editor/src/components/rightMenu/RightMenu.tsx +++ b/src/studio/src/designer/frontend/packages/ux-editor/src/components/rightMenu/RightMenu.tsx @@ -9,6 +9,8 @@ import type { IAppState, LogicMode } from '../../types/global'; import classes from './RightMenu.module.css'; import { Add } from '@navikt/ds-icons'; import { Button, ButtonVariant } from '@altinn/altinn-design-system'; +import { deepCopy } from 'app-shared/pure'; +import { useSearchParams } from 'react-router-dom'; export interface IRightMenuProps { toggleFileEditor: (mode?: LogicMode) => void; @@ -17,6 +19,7 @@ export interface IRightMenuProps { export default function RightMenu(props: IRightMenuProps) { const [conditionalModalOpen, setConditionalModalOpen] = React.useState(false); + const [searchParams, setSearchParams] = useSearchParams(); const [ruleModalOpen, setRuleModalOpen] = React.useState(false); const layoutOrder = useSelector( (state: IAppState) => state.formDesigner.layout.layoutSettings.pages.order @@ -34,6 +37,7 @@ export default function RightMenu(props: IRightMenuProps) { function handleAddPage() { const name = t('right_menu.page') + (layoutOrder.length + 1); dispatch(FormLayoutActions.addLayout({ layout: name })); + setSearchParams({ ...deepCopy(searchParams), layout: name }); } return ( @@ -53,8 +57,7 @@ export default function RightMenu(props: IRightMenuProps) {
{t('right_menu.dynamics')}
- {t('right_menu.dynamics_description')} -   + {t('right_menu.dynamics_description')}  state.appData.languageState.language); const t = (key: string) => getLanguageFromKey(key, language); - const selectedLayout = useSelector( - (state: IAppState) => state.formDesigner.layout.selectedLayout - ); const layoutOrder = useSelector( (state: IAppState) => state.formDesigner.layout.layoutSettings.pages.order ); - const [editMode, setEditMode] = React.useState(false); - const [errorMessage, setErrorMessage] = React.useState(''); - const [newName, setNewName] = React.useState(''); - const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); - const [deleteAnchorEl, setDeleteAnchorEl] = React.useState(null); + const [editMode, setEditMode] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [newName, setNewName] = useState(''); + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const [deleteAnchorEl, setDeleteAnchorEl] = useState(null); const disableUp = layoutOrder.indexOf(name) === 0; const disableDown = layoutOrder.indexOf(name) === layoutOrder.length - 1; + useEffect(() => { + if (name !== selectedLayout) { + setEditMode(false); + } + }, [name, selectedLayout]); + const onPageClick = () => { if (invalid) { alert(`${name}: ${t('right_menu.pages.invalid_page_data')}`); - } - else if (selectedLayout !== name) { + } else if (selectedLayout !== name) { dispatch(FormLayoutActions.updateSelectedLayout({ selectedLayout: name })); + setSearchParams({ ...deepCopy(searchParams), layout: name }); } }; - const onPageSettingsClick = (event: React.MouseEvent) => { - event.stopPropagation(); + const onPageSettingsClick = (event: MouseEvent) => setMenuAnchorEl(event.currentTarget); - }; - const onMenuClose = (event: React.SyntheticEvent) => { - event.stopPropagation(); - setMenuAnchorEl(null); - }; + const onMenuClose = (_event: SyntheticEvent) => setMenuAnchorEl(null); - const onMenuItemClick = ( - event: React.SyntheticEvent, - action: 'up' | 'down' | 'edit' | 'delete' - ) => { - event.stopPropagation(); + const onMenuItemClick = (event: SyntheticEvent, action: 'up' | 'down' | 'edit' | 'delete') => { if (action === 'delete') { setDeleteAnchorEl(event.currentTarget); } else if (action === 'edit') { @@ -75,21 +73,18 @@ export function PageElement({ name, invalid }: IPageElementProps) { setMenuAnchorEl(null); }; - const handleOnBlur = (event: any) => { - event.stopPropagation(); + const handleOnBlur = async (_event: any) => { setEditMode(false); if (!errorMessage && name !== newName) { - dispatch(FormLayoutActions.updateLayoutName({ oldName: name, newName })); + await dispatch(FormLayoutActions.updateLayoutName({ oldName: name, newName })); + setSearchParams({ ...deepCopy(searchParams), layout: newName }); } }; - const pageNameExists = (candidateName: string): boolean => { - return layoutOrder.some( - (pageName: string) => pageName.toLowerCase() === candidateName.toLowerCase() - ); - }; + const pageNameExists = (candidateName: string): boolean => + layoutOrder.some((p: string) => p.toLowerCase() === candidateName.toLowerCase()); - const handleOnChange = (event: React.ChangeEvent) => { + const handleOnChange = (event: ChangeEvent) => { const nameRegex = new RegExp('^[a-zA-Z0-9_\\-\\.]*$'); const newNameCandidate = event.target.value.replace(/[/\\?%*:|"<>]/g, '-').trim(); if (pageNameExists(newNameCandidate)) { @@ -106,10 +101,10 @@ export function PageElement({ name, invalid }: IPageElementProps) { } }; - const handleKeyPress = (event: any) => { - event.stopPropagation(); + const handleKeyPress = async (event: any) => { if (event.key === 'Enter' && !errorMessage && name !== newName) { - dispatch(FormLayoutActions.updateLayoutName({ oldName: name, newName })); + await dispatch(FormLayoutActions.updateLayoutName({ oldName: name, newName })); + setSearchParams({ ...deepCopy(searchParams), layout: newName }); setEditMode(false); } else if (event.key === 'Escape') { setEditMode(false); @@ -117,19 +112,18 @@ export function PageElement({ name, invalid }: IPageElementProps) { } }; - const handleConfirmDeleteClose = (event?: React.SyntheticEvent) => { - event?.stopPropagation(); - setDeleteAnchorEl(null); - }; + const handleConfirmDeleteClose = () => setDeleteAnchorEl(null); - const handleConfirmDelete = (event?: React.SyntheticEvent) => { - event?.stopPropagation(); + const handleConfirmDelete = () => { setDeleteAnchorEl(null); dispatch(FormLayoutActions.deleteLayout({ layout: name })); + setSearchParams(removeKey(searchParams, 'layout')); }; return ( -
+
-
- {!editMode && name} - {editMode && ( - <> - - {errorMessage} - - )} -
-
-
+ {editMode ? ( + <> + + {errorMessage} + + ) : ( +
{name}
+ )} +
diff --git a/src/studio/src/designer/frontend/packages/ux-editor/src/containers/Container.tsx b/src/studio/src/designer/frontend/packages/ux-editor/src/containers/Container.tsx index e82d28acf1e..76f65a489fc 100644 --- a/src/studio/src/designer/frontend/packages/ux-editor/src/containers/Container.tsx +++ b/src/studio/src/designer/frontend/packages/ux-editor/src/containers/Container.tsx @@ -378,7 +378,7 @@ export class ContainerComponent extends Component