Skip to content

Commit

Permalink
Replace OverviewComponent with new implementation
Browse files Browse the repository at this point in the history
- Use FluentUI DetailsList for contents
- Always display all properties, even if value is not present
- Modify E2E test to check each individual property
  • Loading branch information
tsatam committed Sep 21, 2023
1 parent 0c7ebc0 commit 2a1c32a
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 148 deletions.
158 changes: 35 additions & 123 deletions portal/v2/src/ClusterDetailListComponents/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { IShimmerStyles, Shimmer, ShimmerElementType } from '@fluentui/react/lib/Shimmer';
import { Component } from "react"
import { Stack, Text, IStackStyles, IStackItemStyles } from '@fluentui/react';
import { useMemo } from "react"
import { Stack, ShimmeredDetailsList, SelectionMode } from '@fluentui/react';
import { contentStackStylesNormal } from "../App"
import { IClusterDetails } from "../ClusterDetailList"

Expand All @@ -9,59 +8,6 @@ interface OverviewComponentProps {
clusterName: string
}

interface IOverviewComponentState {
item: IClusterDetails
}

export const ShimmerStyle: Partial<IShimmerStyles> = {
root: {
margin: "11px 0"
}
}

export const headShimmerStyle: Partial<IShimmerStyles> = {
root: {
margin: "15px 0"
}
}

export const headerShimmer = [
{ type: ShimmerElementType.line, height: 32, width: '25%' },
]

export const rowShimmer = [
{ type: ShimmerElementType.line, height: 18, width: '75%' },
]

export const KeyColumnStyle: Partial<IStackStyles> = {
root: {
paddingTop: 10,
paddingRight: 15,
}
}

export const ValueColumnStyle: Partial<IStackStyles> = {
root: {
paddingTop: 10,
}
}

export const KeyStyle: IStackItemStyles = {
root: {
fontStyle: "bold",
alignSelf: "flex-start",
fontVariantAlternates: "bold",
color: "grey",
paddingBottom: 10
}
}

export const ValueStyle: IStackItemStyles = {
root: {
paddingBottom: 10
}
}

const clusterDetailHeadings : IClusterDetails = {
apiServerVisibility: 'ApiServer Visibility',
apiServerURL: 'ApiServer URL',
Expand All @@ -82,71 +28,37 @@ const clusterDetailHeadings : IClusterDetails = {
version: 'Version',
installStatus: 'Installation Status'
}

function ClusterDetailCell(
value: any,
): any {
if (typeof (value.value) == typeof (" ")) {
return <Stack.Item id="ClusterDetailCell" styles={value.style}>
<Text styles={value.style} variant={'medium'}>{value.value}</Text>
</Stack.Item>
}
}

export class OverviewComponent extends Component<OverviewComponentProps, IOverviewComponentState> {

constructor(props: OverviewComponentProps | Readonly<OverviewComponentProps>) {
super(props);
}

public render() {
const headerEntries = Object.entries(clusterDetailHeadings)
const filteredHeaders: Array<[string, any]> = []
if (this.props.item.length != 0) {
headerEntries.filter((element: [string, any]) => {
if (this.props.item[element[0]] != null &&
this.props.item[element[0]].toString().length > 0) {
filteredHeaders.push(element)
}
})
return (
<Stack styles={contentStackStylesNormal}>
<Stack horizontal>
<Stack id="Headers" styles={KeyColumnStyle}>
{filteredHeaders.map((value: [string, any], index: number) => (
<ClusterDetailCell style={KeyStyle} key={index} value={value[1]} />
)
)}
</Stack>

<Stack id="Colons" styles={KeyColumnStyle}>
{Array(filteredHeaders.length).fill(':').map((value: [string], index: number) => (
<ClusterDetailCell style={KeyStyle} key={index} value={value} />
)
)}
</Stack>

<Stack id="Values" styles={ValueColumnStyle}>
{filteredHeaders.map((value: [string, any], index: number) => (
<ClusterDetailCell style={ValueStyle}
key={index}
value={this.props.item[value[0]]} />
)
)}
</Stack>
</Stack>
</Stack>
);
} else {
return (
<Stack>
<Shimmer styles={headShimmerStyle} shimmerElements={headerShimmer} width="25%"></Shimmer>
{headerEntries.map(header => (
<Shimmer key={header[0]} styles={ShimmerStyle} shimmerElements={rowShimmer} width="75%"></Shimmer>
)
)}
</Stack>
)
}
}
}
interface ClusterDetailItem {
key: number,
name: string,
value: string,
}

export function OverviewComponent(props: OverviewComponentProps) {
const items: ClusterDetailItem[] = useMemo(() =>
Object.entries(clusterDetailHeadings)
.map(([property, heading], index) =>
({key: index, name: heading, value: props.item[property]})
),
[props.item])

return (
<Stack styles={contentStackStylesNormal}>
<Stack horizontal>
<ShimmeredDetailsList
compact={true}
className="clusterOverviewList"
items={items}
columns={[
{key: 'name', name: 'Name', fieldName: 'name', minWidth: 200, isRowHeader: true},
{key: 'value', name: 'Value', fieldName: 'value', minWidth: 300, onRender: (item) => (<span>{item.value || '-'}</span>)}
]}
enableShimmer={props.item.length == 0}
selectionMode={SelectionMode.none}
isHeaderVisible={false}
/>
</Stack>
</Stack>
);
}
59 changes: 34 additions & 25 deletions test/e2e/admin_portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ var _ = Describe("Admin Portal E2E Testing", func() {
})

It("Should be able to populate cluster info panel correctly", func() {
const CLUSTER_INFO_HEADINGS = 10

err := wd.Wait(conditions.ElementIsLocated(selenium.ByCSSSelector, "div[data-automation-key='name']"))
Expect(err).ToNot(HaveOccurred())

Expand All @@ -89,34 +87,45 @@ var _ = Describe("Admin Portal E2E Testing", func() {
err = cluster.Click()
Expect(err).ToNot(HaveOccurred())

err = wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByID, "ClusterDetailCell"), 2*time.Minute)
err = wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByClassName, "ClusterOverviewList"), 2*time.Minute)
Expect(err).ToNot(HaveOccurred())

list, err := wd.FindElement(selenium.ByClassName, "ClusterOverviewList")
Expect(err).ToNot(HaveOccurred())

expectedProperties := []string{
"ApiServer Visibility",
"ApiServer URL",
"Architecture Version",
"Console Link",
"Created At",
"Created By",
"Failed Provisioning State",
"Infra Id",
"Last Admin Update Error",
"Last Modified At",
"Last Modified By",
"Last Provisioning State",
"Location",
"Name",
"Provisioning State",
"Resource Id",
"Version",
"Installation Status",
}

Eventually(func(g Gomega) {
panelSpans, err := wd.FindElements(selenium.ByID, "ClusterDetailCell")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(panelSpans)).To(Equal(CLUSTER_INFO_HEADINGS * 3))

panelFields := panelSpans[0 : CLUSTER_INFO_HEADINGS-1]
panelColons := panelSpans[CLUSTER_INFO_HEADINGS : CLUSTER_INFO_HEADINGS*2-1]
panelValues := panelSpans[CLUSTER_INFO_HEADINGS*2 : len(panelSpans)-1]

for _, panelField := range panelFields {
panelText, err := panelField.Text()
g.Expect(err).ToNot(HaveOccurred())
g.Expect(panelText).To(Not(Equal("")))
}
for i, wantName := range expectedProperties {
cell, err := list.FindElement(selenium.ByCSSSelector, fmt.Sprintf(".ms-List-cell[data-list-index='%d']", i))
Expect(err).ToNot(HaveOccurred())

for _, panelField := range panelColons {
panelText, err := panelField.Text()
g.Expect(err).ToNot(HaveOccurred())
g.Expect(panelText).To(Equal(":"))
}
name, err := cell.FindElement(selenium.ByCSSSelector, "[data-automation-key='name']")
Expect(err).ToNot(HaveOccurred())
Expect(name.Text()).To(Equal(wantName))

for _, panelField := range panelValues {
panelText, err := panelField.Text()
g.Expect(err).ToNot(HaveOccurred())
g.Expect(panelText).To(Not(Equal("")))
value, err := cell.FindElement(selenium.ByCSSSelector, "[data-automation-key='value']")
Expect(err).ToNot(HaveOccurred())
Expect(value.Text()).To(Not(Equal("")))
}
}).WithTimeout(time.Minute).WithPolling(time.Second).Should(Succeed())
})
Expand Down

0 comments on commit 2a1c32a

Please sign in to comment.