зеркало из https://github.com/Azure/ARO-RP.git
[Admin Portal]: Add resource ID param in the URL (#2640)
This commit is contained in:
Родитель
74964218d2
Коммит
06f7b8bc57
|
@ -27,14 +27,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
|||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||
// of clientsets, like in:
|
||||
//
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
//
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
//
|
||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||
// correctly.
|
||||
|
|
|
@ -27,14 +27,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
|||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||
// of clientsets, like in:
|
||||
//
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
//
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
//
|
||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||
// correctly.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"files": {
|
||||
"main.js": "/static/js/main.66cb7002.js",
|
||||
"main.js": "/static/js/main.55666f6d.js",
|
||||
"index.html": "/index.html",
|
||||
"main.66cb7002.js.map": "/static/js/main.66cb7002.js.map"
|
||||
"main.55666f6d.js.map": "/static/js/main.55666f6d.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/main.66cb7002.js"
|
||||
"static/js/main.55666f6d.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>ARO Portal</title><script defer="defer" src="/static/js/main.66cb7002.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>ARO Portal</title><script defer="defer" src="/static/js/main.55666f6d.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -54,7 +54,8 @@
|
|||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
"last 1 safari version",
|
||||
"last 1 edge version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ export interface IClusterDetail {
|
|||
clusterName: string
|
||||
}
|
||||
|
||||
function App() {
|
||||
function App(props: {params: any}) {
|
||||
const [data, updateData] = useState({ location: "", csrf: "", elevated: false, username: "" })
|
||||
const [regions, setRegions] = useState<any>([])
|
||||
const [error, setError] = useState<AxiosResponse | null>(null)
|
||||
|
@ -275,6 +275,7 @@ function App() {
|
|||
sshBox={sshRef}
|
||||
setCurrentCluster={setCurrentCluster}
|
||||
csrfTokenAvailable={fetching}
|
||||
params={props.params}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
|
|
|
@ -266,6 +266,7 @@ export function ClusterDetailPanel(props: {
|
|||
|
||||
return (
|
||||
<Panel
|
||||
id="ClusterDetailPanel"
|
||||
isOpen={isOpen}
|
||||
type={PanelType.custom}
|
||||
onDismiss={_dismissPanel}
|
||||
|
|
|
@ -13,6 +13,10 @@ import {
|
|||
TooltipHost,
|
||||
TextField,
|
||||
Link,
|
||||
Layer,
|
||||
Popup,
|
||||
DefaultButton,
|
||||
FocusTrapZone,
|
||||
} from "@fluentui/react"
|
||||
import {
|
||||
DetailsList,
|
||||
|
@ -21,6 +25,7 @@ import {
|
|||
IColumn,
|
||||
IDetailsListStyles,
|
||||
} from "@fluentui/react/lib/DetailsList"
|
||||
import { useBoolean } from "@fluentui/react-hooks"
|
||||
import { FetchClusters } from "./Request"
|
||||
import { KubeconfigButton } from "./Kubeconfig"
|
||||
import { AxiosResponse } from "axios"
|
||||
|
@ -71,6 +76,58 @@ const separatorStyle = {
|
|||
},
|
||||
}
|
||||
|
||||
const popupStyles = mergeStyleSets({
|
||||
root: {
|
||||
background: 'rgba(0, 0, 0, 0.2)',
|
||||
bottom: '0',
|
||||
left: '0',
|
||||
position: 'fixed',
|
||||
right: '0',
|
||||
top: '0',
|
||||
},
|
||||
content: {
|
||||
background: 'white',
|
||||
left: '50%',
|
||||
maxWidth: '400px',
|
||||
padding: '0 2em 2em',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
});
|
||||
|
||||
const PopupModal = (props: {title: string, text: string, hidePopup: any}) => {
|
||||
return (
|
||||
<>
|
||||
<Layer>
|
||||
<Popup
|
||||
className={popupStyles.root}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
onDismiss={props.hidePopup}
|
||||
enableAriaHiddenSiblings={true}
|
||||
>
|
||||
<FocusTrapZone>
|
||||
<div role="document" className={popupStyles.content}>
|
||||
<h2>{props.title}</h2>
|
||||
<p>
|
||||
{props.text}
|
||||
</p>
|
||||
<DefaultButton onClick={() => {
|
||||
// this is to change the URL in the address bar
|
||||
window.history.replaceState({}, "", "/v2")
|
||||
props.hidePopup()
|
||||
}}>
|
||||
Close
|
||||
</DefaultButton>
|
||||
</div>
|
||||
</FocusTrapZone>
|
||||
</Popup>
|
||||
</Layer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface IClusterListState {
|
||||
columns: IColumn[]
|
||||
items: ICluster[]
|
||||
|
@ -364,9 +421,11 @@ export function ClusterList(props: {
|
|||
sshBox: MutableRefObject<any>
|
||||
setCurrentCluster: any
|
||||
csrfTokenAvailable: string
|
||||
params: any
|
||||
}) {
|
||||
const [data, setData] = useState<any>([])
|
||||
const [error, setError] = useState<AxiosResponse | null>(null)
|
||||
const [isPopupVisible, { setTrue: showPopup, setFalse: hidePopup }] = useBoolean(false);
|
||||
const state = useRef<ClusterListComponent>(null)
|
||||
const [fetching, setFetching] = useState("")
|
||||
|
||||
|
@ -406,6 +465,20 @@ export function ClusterList(props: {
|
|||
setFetching("FETCHING")
|
||||
FetchClusters().then(onData)
|
||||
}
|
||||
|
||||
if (props.params) {
|
||||
const resourceID: string = props.params["resourceid"]
|
||||
const clusterList = data as ICluster[]
|
||||
const currentCluster = clusterList.find((item): item is ICluster => resourceID === item.resourceId)
|
||||
|
||||
if (fetching === "DONE" && !currentCluster) {
|
||||
showPopup()
|
||||
return
|
||||
}
|
||||
|
||||
props.setCurrentCluster(currentCluster)
|
||||
}
|
||||
|
||||
}, [data, fetching, setFetching, props.csrfTokenAvailable])
|
||||
|
||||
const _items: ICommandBarItemProps[] = [
|
||||
|
@ -433,6 +506,9 @@ export function ClusterList(props: {
|
|||
<Separator styles={separatorStyle} />
|
||||
|
||||
{error && errorBar()}
|
||||
|
||||
{isPopupVisible && PopupModal({title: "Resource Not Found", text: "No resource found due to Invalid/Non-existent resource ID in the URL.", hidePopup: hidePopup})}
|
||||
|
||||
<ClusterListComponent
|
||||
items={data}
|
||||
ref={state} // why do we need ref here?
|
||||
|
|
|
@ -14,4 +14,19 @@ mergeStyles({
|
|||
},
|
||||
})
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"))
|
||||
const searchParams = window.location.search
|
||||
|
||||
const getURLParams = () : any => {
|
||||
let urlParams = {}
|
||||
const paramStringFromURL = searchParams.split('?')[1];
|
||||
const paramsArr = paramStringFromURL.split('&');
|
||||
|
||||
for (let i = 0; i < paramsArr.length; i++) {
|
||||
let pair = paramsArr[i].split('=');
|
||||
urlParams = {[pair[0]]: pair[1]}
|
||||
}
|
||||
|
||||
return urlParams
|
||||
}
|
||||
|
||||
ReactDOM.render(<App params={ searchParams ? getURLParams(): undefined} />, document.getElementById("root"))
|
||||
|
|
|
@ -244,4 +244,34 @@ var _ = Describe("Admin Portal E2E Testing", func() {
|
|||
Expect(link.GetAttribute("href")).To(MatchRegexp(`https://([a-z]|[0-9])+\.admin\.aro\.azure\.com`))
|
||||
}
|
||||
})
|
||||
|
||||
It("Should open an error modal for an invalid resource ID parameter in the URL", func() {
|
||||
wd.Get(host + "/v2" + "?resourceid=" + "invalidResourceId")
|
||||
|
||||
wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByCSSSelector, "div[role='document']"), time.Second*3)
|
||||
errorModal, err := wd.FindElement(selenium.ByCSSSelector, "div[role='document']")
|
||||
if err != nil {
|
||||
SaveScreenshotAndExit(wd, err)
|
||||
}
|
||||
|
||||
Expect(errorModal.IsDisplayed()).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Should display the correct cluster detail view for the resource ID parameter in the URL", func() {
|
||||
wd.Get(host + "/v2" + "?resourceid=" + resourceIDFromEnv())
|
||||
wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByID, "ClusterDetailPanel"), time.Second*3)
|
||||
|
||||
detailPanel, err := wd.FindElement(selenium.ByID, "ClusterDetailPanel")
|
||||
if err != nil {
|
||||
SaveScreenshotAndExit(wd, err)
|
||||
}
|
||||
|
||||
Expect(detailPanel.IsDisplayed()).To(BeTrue())
|
||||
|
||||
elem, err := wd.FindElement(selenium.ByCSSSelector, "div[class='titleText-109']")
|
||||
if err != nil {
|
||||
SaveScreenshotAndExit(wd, err)
|
||||
}
|
||||
Expect(elem.Text()).To(Equal(clusterName))
|
||||
})
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче