diff --git a/app-config.yaml b/app-config.yaml index 64c2fa5c4c..3abf08af0e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -322,6 +322,8 @@ catalog: # Backstage example templates - type: file target: ../../plugins/scaffolder-backend/sample-templates/all-templates.yaml + - type: file + target: ../../plugins/scaffolder-backend/sample-templates/big-review-step.yaml rules: - allow: [Template] scaffolder: diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 55c8895dca..b839280cdd 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -14,50 +14,138 @@ * limitations under the License. */ -import React, { Fragment } from 'react'; +import React, { Fragment, ReactElement } from 'react'; import startCase from 'lodash/startCase'; import Typography from '@material-ui/core/Typography'; -import { MetadataTable, MetadataTableItem } from './MetadataTable'; +import { + MetadataList, + MetadataListItem, + MetadataTable, + MetadataTableItem, +} from './MetadataTable'; import { CodeSnippet } from '../CodeSnippet'; import jsyaml from 'js-yaml'; +import { + Theme, + createStyles, + WithStyles, + withStyles, +} from '@material-ui/core/styles'; export type StructuredMetadataTableListClassKey = 'root'; -export type StructuredMetadataTableNestedListClassKey = 'root'; +const listStyle = createStyles({ + root: { + margin: '0 0', + listStyleType: 'none', + }, +}); -function toValue(value: object | Array | boolean | string) { +export type StructuredMetadataTableNestedListClassKey = 'root'; +const nestedListStyle = (theme: Theme) => + createStyles({ + root: { + ...listStyle.root, + paddingLeft: theme.spacing(1), + }, + }); + +interface StyleProps extends WithStyles { + children?: React.ReactNode; +} +// Sub Components +const StyledList = withStyles(listStyle, { + name: 'BackstageStructuredMetadataTableList', +})(({ classes, children }: StyleProps) => ( + {children} +)); +const StyledNestedList = withStyles(nestedListStyle, { + name: 'BackstageStructuredMetadataTableNestedList', +})(({ classes, children }: StyleProps) => ( + {children} +)); + +function renderList(list: Array, options: Options, nested: boolean) { + const values = list.map((item: any, index: number) => ( + + {toValue(item, options, nested)} + + )); + return nested ? ( + {values} + ) : ( + {values} + ); +} + +function renderMap( + map: { [key: string]: any }, + options: Options, + nested: boolean, +) { + const values = Object.keys(map).map(key => { + const value = toValue(map[key], options, true); + return ( + + + {`${options.titleFormat(key)}: `} + + {value} + + ); + }); + + return nested ? ( + {values} + ) : ( + {values} + ); +} + +function toValue( + value: ReactElement | object | Array | boolean, + options: Options, + nested: boolean, +) { if (React.isValidElement(value)) { return {value}; } - if (value !== null && typeof value === 'object') { - return ( - - ); + if (options.nestedValuesAsYaml) { + return ( + + ); + } + if (!Array.isArray(value)) { + return renderMap(value, options, nested); + } + } + + if (Array.isArray(value)) { + return renderList(value, options, nested); } if (typeof value === 'boolean') { return {value ? '✅' : '❌'}; } - return ( {value} ); } -const ItemValue = ({ value }: { value: any }) => ( - {toValue(value)} +const ItemValue = ({ value, options }: { value: any; options: Options }) => ( + {toValue(value, options, false)} ); const TableItem = ({ @@ -71,7 +159,7 @@ const TableItem = ({ }) => { return ( - + ); }; @@ -94,6 +182,7 @@ export interface StructuredMetadataTableProps { * @returns Formatted key */ titleFormat?: (key: string) => string; + nestedValuesAsYaml?: boolean; }; } @@ -101,9 +190,10 @@ type Options = Required>; /** @public */ export function StructuredMetadataTable(props: StructuredMetadataTableProps) { - const { metadata, dense = true, options = {} } = props; + const { metadata, dense = true, options } = props; const metadataItems = mapToItems(metadata, { titleFormat: startCase, + nestedValuesAsYaml: options?.nestedValuesAsYaml ?? false, ...options, }); return {metadataItems}; diff --git a/plugins/kubernetes-cluster/src/components/ClusterOverview/ClusterOverview.tsx b/plugins/kubernetes-cluster/src/components/ClusterOverview/ClusterOverview.tsx index b77b2dff88..af741239e5 100644 --- a/plugins/kubernetes-cluster/src/components/ClusterOverview/ClusterOverview.tsx +++ b/plugins/kubernetes-cluster/src/components/ClusterOverview/ClusterOverview.tsx @@ -58,6 +58,7 @@ export const ClusterOverview = () => { 'OIDC Token Provider': value.oidcTokenProvider ?? 'N/A', 'Dashboard Link': value.dashboardUrl ?? 'N/A', }} + options={{ nestedValuesAsYaml: true }} /> )} diff --git a/plugins/kubernetes-cluster/src/components/Nodes/Nodes.tsx b/plugins/kubernetes-cluster/src/components/Nodes/Nodes.tsx index 0b3f7ddb60..9307b11a6e 100644 --- a/plugins/kubernetes-cluster/src/components/Nodes/Nodes.tsx +++ b/plugins/kubernetes-cluster/src/components/Nodes/Nodes.tsx @@ -50,7 +50,10 @@ const defaultColumns: TableColumn[] = [ Node Info - + Addresses @@ -61,6 +64,7 @@ const defaultColumns: TableColumn[] = [ return accum; }, {} as any) ?? {} } + options={{ nestedValuesAsYaml: true }} /> @@ -72,6 +76,7 @@ const defaultColumns: TableColumn[] = [ return accum; }, {} as any) ?? {} } + options={{ nestedValuesAsYaml: true }} /> diff --git a/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx b/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx index ee484ecda8..d34ba23f7b 100644 --- a/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx +++ b/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx @@ -84,7 +84,10 @@ const DefaultCustomResourceAccordion = ({ {Object.prototype.hasOwnProperty.call(customResource, 'status') && ( - + )} diff --git a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressesAccordions.tsx b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressesAccordions.tsx index 5962e78f59..d2d674042b 100644 --- a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressesAccordions.tsx +++ b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressesAccordions.tsx @@ -65,6 +65,9 @@ const IngressCard = ({ ingress }: IngressCardProps) => { metadata={{ ...ingress.spec, }} + options={{ + nestedValuesAsYaml: true, + }} /> ); }; diff --git a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx index b31d3f9aa7..0a5802ddc8 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx @@ -237,6 +237,7 @@ const KubernetesStructuredMetadataTableDrawerContent = < {!isYaml && ( )} diff --git a/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx b/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx index 1919bc112d..6aed2c75d7 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx @@ -176,6 +176,7 @@ export const ContainerCard: React.FC = ({ containerSpec, containerStatus, )} + options={{ nestedValuesAsYaml: true }} /> {containerMetrics && ( diff --git a/plugins/kubernetes-react/src/components/ServicesAccordions/ServicesAccordions.tsx b/plugins/kubernetes-react/src/components/ServicesAccordions/ServicesAccordions.tsx index 71832fd35c..c44a1d158c 100644 --- a/plugins/kubernetes-react/src/components/ServicesAccordions/ServicesAccordions.tsx +++ b/plugins/kubernetes-react/src/components/ServicesAccordions/ServicesAccordions.tsx @@ -77,6 +77,7 @@ const ServiceCard = ({ service }: ServiceCardProps) => { ports: service.spec?.ports, ...metadata, }} + options={{ nestedValuesAsYaml: true }} /> ); };