From 88aa3273dc2fc41ee86652d33e79fe0162242f78 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Mon, 25 Nov 2024 11:53:20 +0100 Subject: [PATCH 01/13] fix: fix flickering on column layout --- .../DynamicPageComponent/DynamicPageComponent.js | 6 +++--- src/shared/components/GenericList/GenericList.js | 11 +++++------ src/shared/components/GenericList/components.js | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index ded9f98537..371087cb16 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -352,7 +352,7 @@ export const DynamicPageComponent = ({ titleArea={headerTitle} headerArea={customHeaderContent ?? headerContent} selectedSectionId={selectedSectionIdState} - onBeforeNavigate={e => { + onSelectedSectionChange={e => { if (isFormOpen.formOpen) { e.preventDefault(); } @@ -363,14 +363,14 @@ export const DynamicPageComponent = ({ isFormOpen, setIsFormOpen, () => { - setSelectedSectionIdState(e.detail.sectionId); + setSelectedSectionIdState(e.detail.selectedSectionId); setIsResourceEdited({ isEdited: false, }); }, ); - if (e.detail.sectionId === 'edit') { + if (e.detail.selectedSectionI === 'edit') { setIsFormOpen({ formOpen: true }); } }} diff --git a/src/shared/components/GenericList/GenericList.js b/src/shared/components/GenericList/GenericList.js index 2c310262fb..cf3d1e6396 100644 --- a/src/shared/components/GenericList/GenericList.js +++ b/src/shared/components/GenericList/GenericList.js @@ -1,4 +1,4 @@ -import { Table } from '../../../components/App/UI5Imports'; +import { Table, TableCell, TableRow } from '../../../components/App/UI5Imports'; import { isEmpty } from 'lodash'; import PropTypes from 'prop-types'; @@ -32,6 +32,7 @@ import pluralize from 'pluralize'; import { isResourceEditedState } from 'state/resourceEditedAtom'; import { isFormOpenState } from 'state/formOpenAtom'; import { handleActionIfFormOpen } from '../UnsavedMessageBox/helpers'; +import './GenericList.scss'; const defaultSort = { name: nameLocaleSort, @@ -113,9 +114,7 @@ export const GenericList = ({ }, [pageSize, pagination]); const { i18n, t } = useTranslation(); - const [currentPage, setCurrentPage] = React.useState( - pagination?.initialPage || 1, - ); + const [currentPage, setCurrentPage] = useState(pagination?.initialPage || 1); const [filteredEntries, setFilteredEntries] = useState(() => sorting(sort, entries), @@ -197,7 +196,7 @@ export const GenericList = ({ const renderTableBody = () => { if (serverDataError) { return ( - +

{getErrorMessage(serverDataError)}

); @@ -205,7 +204,7 @@ export const GenericList = ({ if (serverDataLoading) { return ( - + ); diff --git a/src/shared/components/GenericList/components.js b/src/shared/components/GenericList/components.js index ee2710614e..fa39eb4ff9 100644 --- a/src/shared/components/GenericList/components.js +++ b/src/shared/components/GenericList/components.js @@ -8,9 +8,9 @@ import { Button, FlexBox, Icon, Text } from '@ui5/webcomponents-react'; import ListActions from 'shared/components/ListActions/ListActions'; -export const BodyFallback = ({ children }) => ( +export const BodyFallback = ({ children, key }) => ( // TODO replace once new Table component is available in ui5-webcomponents-react - +
{children}
From c85b9051c7acdeda9421b5a1574f041e8001ca56 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Mon, 25 Nov 2024 11:59:26 +0100 Subject: [PATCH 02/13] fix: remove unused --- src/shared/components/GenericList/GenericList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/GenericList/GenericList.js b/src/shared/components/GenericList/GenericList.js index cf3d1e6396..e8cf5dab4a 100644 --- a/src/shared/components/GenericList/GenericList.js +++ b/src/shared/components/GenericList/GenericList.js @@ -1,4 +1,4 @@ -import { Table, TableCell, TableRow } from '../../../components/App/UI5Imports'; +import { Table } from '../../../components/App/UI5Imports'; import { isEmpty } from 'lodash'; import PropTypes from 'prop-types'; From bbddeba21f657fa8e7658fbfb0419c60c1b16a13 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Tue, 26 Nov 2024 14:30:34 +0100 Subject: [PATCH 03/13] fix: replace DynamicPage with ObjectPage --- src/components/KymaModules/KymaModulesList.js | 6 +- .../DynamicPageComponent.js | 144 ++++-------------- .../DynamicPageComponent.scss | 7 +- src/shared/components/UI5Panel/UI5Panel.tsx | 2 +- 4 files changed, 39 insertions(+), 120 deletions(-) diff --git a/src/components/KymaModules/KymaModulesList.js b/src/components/KymaModules/KymaModulesList.js index 52ed92a242..d7176fad51 100644 --- a/src/components/KymaModules/KymaModulesList.js +++ b/src/components/KymaModules/KymaModulesList.js @@ -4,11 +4,11 @@ import jsyaml from 'js-yaml'; import { ResourceDetails } from 'shared/components/ResourceDetails/ResourceDetails'; import { - DynamicPageHeader, Button, FlexBox, Text, Tag, + ObjectPageHeader, } from '@ui5/webcomponents-react'; import { HintButton } from 'shared/components/DescriptionHint/DescriptionHint'; @@ -421,7 +421,7 @@ export default function KymaModulesList({ layoutNumber="StartColumn" windowTitle={t('kyma-modules.title')} headerContent={ - + @@ -434,7 +434,7 @@ export default function KymaModulesList({ ariaTitle={t('kyma-modules.release-channel')} /> - + } customComponents={[ModulesList]} apiGroup={apiGroup} diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index 371087cb16..e5ef540df5 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -1,14 +1,11 @@ import PropTypes from 'prop-types'; import { Button, - DynamicPage, FlexBox, ObjectPage, ObjectPageHeader, ObjectPageSection, ObjectPageTitle, - DynamicPageHeader, - DynamicPageTitle, Title, } from '@ui5/webcomponents-react'; import { Toolbar } from '@ui5/webcomponents-react-compat/dist/components/Toolbar/index.js'; @@ -25,51 +22,6 @@ import { isResourceEditedState } from 'state/resourceEditedAtom'; import { isFormOpenState } from 'state/formOpenAtom'; import { handleActionIfFormOpen } from '../UnsavedMessageBox/helpers'; -const useGetHeaderHeight = dynamicPageRef => { - const [headerHeight, setHeaderHeight] = useState(undefined); - useEffect(() => { - const headerObserver = new ResizeObserver(([header]) => { - setHeaderHeight(header.contentRect.height); - }); - if (dynamicPageRef.current) { - // wait for the custom element to be defined (adjust the tag-name if you're using the scoping feature) - void customElements.whenDefined('ui5-dynamic-page').then(() => { - const shadowRoot = dynamicPageRef.current?.shadowRoot; - - if (!shadowRoot) { - return; - } - - // wait for the shadowRoot to be populated - const shadowRootObserver = new MutationObserver(() => { - const header = shadowRoot.querySelector('header'); - if (header) { - shadowRootObserver.disconnect(); - headerObserver.observe(header); - } - }); - - if (shadowRoot.childElementCount > 0) { - const header = shadowRoot.querySelector('header'); - if (header) { - headerObserver.observe(header); - } else { - return; - } - } else if (shadowRoot instanceof Node) { - shadowRootObserver.observe(shadowRoot, { childList: true }); - } else { - return; - } - }); - } - return () => { - headerObserver.disconnect(); - }; - }, [dynamicPageRef]); - return headerHeight; -}; - const Column = ({ title, children, columnSpan, image, style = {} }) => { const styleComputed = { gridColumn: columnSpan, ...style }; return ( @@ -109,8 +61,25 @@ export const DynamicPageComponent = ({ const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState); const [selectedSectionIdState, setSelectedSectionIdState] = useState('view'); - const dynamicPageRef = useRef(null); - const headerHeight = useGetHeaderHeight(dynamicPageRef); + const [stickyHeaderHeight, setStickyHeaderHeight] = useState(0); + + useEffect(() => { + const pageCompClassName = `.page-header${className ? `.${className}` : ''}`; + setTimeout(() => { + setStickyHeaderHeight( + (document.querySelector(pageCompClassName)?.querySelector('header') + ?.clientHeight ?? 0) + + (document + .querySelector(pageCompClassName) + ?.querySelector('[data-component-name="ObjectPageTabContainer"]') + ?.clientHeight ?? 0) + + (document + .querySelector(pageCompClassName) + ?.querySelector('[data-component-name="ObjectPageHeader"]') + ?.clientHeight ?? 0), + ); + }); + }, []); const handleColumnClose = () => { window.history.pushState( @@ -246,7 +215,7 @@ export const DynamicPageComponent = ({ ); - const headerTitle = inlineEditForm ? ( + const headerTitle = ( - ) : ( - - - {title} - - {protectedResource && ( - - {protectedResourceWarning} - - )} - {description && ( - - )} - - } - actionsBar={actionsBar} - /> ); const headerContent = title !== 'Clusters Overview' && children ? ( - inlineEditForm ? ( - -
- {children} -
-
- ) : ( - -
- {children} -
-
- ) + +
+ {children} +
+
) : null; - const [stickyHeaderHeight, setStickyHeaderHeight] = useState(0); - - useEffect(() => { - setTimeout(() => { - setStickyHeaderHeight( - (document.querySelector('.page-header')?.querySelector('header') - ?.clientHeight ?? 0) + - (document - .querySelector('.page-header') - ?.querySelector('ui5-tabcontainer')?.clientHeight ?? 0), - ); - }); - }, []); - if (inlineEditForm) { return ( - {typeof content === 'function' ? content(headerHeight) : content} - + {typeof content === 'function' ? content(stickyHeaderHeight) : content} + ); }; DynamicPageComponent.Column = Column; diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss index 1d512b03ed..3ca512f01c 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss @@ -1,6 +1,12 @@ .page-header { background-color: var(--sapBackgroundColor); + &.no-tabs { + ui5-tabcontainer { + display: none; + } + } + ui5-dynamic-page-title { justify-content: center; min-height: 3rem; @@ -111,7 +117,6 @@ ui5-button[data-component-name='ObjectPageAnchorBarExpandBtn'] { } } -[data-component-name='DynamicPageContent'], [data-component-name='ObjectPageContent'] { padding: 0 !important; } diff --git a/src/shared/components/UI5Panel/UI5Panel.tsx b/src/shared/components/UI5Panel/UI5Panel.tsx index 1aaa63aac5..f2c7dd47f6 100644 --- a/src/shared/components/UI5Panel/UI5Panel.tsx +++ b/src/shared/components/UI5Panel/UI5Panel.tsx @@ -38,7 +38,7 @@ export const UI5Panel = ({ if (headerTop !== '0') setTimeout(() => { const stickyHeader = document - .querySelector('ui5-panel') + .querySelector('.resource-form--panel') ?.shadowRoot?.querySelector('.ui5-panel-root') ?.querySelector( '.ui5-panel-heading-wrapper.ui5-panel-heading-wrapper-sticky', From fbbad073c8a69de5799fd359fcc69d627a6965ee Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Tue, 26 Nov 2024 15:22:13 +0100 Subject: [PATCH 04/13] fix: fix key error --- src/shared/components/GenericList/components.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/components/GenericList/components.js b/src/shared/components/GenericList/components.js index fa39eb4ff9..ee2710614e 100644 --- a/src/shared/components/GenericList/components.js +++ b/src/shared/components/GenericList/components.js @@ -8,9 +8,9 @@ import { Button, FlexBox, Icon, Text } from '@ui5/webcomponents-react'; import ListActions from 'shared/components/ListActions/ListActions'; -export const BodyFallback = ({ children, key }) => ( +export const BodyFallback = ({ children }) => ( // TODO replace once new Table component is available in ui5-webcomponents-react - +
{children}
From dc2100ff93352607f88ac27533665713458f03b7 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Mon, 2 Dec 2024 09:25:06 +0100 Subject: [PATCH 05/13] fix: banner height and remove unused --- .../Extensibility/components/FeaturedCard/FeaturedCard.scss | 5 +++++ .../components/DynamicPageComponent/DynamicPageComponent.js | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Extensibility/components/FeaturedCard/FeaturedCard.scss b/src/components/Extensibility/components/FeaturedCard/FeaturedCard.scss index fca4f1243b..2e1e9020c2 100644 --- a/src/components/Extensibility/components/FeaturedCard/FeaturedCard.scss +++ b/src/components/Extensibility/components/FeaturedCard/FeaturedCard.scss @@ -3,6 +3,11 @@ margin-left: 0.25rem !important; margin-right: 0.25rem !important; } + +.banner-carousel { + height: fit-content; +} + .feature-card { position: relative; display: flex; diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index e5ef540df5..89bf9c2d64 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -13,7 +13,7 @@ import { ToolbarSpacer } from '@ui5/webcomponents-react-compat/dist/components/T import { ToolbarSeparator } from '@ui5/webcomponents-react-compat/dist/components/ToolbarSeparator/index.js'; import './DynamicPageComponent.scss'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilState } from 'recoil'; import { columnLayoutState } from 'state/columnLayoutAtom'; @@ -79,6 +79,7 @@ export const DynamicPageComponent = ({ ?.clientHeight ?? 0), ); }); + // eslint-disable-line react-hooks/exhaustive-deps }, []); const handleColumnClose = () => { From 4c190a672da7facc7943382cd85b3b26a8c817c5 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Fri, 6 Dec 2024 11:18:05 +0100 Subject: [PATCH 06/13] fix: initial render flicker --- src/components/App/App.tsx | 7 ++++++- src/state/authDataAtom.ts | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 35a42b300d..cbabe87e08 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -39,6 +39,7 @@ import { useAfterInitHook } from 'state/useAfterInitHook'; import useSidebarCondensed from 'sidebar/useSidebarCondensed'; import { useGetValidationEnabledSchemas } from 'state/validationEnabledSchemasAtom'; import { useGetKymaResources } from 'state/kymaResourcesAtom'; +import { Spinner } from 'shared/components/Spinner/Spinner'; export default function App() { const language = useRecoilValue(languageAtom); @@ -59,7 +60,7 @@ export default function App() { useResourceSchemas(); useSidebarCondensed(); - useAuthHandler(); + const { isLoading } = useAuthHandler(); useGetConfiguration(); useGetExtensions(); useGetExtensibilitySchemas(); @@ -75,6 +76,10 @@ export default function App() { useAfterInitHook(kubeconfigIdState); useGetKymaResources(); + if (isLoading) { + return ; + } + return (
diff --git a/src/state/authDataAtom.ts b/src/state/authDataAtom.ts index c598899f2b..a5dbdfe90d 100644 --- a/src/state/authDataAtom.ts +++ b/src/state/authDataAtom.ts @@ -1,6 +1,6 @@ import { parseOIDCparams } from 'components/Clusters/components/oidc-params'; import { UserManager, User } from 'oidc-client-ts'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { atom, useSetRecoilState, useRecoilValue, RecoilState } from 'recoil'; import { KubeconfigNonOIDCAuth, KubeconfigOIDCAuth } from 'types'; @@ -137,6 +137,7 @@ export function useAuthHandler() { const setAuth = useSetRecoilState(authDataState); const navigate = useNavigate(); const setLastFetched = useSetRecoilState(openapiLastFetchedState); + const [isLoading, setIsLoading] = useState(true); useEffect(() => { console.log( @@ -145,14 +146,17 @@ export function useAuthHandler() { if (!cluster) { setAuth(null); + setIsLoading(false); } else { // don't do the auth flow on cluster list (e.g. after refresh, while the OIDC cluster is still connected) if (window.location.pathname === '/clusters') { + setIsLoading(false); return; } const userCredentials = cluster.currentContext?.user?.user; if (hasNonOidcAuth(userCredentials)) { setAuth(userCredentials as KubeconfigNonOIDCAuth); + setIsLoading(false); } else { const onAfterLogin = () => { if (!getPreviousPath() || getPreviousPath() === '/clusters') { @@ -166,8 +170,12 @@ export function useAuthHandler() { navigate('/cluster/' + encodeURIComponent(cluster.name)); } } + setIsLoading(false); + }; + const onError = () => { + navigate('/clusters'); + setIsLoading(false); }; - const onError = () => navigate('/clusters'); handleLogin({ userCredentials: userCredentials as KubeconfigOIDCAuth, @@ -181,6 +189,8 @@ export function useAuthHandler() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [cluster]); + + return { isLoading }; } export const authDataState: RecoilState = atom({ From f68e9f2274e4b50acf2ab8b3bb217d105ebd7d94 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor Date: Wed, 11 Dec 2024 10:04:10 +0100 Subject: [PATCH 07/13] fix: change to DynamicPage --- .../components-form/GenericList.js | 2 +- src/components/KymaModules/KymaModulesList.js | 6 +- .../NamespaceDropdown/NamespaceDropdown.tsx | 4 +- src/hooks/useAvailableNamespaces.ts | 2 +- .../DynamicPageComponent.js | 62 +++++++++---------- .../DynamicPageComponent.scss | 19 +----- src/state/namespacesAtom.ts | 4 +- 7 files changed, 43 insertions(+), 56 deletions(-) diff --git a/src/components/Extensibility/components-form/GenericList.js b/src/components/Extensibility/components-form/GenericList.js index 18341edf0d..d8afcb3342 100644 --- a/src/components/Extensibility/components-form/GenericList.js +++ b/src/components/Extensibility/components-form/GenericList.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { PluginStack, useUIStore } from '@ui-schema/ui-schema'; import { Button } from '@ui5/webcomponents-react'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/KymaModules/KymaModulesList.js b/src/components/KymaModules/KymaModulesList.js index d7176fad51..3e3c028bcd 100644 --- a/src/components/KymaModules/KymaModulesList.js +++ b/src/components/KymaModules/KymaModulesList.js @@ -8,7 +8,7 @@ import { FlexBox, Text, Tag, - ObjectPageHeader, + DynamicPageHeader, } from '@ui5/webcomponents-react'; import { HintButton } from 'shared/components/DescriptionHint/DescriptionHint'; @@ -421,7 +421,7 @@ export default function KymaModulesList({ layoutNumber="StartColumn" windowTitle={t('kyma-modules.title')} headerContent={ - + @@ -434,7 +434,7 @@ export default function KymaModulesList({ ariaTitle={t('kyma-modules.release-channel')} /> - + } customComponents={[ModulesList]} apiGroup={apiGroup} diff --git a/src/header/NamespaceDropdown/NamespaceDropdown.tsx b/src/header/NamespaceDropdown/NamespaceDropdown.tsx index 0d316f4db4..d62d4d2823 100644 --- a/src/header/NamespaceDropdown/NamespaceDropdown.tsx +++ b/src/header/NamespaceDropdown/NamespaceDropdown.tsx @@ -11,7 +11,7 @@ export function NamespaceDropdown() { let namespaces = []; - if (allNamespaces.length > 0) { + if (allNamespaces && allNamespaces.length > 0) { namespaces.push( + allNamespaces?.map(ns => namespaces.push(), ); diff --git a/src/hooks/useAvailableNamespaces.ts b/src/hooks/useAvailableNamespaces.ts index 288426131f..beddfe01b7 100644 --- a/src/hooks/useAvailableNamespaces.ts +++ b/src/hooks/useAvailableNamespaces.ts @@ -28,7 +28,7 @@ export function useAvailableNamespaces() { useEffect(() => { if (error) { - setNamespaces([]); + setNamespaces(null); return; } const filteredNamespaces = allNamespaces diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index 89bf9c2d64..4a2e761b14 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -2,11 +2,15 @@ import PropTypes from 'prop-types'; import { Button, FlexBox, - ObjectPage, - ObjectPageHeader, + DynamicPage, + DynamicPageHeader, ObjectPageSection, - ObjectPageTitle, + DynamicPageTitle, Title, + SegmentedButton, + SegmentedButtonItem, + TabContainer, + Tab, } from '@ui5/webcomponents-react'; import { Toolbar } from '@ui5/webcomponents-react-compat/dist/components/Toolbar/index.js'; import { ToolbarSpacer } from '@ui5/webcomponents-react-compat/dist/components/ToolbarSpacer/index.js'; @@ -75,7 +79,7 @@ export const DynamicPageComponent = ({ ?.clientHeight ?? 0) + (document .querySelector(pageCompClassName) - ?.querySelector('[data-component-name="ObjectPageHeader"]') + ?.querySelector('[data-component-name="DynamicPageHeader"]') ?.clientHeight ?? 0), ); }); @@ -217,9 +221,9 @@ export const DynamicPageComponent = ({ ); const headerTitle = ( - + <DynamicPageHeader className="header-wrapper"> <section className={`column-wrapper ${columnWrapperClassName || ''}`}> {children} </section> - </ObjectPageHeader> + </DynamicPageHeader> ) : null; if (inlineEditForm) { return ( - <ObjectPage + <DynamicPage mode="IconTabBar" - className={`page-header ${className}`} + className={`page-header ${className} no-shadow`} headerPinned hidePinButton={true} titleArea={headerTitle} @@ -291,38 +295,34 @@ export const DynamicPageComponent = ({ } }} > - <ObjectPageSection - aria-label="View" - hideTitleText - id="view" - titleText={t('common.tabs.view')} + <TabContainer + onTabSelect={event => { + const mode = event.detail.tab.getAttribute('data-mode'); + setSelectedSectionIdState(mode); + }} > - {content} - </ObjectPageSection> - <ObjectPageSection - aria-label="Edit" - hideTitleText - id="edit" - titleText={ - showYamlTab ? t('common.tabs.yaml') : t('common.tabs.edit') - } - > - {inlineEditForm(stickyHeaderHeight)} - </ObjectPageSection> - </ObjectPage> + <Tab data-mode="view" text="View"></Tab> + <Tab data-mode="edit" text="Edit"></Tab> + </TabContainer> + + {selectedSectionIdState === 'view' && content} + + {selectedSectionIdState === 'edit' && + inlineEditForm(stickyHeaderHeight)} + </DynamicPage> ); } return ( - <ObjectPage - className="page-header no-tabs" + <DynamicPage + className="page-header" hidePinButton titleArea={headerTitle} headerArea={headerContent} footerArea={footer} > {typeof content === 'function' ? content(stickyHeaderHeight) : content} - </ObjectPage> + </DynamicPage> ); }; DynamicPageComponent.Column = Column; diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss index 3ca512f01c..7f3b00be43 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss @@ -1,15 +1,14 @@ .page-header { background-color: var(--sapBackgroundColor); - &.no-tabs { - ui5-tabcontainer { - display: none; - } + .no-shadow { + box-shadow: none; } ui5-dynamic-page-title { justify-content: center; min-height: 3rem; + padding-left: 2rem; } .bold-title { @@ -33,10 +32,6 @@ display: none; } - [data-component-name='ObjectPageTitleMiddleSection'] > div { - flex-basis: 100%; - } - &__actions { display: flex; align-items: center; @@ -105,10 +100,6 @@ } } -ui5-button[data-component-name='ObjectPageAnchorBarExpandBtn'] { - display: none; -} - @media (max-width: 768px) { .column-wrapper { display: flex; @@ -116,7 +107,3 @@ ui5-button[data-component-name='ObjectPageAnchorBarExpandBtn'] { gap: 12px; } } - -[data-component-name='ObjectPageContent'] { - padding: 0 !important; -} diff --git a/src/state/namespacesAtom.ts b/src/state/namespacesAtom.ts index bf94bf69d3..c03fd3b363 100644 --- a/src/state/namespacesAtom.ts +++ b/src/state/namespacesAtom.ts @@ -1,8 +1,8 @@ import { atom, RecoilState } from 'recoil'; -export type NamespacesState = string[]; +export type NamespacesState = string[] | null; -const defaultValue: string[] = []; +const defaultValue = null; export const namespacesState: RecoilState<NamespacesState> = atom< NamespacesState From 6dd8e66956bbb33468385790543718cbac20ff62 Mon Sep 17 00:00:00 2001 From: mrCherry97 <mwisnia97@gmail.com> Date: Thu, 12 Dec 2024 11:20:55 +0100 Subject: [PATCH 08/13] hotfix --- src/resources/createResourceRoutes.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/resources/createResourceRoutes.js b/src/resources/createResourceRoutes.js index 845145da95..709f5e0104 100644 --- a/src/resources/createResourceRoutes.js +++ b/src/resources/createResourceRoutes.js @@ -122,16 +122,13 @@ const ColumnWrapper = ({ detailsMidColumn = detailsComponent; } - const { schema, loading } = useGetSchema({ + const { schema } = useGetSchema({ resource: { group: props?.apiGroup, version: props.apiVersion, kind: props?.resourceType.slice(0, -1), }, }); - if (loading) { - return null; - } const createMidColumn = ( <ResourceCreate @@ -156,7 +153,7 @@ const ColumnWrapper = ({ ); return ( - <SchemaContext.Provider value={schema}> + <SchemaContext.Provider value={schema || null}> <FlexibleColumnLayout style={{ height: '100%' }} layout={layoutState?.layout || 'OneColumn'} @@ -211,9 +208,7 @@ export const createResourceRoutes = ({ details={<Details />} create={Create ? <Create /> : null} {...props} - > - <List allowSlashShortcut /> - </ColumnWrapper> + /> </Suspense> } /> @@ -231,9 +226,7 @@ export const createResourceRoutes = ({ create={Create ? <Create /> : null} defaultColumn="details" {...props} - > - <Details /> - </ColumnWrapper> + /> </Suspense> } /> From c2136846dbc95bea8bb9daacc5474567a3317151 Mon Sep 17 00:00:00 2001 From: akucharska <agata.kucharska@sap.com> Date: Thu, 12 Dec 2024 11:27:18 +0100 Subject: [PATCH 09/13] Replace props spread --- src/resources/createResourceRoutes.js | 35 +++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/resources/createResourceRoutes.js b/src/resources/createResourceRoutes.js index 709f5e0104..2f1b975f49 100644 --- a/src/resources/createResourceRoutes.js +++ b/src/resources/createResourceRoutes.js @@ -84,17 +84,31 @@ const ColumnWrapper = ({ }); const elementListProps = usePrepareListProps({ - ...props, + resourceCustomType: props.resourceCustomType, + resourceType: props.resourceType, + resourceI18Key: props.resourceI18Key, + apiGroup: props.apiGroup, + apiVersion: props.apiVersion, + hasDetailsView: props.hasDetailsView, }); const elementDetailsProps = usePrepareDetailsProps({ - ...props, + resourceCustomType: props.resourceCustomType, + resourceType: props.resourceType, + resourceI18Key: props.resourceI18Key, + apiGroup: props.apiGroup, + apiVersion: props.apiVersion, resourceName: layoutState?.midColumn?.resourceName ?? resourceName, namespaceId: layoutState?.midColumn?.namespaceId ?? namespaceId, + showYamlTab: props.showYamlTab, }); const elementCreateProps = usePrepareCreateProps({ - ...props, + resourceCustomType: props.resourceCustomType, + resourceType: props.resourceType, + resourceTypeForTitle: props.resourceType, + apiGroup: props.apiGroup, + apiVersion: props.apiVersion, }); const listComponent = React.cloneElement(list, { @@ -122,13 +136,16 @@ const ColumnWrapper = ({ detailsMidColumn = detailsComponent; } - const { schema } = useGetSchema({ + const { schema, loading } = useGetSchema({ resource: { group: props?.apiGroup, version: props.apiVersion, kind: props?.resourceType.slice(0, -1), }, }); + if (loading) { + return null; + } const createMidColumn = ( <ResourceCreate @@ -153,7 +170,7 @@ const ColumnWrapper = ({ ); return ( - <SchemaContext.Provider value={schema || null}> + <SchemaContext.Provider value={schema}> <FlexibleColumnLayout style={{ height: '100%' }} layout={layoutState?.layout || 'OneColumn'} @@ -208,7 +225,9 @@ export const createResourceRoutes = ({ details={<Details />} create={Create ? <Create /> : null} {...props} - /> + > + <List allowSlashShortcut /> + </ColumnWrapper> </Suspense> } /> @@ -226,7 +245,9 @@ export const createResourceRoutes = ({ create={Create ? <Create /> : null} defaultColumn="details" {...props} - /> + > + <Details /> + </ColumnWrapper> </Suspense> } /> From 25cfea9e1daf259fcf7160cd98a2f587905fe159 Mon Sep 17 00:00:00 2001 From: mrCherry97 <mwisnia97@gmail.com> Date: Thu, 12 Dec 2024 11:28:36 +0100 Subject: [PATCH 10/13] hotfix --- src/resources/createResourceRoutes.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/resources/createResourceRoutes.js b/src/resources/createResourceRoutes.js index 2f1b975f49..9c1052751c 100644 --- a/src/resources/createResourceRoutes.js +++ b/src/resources/createResourceRoutes.js @@ -136,16 +136,13 @@ const ColumnWrapper = ({ detailsMidColumn = detailsComponent; } - const { schema, loading } = useGetSchema({ + const { schema } = useGetSchema({ resource: { group: props?.apiGroup, version: props.apiVersion, kind: props?.resourceType.slice(0, -1), }, }); - if (loading) { - return null; - } const createMidColumn = ( <ResourceCreate @@ -170,7 +167,7 @@ const ColumnWrapper = ({ ); return ( - <SchemaContext.Provider value={schema}> + <SchemaContext.Provider value={schema || null}> <FlexibleColumnLayout style={{ height: '100%' }} layout={layoutState?.layout || 'OneColumn'} From 074ca3f32bcff48a2518ef952deadf20d67437fa Mon Sep 17 00:00:00 2001 From: Oliwia Gowor <oliwiagowor@gmail.com> Date: Thu, 12 Dec 2024 11:59:59 +0100 Subject: [PATCH 11/13] fix: shadow & unused --- .../HelmReleases/HelmReleasesList.js | 58 +++++++++---------- .../DynamicPageComponent.js | 16 ++--- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/components/HelmReleases/HelmReleasesList.js b/src/components/HelmReleases/HelmReleasesList.js index ababeb4705..8ace16c496 100644 --- a/src/components/HelmReleases/HelmReleasesList.js +++ b/src/components/HelmReleases/HelmReleasesList.js @@ -72,36 +72,34 @@ function HelmReleasesList() { ]; return ( - <> - <ResourcesList - resources={entries} - customColumns={customColumns} - serverDataLoading={loading} - serverDataError={error} - allowSlashShortcut - hasDetailsView - enableColumnLayout - customUrl={resourceUrl} - resourceUrl={dataUrl} - resourceType="HelmReleases" - sortBy={{ - name: (a, b) => a.releaseName.localeCompare(b.releaseName), - }} - searchSettings={{ - textSearchProperties: [ - 'recentRelease.chart.metadata.name', - 'releaseName', - ], - }} - emptyListProps={{ - subtitleText: ResourceDescription, - url: docsURL, - showButton: false, - }} - readOnly - description={ResourceDescription} - /> - </> + <ResourcesList + resources={entries} + customColumns={customColumns} + serverDataLoading={loading} + serverDataError={error} + allowSlashShortcut + hasDetailsView + enableColumnLayout + customUrl={resourceUrl} + resourceUrl={dataUrl} + resourceType="HelmReleases" + sortBy={{ + name: (a, b) => a.releaseName.localeCompare(b.releaseName), + }} + searchSettings={{ + textSearchProperties: [ + 'recentRelease.chart.metadata.name', + 'releaseName', + ], + }} + emptyListProps={{ + subtitleText: ResourceDescription, + url: docsURL, + showButton: false, + }} + readOnly + description={ResourceDescription} + /> ); } diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index 4a2e761b14..cf410fffdd 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -4,11 +4,8 @@ import { FlexBox, DynamicPage, DynamicPageHeader, - ObjectPageSection, DynamicPageTitle, Title, - SegmentedButton, - SegmentedButtonItem, TabContainer, Tab, } from '@ui5/webcomponents-react'; @@ -83,8 +80,7 @@ export const DynamicPageComponent = ({ ?.clientHeight ?? 0), ); }); - // eslint-disable-line react-hooks/exhaustive-deps - }, []); + }, []); // eslint-disable-line react-hooks/exhaustive-deps const handleColumnClose = () => { window.history.pushState( @@ -222,6 +218,7 @@ export const DynamicPageComponent = ({ const headerTitle = ( <DynamicPageTitle + className={inlineEditForm ? 'no-shadow' : ''} style={title === 'Clusters Overview' ? { display: 'none' } : null} heading={ <FlexBox className="title-container" alignItems="Center"> @@ -266,7 +263,7 @@ export const DynamicPageComponent = ({ return ( <DynamicPage mode="IconTabBar" - className={`page-header ${className} no-shadow`} + className={`page-header ${className}`} headerPinned hidePinButton={true} titleArea={headerTitle} @@ -301,8 +298,11 @@ export const DynamicPageComponent = ({ setSelectedSectionIdState(mode); }} > - <Tab data-mode="view" text="View"></Tab> - <Tab data-mode="edit" text="Edit"></Tab> + <Tab data-mode="view" text={t('common.tabs.view')}></Tab> + <Tab + data-mode="edit" + text={showYamlTab ? t('common.tabs.yaml') : t('common.tabs.edit')} + ></Tab> </TabContainer> {selectedSectionIdState === 'view' && content} From e196b344e5e1aa72ba8a30fece97675ce4ab2258 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor <oliwiagowor@gmail.com> Date: Fri, 13 Dec 2024 08:25:45 +0100 Subject: [PATCH 12/13] fix: shadow & sticky tabs --- src/components/KymaModules/KymaModulesList.js | 4 +- .../DynamicPageComponent.js | 107 ++++++++++++++---- .../DynamicPageComponent.scss | 15 ++- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/components/KymaModules/KymaModulesList.js b/src/components/KymaModules/KymaModulesList.js index 3e3c028bcd..3f7800fa4e 100644 --- a/src/components/KymaModules/KymaModulesList.js +++ b/src/components/KymaModules/KymaModulesList.js @@ -4,11 +4,11 @@ import jsyaml from 'js-yaml'; import { ResourceDetails } from 'shared/components/ResourceDetails/ResourceDetails'; import { + DynamicPageHeader, Button, FlexBox, Text, Tag, - DynamicPageHeader, } from '@ui5/webcomponents-react'; import { HintButton } from 'shared/components/DescriptionHint/DescriptionHint'; @@ -421,7 +421,7 @@ export default function KymaModulesList({ layoutNumber="StartColumn" windowTitle={t('kyma-modules.title')} headerContent={ - <DynamicPageHeader> + <DynamicPageHeader className="no-shadow"> <FlexBox alignItems="Center"> <Label showColon>{t('kyma-modules.release-channel')}</Label> <Text renderWhitespace={true}> </Text> diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index cf410fffdd..995a11edb2 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -14,7 +14,7 @@ import { ToolbarSpacer } from '@ui5/webcomponents-react-compat/dist/components/T import { ToolbarSeparator } from '@ui5/webcomponents-react-compat/dist/components/ToolbarSeparator/index.js'; import './DynamicPageComponent.scss'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilState } from 'recoil'; import { columnLayoutState } from 'state/columnLayoutAtom'; @@ -23,6 +23,62 @@ import { isResourceEditedState } from 'state/resourceEditedAtom'; import { isFormOpenState } from 'state/formOpenAtom'; import { handleActionIfFormOpen } from '../UnsavedMessageBox/helpers'; +const useGetHeaderHeight = (dynamicPageRef, tabContainerRef) => { + const [headerHeight, setHeaderHeight] = useState(undefined); + const [tabContainerHeight, setTabContainerHeight] = useState(undefined); + + useEffect(() => { + const headerObserver = new ResizeObserver(([header]) => { + setHeaderHeight(header.contentRect.height); + }); + + const tabContainerObserver = new ResizeObserver(([tabContainer]) => { + setTabContainerHeight(tabContainer.contentRect.height); + }); + + if (dynamicPageRef.current) { + // Wait for the custom element to be defined + void customElements.whenDefined('ui5-dynamic-page').then(() => { + const shadowRoot = dynamicPageRef.current?.shadowRoot; + + if (!shadowRoot) { + return; + } + + // Wait for the shadowRoot to be populated + const shadowRootObserver = new MutationObserver(() => { + const header = shadowRoot.querySelector('header'); + + if (header) { + headerObserver.observe(header); + shadowRootObserver.disconnect(); + } + }); + + if (shadowRoot.childElementCount > 0) { + const header = shadowRoot.querySelector('header'); + if (header) { + headerObserver.observe(header); + } + } else if (shadowRoot instanceof Node) { + shadowRootObserver.observe(shadowRoot, { childList: true }); + } + }); + } + + if (tabContainerRef.current) { + tabContainerObserver.observe(tabContainerRef.current); + } + + return () => { + headerObserver.disconnect(); + tabContainerObserver.disconnect(); + }; + }, [dynamicPageRef, tabContainerRef]); + + return { headerHeight, tabContainerHeight }; +}; + const Column = ({ title, children, columnSpan, image, style = {} }) => { const styleComputed = { gridColumn: columnSpan, ...style }; return ( @@ -62,25 +118,12 @@ export const DynamicPageComponent = ({ const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState); const [selectedSectionIdState, setSelectedSectionIdState] = useState('view'); - const [stickyHeaderHeight, setStickyHeaderHeight] = useState(0); - - useEffect(() => { - const pageCompClassName = `.page-header${className ? `.${className}` : ''}`; - setTimeout(() => { - setStickyHeaderHeight( - (document.querySelector(pageCompClassName)?.querySelector('header') - ?.clientHeight ?? 0) + - (document - .querySelector(pageCompClassName) - ?.querySelector('[data-component-name="ObjectPageTabContainer"]') - ?.clientHeight ?? 0) + - (document - .querySelector(pageCompClassName) - ?.querySelector('[data-component-name="DynamicPageHeader"]') - ?.clientHeight ?? 0), - ); - }); - }, []); // eslint-disable-line react-hooks/exhaustive-deps + const dynamicPageRef = useRef(null); + const tabContainerRef = useRef(null); + const { headerHeight, tabContainerHeight } = useGetHeaderHeight( + dynamicPageRef, + tabContainerRef, + ); const handleColumnClose = () => { window.history.pushState( @@ -291,8 +334,27 @@ export const DynamicPageComponent = ({ setIsFormOpen({ formOpen: true }); } }} + ref={dynamicPage => { + if (dynamicPageRef) { + if (typeof dynamicPageRef === 'function') { + dynamicPageRef(dynamicPage); + } else if (dynamicPageRef.current !== undefined) { + dynamicPageRef.current = dynamicPage; + } + } + + const button = dynamicPage?.shadowRoot?.querySelector( + 'ui5-dynamic-page-header-actions', + ); + if (button) { + button.style['display'] = 'none'; + } + }} > <TabContainer + className="tab-container" + style={{ top: `${headerHeight}px` }} + ref={tabContainerRef} onTabSelect={event => { const mode = event.detail.tab.getAttribute('data-mode'); setSelectedSectionIdState(mode); @@ -308,7 +370,7 @@ export const DynamicPageComponent = ({ {selectedSectionIdState === 'view' && content} {selectedSectionIdState === 'edit' && - inlineEditForm(stickyHeaderHeight)} + inlineEditForm(headerHeight + tabContainerHeight)} </DynamicPage> ); } @@ -320,8 +382,9 @@ export const DynamicPageComponent = ({ titleArea={headerTitle} headerArea={headerContent} footerArea={footer} + ref={dynamicPageRef} > - {typeof content === 'function' ? content(stickyHeaderHeight) : content} + {typeof content === 'function' ? content(headerHeight) : content} </DynamicPage> ); }; diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss index 7f3b00be43..a1122a9d0b 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.scss @@ -1,10 +1,6 @@ .page-header { background-color: var(--sapBackgroundColor); - .no-shadow { - box-shadow: none; - } - ui5-dynamic-page-title { justify-content: center; min-height: 3rem; @@ -58,6 +54,17 @@ position: static; padding: unset; } + + .tab-container { + position: sticky; + z-index: 1; + } +} + +ui5-dynamic-page { + .no-shadow { + box-shadow: none; + } } .header-wrapper { From 26e2f295798ab50b82a69ec7e93af82a37c7bb39 Mon Sep 17 00:00:00 2001 From: Oliwia Gowor <oliwiagowor@gmail.com> Date: Fri, 13 Dec 2024 08:35:58 +0100 Subject: [PATCH 13/13] fix: hide button everywhere --- .../DynamicPageComponent.js | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js index 995a11edb2..3328464404 100644 --- a/src/shared/components/DynamicPageComponent/DynamicPageComponent.js +++ b/src/shared/components/DynamicPageComponent/DynamicPageComponent.js @@ -302,6 +302,23 @@ export const DynamicPageComponent = ({ </DynamicPageHeader> ) : null; + const handlePageRef = dynamicPage => { + if (dynamicPageRef) { + if (typeof dynamicPageRef === 'function') { + dynamicPageRef(dynamicPage); + } else if (dynamicPageRef.current !== undefined) { + dynamicPageRef.current = dynamicPage; + } + } + + const button = dynamicPage?.shadowRoot?.querySelector( + 'ui5-dynamic-page-header-actions', + ); + if (button) { + button.style['display'] = 'none'; + } + }; + if (inlineEditForm) { return ( <DynamicPage @@ -334,22 +351,7 @@ export const DynamicPageComponent = ({ setIsFormOpen({ formOpen: true }); } }} - ref={dynamicPage => { - if (dynamicPageRef) { - if (typeof dynamicPageRef === 'function') { - dynamicPageRef(dynamicPage); - } else if (dynamicPageRef.current !== undefined) { - dynamicPageRef.current = dynamicPage; - } - } - - const button = dynamicPage?.shadowRoot?.querySelector( - 'ui5-dynamic-page-header-actions', - ); - if (button) { - button.style['display'] = 'none'; - } - }} + ref={dynamicPage => handlePageRef(dynamicPage)} > <TabContainer className="tab-container" @@ -382,7 +384,7 @@ export const DynamicPageComponent = ({ titleArea={headerTitle} headerArea={headerContent} footerArea={footer} - ref={dynamicPageRef} + ref={dynamicPage => handlePageRef(dynamicPage)} > {typeof content === 'function' ? content(headerHeight) : content} </DynamicPage>