Merge branch 'master' into feat-urlreader-issearchurl
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-cluster': patch
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
---
|
||||
|
||||
Improved rendering of Kubernetes resources' metadata.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Added `nestedValuesAsYaml` option to `StructuredMetadataTable` to render data as yaml.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Fixed an issue where invalid expressions or non-object values in `step.each` caused an error.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Add missing route ref to the `/alpha` entity content extension.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
The log message written when plugins fail to initialize now includes the error that caused the plugin startup to fail.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
'@backstage/plugin-scaffolder': minor
|
||||
---
|
||||
|
||||
Updating the `TaskLogStream` to take up all space in a running task, and also show the last line of the log by default
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Fixed the file path pattern of many static assets output as part of the frontend build process, where there was an extra `.` before the extension, leading to names like `image-af7946b..png`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-ldap': patch
|
||||
---
|
||||
|
||||
Make `ldapOrg` config key optional
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Allow route references to be installed in multiple app instances as long as their name is the same.
|
||||
@@ -167,9 +167,7 @@ Here's a view when buttons are responsive.
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonResponsive />}
|
||||
code={`<Button
|
||||
variant={{ xs: 'primary', sm: 'secondary', md: 'tertiary' }}
|
||||
size={{ xs: 'small', sm: 'medium' }}>
|
||||
Button
|
||||
</Button>`}
|
||||
code={`<Button variant={{ initial: 'primary', lg: 'secondary' }}>
|
||||
Responsive Button
|
||||
</Button>`}
|
||||
/>
|
||||
|
||||
@@ -93,5 +93,7 @@ on the screen size.
|
||||
py={4}
|
||||
open
|
||||
preview={<HeadingResponsive />}
|
||||
code={`<Heading variant={{ xs: 'title1', md: 'title2' }}>Responsive</Heading>`}
|
||||
code={`<Heading variant={{ initial: 'title2', lg: 'title1' }}>
|
||||
Responsive heading
|
||||
</Heading>`}
|
||||
/>
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Table
|
||||
# Table (Coming soon)
|
||||
|
||||
@@ -137,5 +137,7 @@ on the screen size.
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextResponsive />}
|
||||
code={`<Text variant={{ xs: 'label', md: 'body' }}>Responsive</Text>`}
|
||||
code={`<Text variant={{ initial: 'body', lg: 'subtitle' }}>
|
||||
Responsive text
|
||||
</Text>`}
|
||||
/>
|
||||
|
||||
@@ -1,67 +1,70 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import * as Table from '@/components/Table';
|
||||
import { Chip } from '@/components/Chip';
|
||||
import { customTheme } from '@/snippets/_snippets';
|
||||
|
||||
# Theming
|
||||
|
||||
Backstage ships with a default theme with a light and dark mode variant. The
|
||||
themes are provided as a part of the `@backstage/canon` package, which also
|
||||
includes utilities for customizing the default theme, or creating completely
|
||||
new themes.
|
||||
Canon's theming is built entirely on CSS, without relying on any CSS-in-JS libraries.
|
||||
At its core, it provides a solid default theme that is easily customizable using a
|
||||
comprehensive set of CSS variables. Additionally, it enables anyone to adapt the design
|
||||
to their specific needs. Each component comes with fixed class names, making customization
|
||||
even more straightforward.
|
||||
|
||||
## Light & Dark modes
|
||||
|
||||
By default we are supporting both light and dark modes. Each user can opt to
|
||||
choose what theme they want to use or to use their system decide what theme to
|
||||
use. If you want to create your own theme, you will have to set both light and
|
||||
dark themes following the instructions below. If you only set one of them, the
|
||||
other mode will fallback to the default theme.
|
||||
By default, Canon supports both light and dark modes using the `data-theme` attribute.
|
||||
The light theme is applied by default if no `data-theme` attribute is specified. To create
|
||||
a custom theme, you'll need to define both light and dark modes as outlined below. If
|
||||
only one mode is defined, the other will fall back to the default theme.
|
||||
|
||||
## How to create your own theme
|
||||
|
||||
To create your own theme, you will have to define the variables below. To do
|
||||
that, create a theme.css file and import it in your application. Here's an
|
||||
example below on how to set your light and dark mode.
|
||||
In our [started guide](/), we ask you to import two css files. The `core.css` file includes
|
||||
the default set of variables. We recommend to keep this file in place and add your own theme
|
||||
on top of it. `core.css` also include an opinionated reset. If you decided to remove `core.css`
|
||||
you will have to provide your own reset css.
|
||||
|
||||
<CodeBlock
|
||||
lang="css"
|
||||
code={`/** Light theme **/
|
||||
[data-theme='light'] {
|
||||
--canon-bg-accent: #1ed760;
|
||||
--canon-bg: #fff;
|
||||
--canon-bg-elevated: #f5f5f5;
|
||||
--canon-bg: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #f0f0f0;
|
||||
--canon-text-secondary: #666;
|
||||
--canon-font-regular: 'Geist', serif;
|
||||
--canon-font-mono: 'Monospace', monospace;
|
||||
/* ... other values */
|
||||
}
|
||||
|
||||
/** Dark theme **/
|
||||
[data-theme='dark'] {
|
||||
--canon-bg-accent: #1ed760;
|
||||
--canon-bg: #000;
|
||||
--canon-bg-elevated: #f5f5f5;
|
||||
--canon-bg: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #fff;
|
||||
--canon-text-secondary: #666;
|
||||
--canon-font-regular: 'Geist', serif;
|
||||
--canon-font-mono: 'Monospace', monospace;
|
||||
/* ... other values */
|
||||
}
|
||||
`}
|
||||
/>
|
||||
Here's an example of how your theme.css file should look like:
|
||||
|
||||
## Colors
|
||||
<CodeBlock lang="css" code={customTheme} />
|
||||
|
||||
We provide a set of generic colours tokens that we use across Canon. By
|
||||
changing these colours you can easily change the look and feel of your
|
||||
application to match your brand.
|
||||
## Available CSS variables
|
||||
|
||||
### Core background colors
|
||||
|
||||
These colors are used for the background of your application. We are mostly using for now a
|
||||
single elevated background for panels. `--canon-bg` should mostly use as the main background
|
||||
color of your app.
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The background color of your Backstage instance.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-elevated</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Use for any panels or elevated surfaces.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
### Component background colors
|
||||
|
||||
These colors are meants for interactive components like buttons, callout avatar, chips, ...
|
||||
We have two main level of background you can customise. The accent one is mostly used as a primary option
|
||||
for interactive components. The tint one is used for secondary components like callouts, avatars, ...
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
@@ -75,54 +78,254 @@ application to match your brand.
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-accent</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The accent color for the theme.</Table.Cell>
|
||||
<Table.Cell>Used mostly for primary interactive components.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
<Chip head>--canon-bg-accent-hover</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are hovered.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The background color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-elevated</Chip>
|
||||
<Chip head>--canon-bg-accent-focus</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are in focus.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The first surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
<Chip head>--canon-bg-accent-disabled</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are disabled.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The second surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline</Chip>
|
||||
<Chip head>--canon-bg-tint</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The outline color for the theme.</Table.Cell>
|
||||
<Table.Cell>Used for secondary interactive components.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline-focus</Chip>
|
||||
<Chip head>--canon-bg-tint-hover</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are hovered.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The outline focus color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-text-primary</Chip>
|
||||
<Chip head>--canon-bg-tint-focus</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are active.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The primary text color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-text-secondary</Chip>
|
||||
<Chip head>--canon-bg-tint-disabled</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used for interactive components when they are disabled.
|
||||
</Table.Cell>
|
||||
<Table.Cell>The secondary text color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
## Typography
|
||||
### Status background colors
|
||||
|
||||
These backgrounds are intended for non-interactive elements to visually convey a specific state.
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-danger</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Used to show errors informations.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-warning</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Used to show warnings informations.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg-success</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Used to show success informations.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
### Foreground colors
|
||||
|
||||
Foreground colours are meant to work in pair with a background colours. Typeically this would work
|
||||
for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors
|
||||
are prefixed with `fg` to make it easier to identify.
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-text-primary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-text-secondary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-link</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-link-hover</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-accent</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-accent`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-tint</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-tint`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-danger</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-danger`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-warning</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-warning`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-success</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-success`.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
### Border colors
|
||||
|
||||
These border colors are mostly meant to be used as borders on top of any components with
|
||||
low contrast to help as a separator with the different background colors.
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-elevated`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-hover</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used when the component is interactive and hovered.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-focus</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used when the component is interactive and focused.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-tint</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-tint`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-tint-hover</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Used when the component is hovered.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-tint-focus</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Used when the component is in focus.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-danger</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-danger`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-warning</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-warning`.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-success</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>It should be used on top of `--canon-bg-success`.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
## Font families
|
||||
|
||||
We have two fonts that we use across Canon. The first one is the sans-serif
|
||||
font that we use for the body of the application. The second one is the
|
||||
@@ -151,12 +354,9 @@ monospace font that we use for code blocks and tables.
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
## Spacing
|
||||
## Font weights
|
||||
|
||||
Our default spacing system is made to work in most scenarios. We have 7 scale
|
||||
values from `xxs` to `xxl`. We use the values on padding and margin in our
|
||||
layout components mostly. If you prefer to use a different spacing system, you
|
||||
can do that by changing the values below.
|
||||
We have two font weights that we use across Canon. Regular or Bold
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
@@ -168,53 +368,184 @@ can do that by changing the values below.
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-unit</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The base unit for the spacing system. Default value is `1em`
|
||||
<Chip head>--canon-font-weight-regular</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The regular font weight for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xxs</Chip>
|
||||
<Chip head>--canon-font-weight-bold</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xs</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.5 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-sm</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.75 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-md</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `1.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-lg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `2 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `3.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xxl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `5.25 x space unit`</Table.Cell>
|
||||
<Table.Cell>The bold font weight for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
## Spacing
|
||||
|
||||
We built a spacing system based on a single value `--canon-space`. This value is
|
||||
used to calculate the spacing for all the components. By default if you would like to
|
||||
increase or decrease the spacing between your components you can do it simply by updating
|
||||
`--canon-space` and it will apply to all spacing values.
|
||||
|
||||
`--canon-space` is not used directly in any components but serve as an easy way to
|
||||
calculate the other values.
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The base unit for the spacing system. Default value is `0.25rem`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
Below is the list of all spacing values you can use in your application. We use these
|
||||
tokens for pretty much each spacing properties like padding, margin, gaps, ...
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-0_5</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 0.5.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-1</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`).</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-1_5</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 1.5.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-2</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 2.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-3</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 3.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-4</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 4.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-5</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 5.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-6</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 6.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-7</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 7.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-8</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 8.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-9</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 9.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-10</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 10.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-11</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 11.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-12</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 12.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-13</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 13.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-14</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Base unit (`--canon-space`) times 14.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
### Radius (Work in progress)
|
||||
|
||||
Our radius system is a little bit different from spacing as we use a factor to calculate the radius values.
|
||||
Under the hood different components uses different radius values. For example, button can be set to have full radius
|
||||
while a checkbox will have to remain a square.
|
||||
|
||||
Here are the different radius factor we recommend to use
|
||||
|
||||
- None: `0`
|
||||
- Small: `0.75`
|
||||
- Medium: `1`
|
||||
- Large: `1.5`
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-radius-factor</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>By default the value is set to medium `1`.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
@@ -2,7 +2,7 @@ body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
--docs-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.page p {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 0;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
padding: 12px 20px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
}
|
||||
|
||||
.code {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: var(--canon-text-primary);
|
||||
fill: var(--canon-fg-text-primary);
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -37,6 +37,6 @@
|
||||
border-radius: 100px;
|
||||
margin-top: var(--canon-space-3);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
gap: var(--canon-space-2);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
left: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-right: 1px solid var(--canon-border);
|
||||
padding-left: 20px;
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
.logo path {
|
||||
fill: var(--canon-text-primary);
|
||||
fill: var(--canon-fg-text-primary);
|
||||
}
|
||||
|
||||
.menu {
|
||||
@@ -75,11 +75,11 @@
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
|
||||
.lineStatus {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
& p {
|
||||
margin: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ export const Tab = (props: React.ComponentProps<typeof TabsPrimitive.Tab>) => (
|
||||
{...rest}
|
||||
style={{
|
||||
color: state.selected
|
||||
? 'var(--canon-text-primary)'
|
||||
: 'var(--canon-text-secondary)',
|
||||
? 'var(--canon-fg-text-primary)'
|
||||
: 'var(--canon-fg-text-secondary)',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
left: var(--active-tab-left);
|
||||
width: var(--active-tab-width);
|
||||
height: 1px;
|
||||
background-color: var(--canon-text-primary);
|
||||
background-color: var(--canon-fg-text-primary);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -27,21 +27,21 @@
|
||||
all: unset;
|
||||
height: 60px;
|
||||
font-family: var(--docs-font);
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
|
||||
& p {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
.tab p {
|
||||
color: var(--canon-text-secondary) !important;
|
||||
color: var(--canon-fg-text-secondary) !important;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
@@ -72,7 +72,7 @@
|
||||
width: var(--active-tab-width);
|
||||
height: 1px;
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--canon-text-primary);
|
||||
background-color: var(--canon-fg-text-primary);
|
||||
transition-property: translate, width, background-color;
|
||||
transition-duration: 200ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
@@ -29,23 +29,23 @@
|
||||
outline: 0;
|
||||
background: none;
|
||||
appearance: none;
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
user-select: none;
|
||||
height: 2rem;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
|
||||
& p {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
.tab p {
|
||||
color: var(--canon-text-secondary) !important;
|
||||
color: var(--canon-fg-text-secondary) !important;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
|
||||
@@ -62,10 +62,13 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
</p>
|
||||
),
|
||||
a: ({ children, href }) => (
|
||||
<a href={href} style={{ color: 'var(--canon-text-primary)' }}>
|
||||
<a href={href} style={{ color: 'var(--canon-fg-text-primary)' }}>
|
||||
{children as ReactNode}
|
||||
</a>
|
||||
),
|
||||
li: ({ children }) => (
|
||||
<li style={{ marginBottom: '0.5rem' }}>{children as ReactNode}</li>
|
||||
),
|
||||
pre: ({ children }) => {
|
||||
const codeContent = React.isValidElement(children)
|
||||
? (children.props as { children: string }).children
|
||||
@@ -80,7 +83,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
backgroundColor: 'var(--canon-bg-elevated)',
|
||||
padding: '0.2rem 0.375rem',
|
||||
borderRadius: '0.25rem',
|
||||
color: 'var(--canon-text-secondary)',
|
||||
color: 'var(--canon-fg-text-secondary)',
|
||||
border: '1px solid var(--canon-border)',
|
||||
fontSize: '0.875rem',
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
// Sometimes codes are not formatted correctly in the docs, so we need to use snippets
|
||||
|
||||
export const customTheme = `:root {
|
||||
--canon-font-regular: system-ui;
|
||||
--canon-font-weight-regular: 400;
|
||||
--canon-font-weight-bold: 600;
|
||||
--canon-bg: #f8f8f8;
|
||||
--canon-bg-elevated: #fff;
|
||||
/* ... other CSS variables */
|
||||
|
||||
/* Add your custom components styles here */
|
||||
.canon-Button {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--canon-font-regular: system-ui;
|
||||
--canon-font-weight-regular: 400;
|
||||
--canon-font-weight-bold: 600;
|
||||
--canon-bg: #f8f8f8;
|
||||
--canon-bg-elevated: #fff;
|
||||
/* ... other CSS variables */
|
||||
|
||||
/* Add your custom components styles here */
|
||||
.canon-Button {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const grid = `import { Grid } from '@backstage/canon';
|
||||
|
||||
<Grid>
|
||||
|
||||
@@ -62,8 +62,11 @@ export const ButtonDisabled = () => {
|
||||
};
|
||||
|
||||
export const ButtonResponsive = () => {
|
||||
// TODO: Add responsive button
|
||||
return null;
|
||||
return (
|
||||
<Button variant={{ initial: 'primary', lg: 'secondary' }}>
|
||||
Responsive Button
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const ButtonPlayground = () => {
|
||||
|
||||
@@ -19,10 +19,9 @@ export const HeadingAllVariants = () => {
|
||||
};
|
||||
|
||||
export const HeadingResponsive = () => {
|
||||
return null;
|
||||
return (
|
||||
<Stack>
|
||||
<Heading variant={{ xs: 'title4', md: 'title2' }}>
|
||||
<Heading variant={{ initial: 'title2', lg: 'title1' }}>
|
||||
Responsive heading
|
||||
</Heading>
|
||||
</Stack>
|
||||
|
||||
@@ -57,8 +57,9 @@ export const TextAllWeights = () => {
|
||||
};
|
||||
|
||||
export const TextResponsive = () => {
|
||||
// TODO: Add responsive text
|
||||
return null;
|
||||
return (
|
||||
<Text variant={{ initial: 'body', lg: 'subtitle' }}>Responsive text</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export const TextPlayground = () => {
|
||||
|
||||
@@ -925,11 +925,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^20":
|
||||
version: 20.17.12
|
||||
resolution: "@types/node@npm:20.17.12"
|
||||
version: 20.17.16
|
||||
resolution: "@types/node@npm:20.17.16"
|
||||
dependencies:
|
||||
undici-types: ~6.19.2
|
||||
checksum: 0c0dbeb4e1480a23071ec38e97bb3d776e0ae9828174bf45f9edcf277caa955945cb10d31d84f7a73c66aaf92ae2e022be6331bd00a4bed1f2ad9639a411d17e
|
||||
checksum: dd19c04c592b602f20448187b4b6ab0293f22173a8bbe53aa3b559188f078b5261d748a107b84ee0580e2074cda760049b13d6e210f9850039bead01cb666a3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5368,22 +5368,22 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:^5":
|
||||
version: 5.7.2
|
||||
resolution: "typescript@npm:5.7.2"
|
||||
version: 5.7.3
|
||||
resolution: "typescript@npm:5.7.3"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: b55300c4cefee8ee380d14fa9359ccb41ff8b54c719f6bc49b424899d662a5ce62ece390ce769568c7f4d14af844085255e63788740084444eb12ef423b13433
|
||||
checksum: 6c38b1e989918e576f0307e6ee013522ea480dfce5f3ca85c9b2d8adb1edeffd37f4f30cd68de0c38a44563d12ba922bdb7e36aa2dac9c51de5d561e6e9a2e9c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@^5#~builtin<compat/typescript>":
|
||||
version: 5.7.2
|
||||
resolution: "typescript@patch:typescript@npm%3A5.7.2#~builtin<compat/typescript>::version=5.7.2&hash=5adc0c"
|
||||
version: 5.7.3
|
||||
resolution: "typescript@patch:typescript@npm%3A5.7.3#~builtin<compat/typescript>::version=5.7.3&hash=5adc0c"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 803430c6da2ba73c25a21880d8d4f08a56d9d2444e6db2ea949ac4abceeece8e4a442b7b9b585db7d8a0b47ebda2060e45fe8ee8b8aca23e27ec1d4844987ee6
|
||||
checksum: 633cd749d6cd7bc842c6b6245847173bba99742a60776fae3c0fbcc0d1733cd51a733995e5f4dadd8afb0e64e57d3c7dbbeae953a072ee303940eca69e22f311
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -515,6 +515,7 @@ The Backstage tooling supports [ECMAScript modules (ESM)](https://nodejs.org/doc
|
||||
|
||||
- To enable support for native ESM in tests, you need to run the tests with the `--experimental-vm-modules` flag enabled, typically via `NODE_OPTIONS='--experimental-vm-modules'`.
|
||||
- Declaring a package as `"type": "module"` in `package.json` is supported, but in tests it will cause all local transitive dependencies to also be treated as ESM, regardless of whether they declare `"type": "module"` or not.
|
||||
- When running tests with coverage enabled the default `babel` coverage provider can mess with the hoisting of named exports. This can be worked around by using the `v8` provider instead by setting `"coverageProvider": "v8"` in the Jest configuration, although note that the `v8` provider is a fair bit slower than the `babel` one.
|
||||
- Node.js has an [ESM interoperability layer with CommonJS](https://nodejs.org/docs/latest-v22.x/api/esm.html#interoperability-with-commonjs) that allows for imports from ESM to identify named exports in CommonJS packages. This interoperability layer is **only** enabled when importing packages with a `.cts` or `.cjs` extension. This is because the interoperability layer is not fully compatible with the NPM ecosystem, and would break package if it was enabled for `.js` files.
|
||||
- Dynamic imports of CommonJS packages will vary in shape depending on the runtime, i.e. test vs local development, etc. It is therefore recommended to avoid dynamic imports of CommonJS packages and instead use `require`, or to use the explicit CommonJS extensions as mentioned above. If you do need to dynamically import CommonJS packages, avoid using `default` exports, as the shape of them vary across different environments and you would otherwise need to manually unwrap the import based on the shape of the module object.
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import type {
|
||||
} from '../../../backend-plugin-api/src/wiring/types';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';
|
||||
import { ForwardedError, ConflictError } from '@backstage/errors';
|
||||
import { ForwardedError, ConflictError, assertError } from '@backstage/errors';
|
||||
import {
|
||||
instanceMetadataServiceRef,
|
||||
featureDiscoveryServiceRef,
|
||||
@@ -405,8 +405,9 @@ export class BackendInitializer {
|
||||
// Once the plugin and all modules have been initialized, we can signal that the plugin has stared up successfully
|
||||
const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);
|
||||
await lifecycleService.startup();
|
||||
} catch (error) {
|
||||
initLogger.onPluginFailed(pluginId);
|
||||
} catch (error: unknown) {
|
||||
assertError(error);
|
||||
initLogger.onPluginFailed(pluginId, error);
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -27,7 +27,7 @@ export function createInitializationLogger(
|
||||
rootLogger?: RootLoggerService,
|
||||
): {
|
||||
onPluginStarted(pluginId: string): void;
|
||||
onPluginFailed(pluginId: string): void;
|
||||
onPluginFailed(pluginId: string, error: Error): void;
|
||||
onAllStarted(): void;
|
||||
} {
|
||||
const logger = rootLogger?.child({ type: 'initialization' });
|
||||
@@ -68,14 +68,15 @@ export function createInitializationLogger(
|
||||
starting.delete(pluginId);
|
||||
started.add(pluginId);
|
||||
},
|
||||
onPluginFailed(pluginId: string) {
|
||||
onPluginFailed(pluginId: string, error: Error) {
|
||||
starting.delete(pluginId);
|
||||
const status =
|
||||
starting.size > 0
|
||||
? `, waiting for ${starting.size} other plugins to finish before shutting down the process`
|
||||
: '';
|
||||
logger?.error(
|
||||
`Plugin '${pluginId}' threw an error during startup${status}`,
|
||||
`Plugin '${pluginId}' threw an error during startup${status}.`,
|
||||
error,
|
||||
);
|
||||
},
|
||||
onAllStarted() {
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
--canon-bg: #f4f4f4;
|
||||
|
||||
/* Text colors */
|
||||
--canon-text-primary: #000;
|
||||
--canon-fg-text-primary: #000;
|
||||
--canon-fg-accent: #fff;
|
||||
--canon-text-secondary: #646464;
|
||||
--canon-fg-text-secondary: #646464;
|
||||
}
|
||||
|
||||
[data-theme-name='legacy'][data-theme='dark'] {
|
||||
@@ -82,7 +82,7 @@
|
||||
--canon-fg-danger: #f50000;
|
||||
|
||||
/* Text colors */
|
||||
--canon-text-primary: #fff;
|
||||
--canon-fg-text-primary: #fff;
|
||||
--canon-fg-accent: #000;
|
||||
--canon-text-secondary: #b3b3b3;
|
||||
--canon-fg-text-secondary: #b3b3b3;
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ export const Border: Story = {
|
||||
args: {
|
||||
style: {
|
||||
background: 'var(--canon-bg-elevated)',
|
||||
color: 'var(--canon-text-primary)',
|
||||
color: 'var(--canon-fg-text-primary)',
|
||||
padding: '4px 8px',
|
||||
width: '80px',
|
||||
height: '32px',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.canon-Box {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-focus);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-Button--variant-secondary {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
@@ -52,10 +52,10 @@
|
||||
|
||||
.canon-Button--variant-tertiary {
|
||||
background-color: transparent;
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
gap: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-xs);
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
.canon-FieldLabel {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
margin-bottom: var(--canon-space-1_5);
|
||||
}
|
||||
|
||||
.canon-FieldDescription {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
margin: 0;
|
||||
padding-top: var(--canon-space-1_5);
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
.canon-FieldValidity {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
margin: 0;
|
||||
padding-top: var(--canon-space-1_5);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
.canon-Heading {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
line-height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
background-color: var(--canon-bg-elevated);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.canon-Input::placeholder {
|
||||
color: var(--canon-text-secondary);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
}
|
||||
|
||||
.canon-Input:hover {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
.canon-Text {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
color: var(--canon-fg-text-primary);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
--canon-bg: #f8f8f8;
|
||||
--canon-bg-elevated: #fff;
|
||||
--canon-bg-accent: #000;
|
||||
--canon-bg-accent-active: #000;
|
||||
--canon-bg-accent-focus: #000;
|
||||
--canon-bg-accent-disabled: #000;
|
||||
--canon-bg-accent-hover: #000;
|
||||
--canon-bg-tint: #edf2fe;
|
||||
@@ -95,14 +95,15 @@
|
||||
--canon-border-hover: rgba(0, 0, 0, 0.2);
|
||||
--canon-border-success: #008000;
|
||||
--canon-border-warning: #e36d05;
|
||||
--canon-fg-text-primary: #000;
|
||||
--canon-fg-text-secondary: #646464;
|
||||
--canon-fg-link: #fff;
|
||||
--canon-fg-link-hover: #fff;
|
||||
--canon-fg-accent: #fff;
|
||||
--canon-fg-tint: #000;
|
||||
--canon-fg-danger: #e22b2b;
|
||||
--canon-fg-success: #008000;
|
||||
--canon-fg-warning: #e36d05;
|
||||
--canon-text-link: #fff;
|
||||
--canon-text-link-hover: #fff;
|
||||
--canon-text-primary: #000;
|
||||
--canon-text-secondary: #646464;
|
||||
--canon-fg-success: #008000;
|
||||
}
|
||||
|
||||
/* Dark theme tokens */
|
||||
@@ -110,7 +111,7 @@
|
||||
--canon-bg: #f8f8f8;
|
||||
--canon-bg-elevated: #fff;
|
||||
--canon-bg-accent: #000;
|
||||
--canon-bg-accent-active: #000;
|
||||
--canon-bg-accent-focus: #000;
|
||||
--canon-bg-accent-disabled: #000;
|
||||
--canon-bg-accent-hover: #000;
|
||||
--canon-bg-tint: #edf2fe;
|
||||
@@ -134,8 +135,8 @@
|
||||
--canon-fg-danger: #e22b2b;
|
||||
--canon-fg-success: #008000;
|
||||
--canon-fg-warning: #e36d05;
|
||||
--canon-text-link: #fff;
|
||||
--canon-text-link-hover: #fff;
|
||||
--canon-text-primary: #000;
|
||||
--canon-text-secondary: #646464;
|
||||
--canon-fg-link: #fff;
|
||||
--canon-fg-link-hover: #fff;
|
||||
--canon-fg-text-primary: #000;
|
||||
--canon-fg-text-secondary: #646464;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ export const transforms = (options: TransformOptions): Transforms => {
|
||||
],
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'static/[name].[hash:8].[ext]',
|
||||
filename: 'static/[name].[hash:8][ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"d3-shape": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"dagre": "^0.8.5",
|
||||
"js-yaml": "^4.1.0",
|
||||
"linkify-react": "4.1.3",
|
||||
"linkifyjs": "4.1.3",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -1307,6 +1307,7 @@ export interface StructuredMetadataTableProps {
|
||||
// (undocumented)
|
||||
options?: {
|
||||
titleFormat?: (key: string) => string;
|
||||
nestedValuesAsYaml?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ import Box from '@material-ui/core/Box';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import CopyIcon from '@material-ui/icons/FileCopy';
|
||||
import classnames from 'classnames';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
|
||||
import { AnsiProcessor } from './AnsiProcessor';
|
||||
import { AnsiLine, AnsiProcessor } from './AnsiProcessor';
|
||||
import { LogLine } from './LogLine';
|
||||
import { LogViewerControls } from './LogViewerControls';
|
||||
import { HEADER_SIZE, useStyles } from './styles';
|
||||
@@ -37,7 +37,9 @@ export interface RealLogViewerProps {
|
||||
|
||||
export function RealLogViewer(props: RealLogViewerProps) {
|
||||
const classes = useStyles({ classes: props.classes });
|
||||
const listRef = useRef<FixedSizeList | null>(null);
|
||||
const [fixedListInstance, setFixedListInstance] = useState<FixedSizeList<
|
||||
AnsiLine[]
|
||||
> | null>(null);
|
||||
|
||||
// The processor keeps state that optimizes appending to the text
|
||||
const processor = useMemo(() => new AnsiProcessor(), []);
|
||||
@@ -48,10 +50,21 @@ export function RealLogViewer(props: RealLogViewerProps) {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (search.resultLine !== undefined && listRef.current) {
|
||||
listRef.current.scrollToItem(search.resultLine - 1, 'center');
|
||||
if (fixedListInstance) {
|
||||
fixedListInstance.scrollToItem(lines.length - 1, 'end');
|
||||
}
|
||||
}, [search.resultLine]);
|
||||
}, [fixedListInstance, lines]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fixedListInstance) {
|
||||
return;
|
||||
}
|
||||
if (search.resultLine) {
|
||||
fixedListInstance.scrollToItem(search.resultLine - 1, 'center');
|
||||
} else {
|
||||
fixedListInstance.scrollToItem(lines.length - 1, 'end');
|
||||
}
|
||||
}, [fixedListInstance, search.resultLine, lines]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
@@ -76,7 +89,9 @@ export function RealLogViewer(props: RealLogViewerProps) {
|
||||
<LogViewerControls {...search} />
|
||||
</Box>
|
||||
<FixedSizeList
|
||||
ref={listRef}
|
||||
ref={(instance: FixedSizeList<AnsiLine[]>) => {
|
||||
setFixedListInstance(instance);
|
||||
}}
|
||||
className={classes.log}
|
||||
height={(height || 480) - HEADER_SIZE}
|
||||
width={width || 640}
|
||||
|
||||
+16
-7
@@ -66,7 +66,7 @@ describe('<StructuredMetadataTable />', () => {
|
||||
expect(getByText(startCase(value))).toBeInTheDocument();
|
||||
});
|
||||
metadata.arrayField.forEach(value => {
|
||||
expect(getByText(value)).toBeInTheDocument();
|
||||
expect(getByText(new RegExp(value))).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,12 +124,19 @@ describe('<StructuredMetadataTable />', () => {
|
||||
};
|
||||
|
||||
it('should make keys human readable', async () => {
|
||||
const rendered = render(<StructuredMetadataTable metadata={metadata} />);
|
||||
const rendered = render(
|
||||
<StructuredMetadataTable
|
||||
metadata={metadata}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>,
|
||||
);
|
||||
expect(rendered.queryByText(/^Test A/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^Test B/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^Test C/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^Test D/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^Test E/)).toBeInTheDocument();
|
||||
|
||||
// nested content is displayed as yaml, so not affected by formatting
|
||||
expect(rendered.queryByText(/^testC/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/testE: stuff/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should be possible to disable it', async () => {
|
||||
@@ -161,14 +168,16 @@ describe('<StructuredMetadataTable />', () => {
|
||||
const rendered = render(
|
||||
<StructuredMetadataTable
|
||||
metadata={metadata}
|
||||
options={{ titleFormat: spongeBobCase }}
|
||||
options={{ titleFormat: spongeBobCase, nestedValuesAsYaml: true }}
|
||||
/>,
|
||||
);
|
||||
expect(rendered.queryByText(/^tEsTa/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^tEsTb/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^tEsTc/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^tEsTd/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^tEsTe/)).toBeInTheDocument();
|
||||
|
||||
// nested content is displayed as yaml, so not affected by formatting
|
||||
expect(rendered.queryByText(/^testC/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/^testE/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+31
-14
@@ -15,21 +15,23 @@
|
||||
*/
|
||||
|
||||
import React, { Fragment, ReactElement } from 'react';
|
||||
import {
|
||||
withStyles,
|
||||
createStyles,
|
||||
WithStyles,
|
||||
Theme,
|
||||
} from '@material-ui/core/styles';
|
||||
import startCase from 'lodash/startCase';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import {
|
||||
MetadataTable,
|
||||
MetadataTableItem,
|
||||
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';
|
||||
|
||||
@@ -41,7 +43,6 @@ const listStyle = createStyles({
|
||||
});
|
||||
|
||||
export type StructuredMetadataTableNestedListClassKey = 'root';
|
||||
|
||||
const nestedListStyle = (theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
@@ -110,9 +111,24 @@ function toValue(
|
||||
if (React.isValidElement(value)) {
|
||||
return <Fragment>{value}</Fragment>;
|
||||
}
|
||||
|
||||
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return renderMap(value, options, nested);
|
||||
if (value !== null && typeof value === 'object') {
|
||||
if (options.nestedValuesAsYaml) {
|
||||
return (
|
||||
<CodeSnippet
|
||||
language="yaml"
|
||||
text={jsyaml.dump(value)}
|
||||
customStyle={{
|
||||
background: 'transparent',
|
||||
lineHeight: '1.4',
|
||||
padding: '0',
|
||||
margin: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!Array.isArray(value)) {
|
||||
return renderMap(value, options, nested);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
@@ -122,7 +138,6 @@ function toValue(
|
||||
if (typeof value === 'boolean') {
|
||||
return <Fragment>{value ? '✅' : '❌'}</Fragment>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography variant="body2" component="span">
|
||||
{value}
|
||||
@@ -167,6 +182,7 @@ export interface StructuredMetadataTableProps {
|
||||
* @returns Formatted key
|
||||
*/
|
||||
titleFormat?: (key: string) => string;
|
||||
nestedValuesAsYaml?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -174,9 +190,10 @@ type Options = Required<NonNullable<StructuredMetadataTableProps['options']>>;
|
||||
|
||||
/** @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 <MetadataTable dense={dense}>{metadataItems}</MetadataTable>;
|
||||
|
||||
@@ -41,3 +41,18 @@
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-22.2.0.tgz#75aa7dcd440821d99def6a60b5f014207ae4968e"
|
||||
integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==
|
||||
|
||||
"@types/node@*":
|
||||
version "22.10.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.8.tgz#e7e2602c83d27d483c056302d76b86321c4e8697"
|
||||
integrity sha512-rk+QvAEGsbX/ZPiiyel6hJHNUS9cnSbPWVaZLvE+Er3tLqQFzWMz9JOfWW7XUmKvRPfxJfbl3qYWve+RGXncFw==
|
||||
|
||||
"@types/node@>=13.7.0":
|
||||
version "22.10.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.8.tgz#e7e2602c83d27d483c056302d76b86321c4e8697"
|
||||
integrity sha512-rk+QvAEGsbX/ZPiiyel6hJHNUS9cnSbPWVaZLvE+Er3tLqQFzWMz9JOfWW7XUmKvRPfxJfbl3qYWve+RGXncFw==
|
||||
|
||||
"@types/node@^22.0.0":
|
||||
version "22.10.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.8.tgz#e7e2602c83d27d483c056302d76b86321c4e8697"
|
||||
integrity sha512-rk+QvAEGsbX/ZPiiyel6hJHNUS9cnSbPWVaZLvE+Er3tLqQFzWMz9JOfWW7XUmKvRPfxJfbl3qYWve+RGXncFw==
|
||||
|
||||
@@ -35,6 +35,7 @@ describe('RouteRef', () => {
|
||||
|
||||
internal.setId('some-id');
|
||||
expect(String(internal)).toBe('RouteRef{some-id}');
|
||||
internal.setId('some-id'); // Should allow same ID
|
||||
|
||||
expect(() => internal.setId('some-other-id')).toThrow(
|
||||
"RouteRef was referenced twice as both 'some-id' and 'some-other-id'",
|
||||
|
||||
@@ -95,7 +95,7 @@ export class RouteRefImpl implements InternalRouteRef {
|
||||
if (!id) {
|
||||
throw new Error(`${this.#name} id must be a non-empty string`);
|
||||
}
|
||||
if (this.#id) {
|
||||
if (this.#id && this.#id !== id) {
|
||||
throw new Error(
|
||||
`${this.#name} was referenced twice as both '${this.#id}' and '${id}'`,
|
||||
);
|
||||
|
||||
@@ -82,7 +82,12 @@ describe('generateOpenApiSchema', () => {
|
||||
}),
|
||||
} as any);
|
||||
|
||||
const { command } = await import('./index');
|
||||
// Same logic as https://github.com/backstage/backstage/blob/547e41da5ac497e5a606c08c8fb57b429687d5f7/packages/repo-tools/src/commands/index.ts#L278-L297
|
||||
const {
|
||||
default: { command },
|
||||
} = (await import('./index')) as unknown as {
|
||||
default: typeof import('./index');
|
||||
};
|
||||
|
||||
const actions = async () => {
|
||||
while (!mockOn.ready) {
|
||||
|
||||
+1
-1
@@ -437,7 +437,7 @@ export interface Config {
|
||||
/**
|
||||
* LdapOrg provider key
|
||||
*/
|
||||
ldapOrg: {
|
||||
ldapOrg?: {
|
||||
/**
|
||||
* Id of the LdapOrg provider
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,7 @@ export const ClusterOverview = () => {
|
||||
'OIDC Token Provider': value.oidcTokenProvider ?? 'N/A',
|
||||
'Dashboard Link': value.dashboardUrl ?? 'N/A',
|
||||
}}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
)}
|
||||
</InfoCard>
|
||||
|
||||
@@ -50,7 +50,10 @@ const defaultColumns: TableColumn<INode>[] = [
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h5">Node Info</Typography>
|
||||
<StructuredMetadataTable metadata={node.status?.nodeInfo ?? {}} />
|
||||
<StructuredMetadataTable
|
||||
metadata={node.status?.nodeInfo ?? {}}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h5">Addresses</Typography>
|
||||
@@ -61,6 +64,7 @@ const defaultColumns: TableColumn<INode>[] = [
|
||||
return accum;
|
||||
}, {} as any) ?? {}
|
||||
}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -72,6 +76,7 @@ const defaultColumns: TableColumn<INode>[] = [
|
||||
return accum;
|
||||
}, {} as any) ?? {}
|
||||
}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -84,7 +84,10 @@ const DefaultCustomResourceAccordion = ({
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{Object.prototype.hasOwnProperty.call(customResource, 'status') && (
|
||||
<StructuredMetadataTable metadata={customResource.status} />
|
||||
<StructuredMetadataTable
|
||||
metadata={customResource.status}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
+4
-4
@@ -40,13 +40,13 @@ describe('DeploymentDrawer', () => {
|
||||
expect(getAllByText('Deployment')).toHaveLength(2);
|
||||
expect(getByText('YAML')).toBeInTheDocument();
|
||||
expect(getByText('Strategy')).toBeInTheDocument();
|
||||
expect(getByText('Rolling Update:')).toBeInTheDocument();
|
||||
expect(getByText(textContentMatcher('Max Surge: 25%'))).toBeInTheDocument();
|
||||
expect(getByText('rollingUpdate:')).toBeInTheDocument();
|
||||
expect(getByText(textContentMatcher('maxSurge: 25%'))).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(textContentMatcher('Max Unavailable: 25%')),
|
||||
getByText(textContentMatcher('maxUnavailable: 25%')),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(textContentMatcher('Type: RollingUpdate')),
|
||||
getByText(textContentMatcher('type: RollingUpdate')),
|
||||
).toBeInTheDocument();
|
||||
expect(getByText('Min Ready Seconds')).toBeInTheDocument();
|
||||
expect(getByText('???')).toBeInTheDocument();
|
||||
|
||||
@@ -37,13 +37,13 @@ describe('IngressDrawer', () => {
|
||||
expect(screen.getByText('YAML')).toBeInTheDocument();
|
||||
expect(screen.getByText('Rules')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(textContentMatcher('Host: api.awesome-host.io')),
|
||||
screen.getByText(textContentMatcher('host: api.awesome-host.io')),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getAllByText(textContentMatcher('Service Port: 80')),
|
||||
screen.getAllByText(textContentMatcher('servicePort: 80')),
|
||||
).toHaveLength(2);
|
||||
expect(
|
||||
screen.getAllByText(textContentMatcher('Service Name: awesome-service')),
|
||||
screen.getAllByText(textContentMatcher('serviceName: awesome-service')),
|
||||
).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,6 +65,9 @@ const IngressCard = ({ ingress }: IngressCardProps) => {
|
||||
metadata={{
|
||||
...ingress.spec,
|
||||
}}
|
||||
options={{
|
||||
nestedValuesAsYaml: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+1
@@ -237,6 +237,7 @@ const KubernetesStructuredMetadataTableDrawerContent = <
|
||||
{!isYaml && (
|
||||
<StructuredMetadataTable
|
||||
metadata={renderObject(replaceNullsWithUndefined(object))}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -176,6 +176,7 @@ export const ContainerCard: React.FC<ContainerCardProps> = ({
|
||||
containerSpec,
|
||||
containerStatus,
|
||||
)}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
</Grid>
|
||||
{containerMetrics && (
|
||||
|
||||
@@ -39,10 +39,10 @@ describe('ServiceDrawer', () => {
|
||||
expect(screen.getByText('Cluster IP')).toBeInTheDocument();
|
||||
expect(screen.getByText('Ports')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(textContentMatcher('Target Port: 1997')),
|
||||
screen.getByText(textContentMatcher('targetPort: 1997')),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(textContentMatcher('App: awesome-service')),
|
||||
screen.getByText(textContentMatcher('app: awesome-service')),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,6 +77,7 @@ const ServiceCard = ({ service }: ServiceCardProps) => {
|
||||
ports: service.spec?.ports,
|
||||
...metadata,
|
||||
}}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-6
@@ -40,20 +40,20 @@ describe('StatefulSetDrawer', () => {
|
||||
expect(getByText('StatefulSet')).toBeInTheDocument();
|
||||
expect(getByText('YAML')).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(textContentMatcher('Type: RollingUpdate')),
|
||||
getByText(textContentMatcher('type: RollingUpdate')),
|
||||
).toBeInTheDocument();
|
||||
expect(getByText('Rolling Update:')).toBeInTheDocument();
|
||||
expect(getByText(textContentMatcher('Max Surge: 25%'))).toBeInTheDocument();
|
||||
expect(getByText('rollingUpdate:')).toBeInTheDocument();
|
||||
expect(getByText(textContentMatcher('maxSurge: 25%'))).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(textContentMatcher('Max Unavailable: 25%')),
|
||||
getByText(textContentMatcher('maxUnavailable: 25%')),
|
||||
).toBeInTheDocument();
|
||||
expect(getByText('Pod Management Policy')).toBeInTheDocument();
|
||||
expect(getByText('Parallel')).toBeInTheDocument();
|
||||
expect(getByText('Service Name')).toBeInTheDocument();
|
||||
expect(getByText('Selector')).toBeInTheDocument();
|
||||
expect(getByText('Match Labels:')).toBeInTheDocument();
|
||||
expect(getByText('matchLabels:')).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(textContentMatcher('App: dice-roller')),
|
||||
getByText(textContentMatcher('app: dice-roller')),
|
||||
).toBeInTheDocument();
|
||||
expect(getByText('Revision History Limit')).toBeInTheDocument();
|
||||
expect(getByText('10')).toBeInTheDocument();
|
||||
|
||||
@@ -34,7 +34,10 @@ import fixture2 from '../src/__fixtures__/2-deployments.json';
|
||||
import fixture3 from '../src/__fixtures__/1-cronjobs.json';
|
||||
import fixture4 from '../src/__fixtures__/2-cronjobs.json';
|
||||
import fixture5 from '../src/__fixtures__/1-rollouts.json';
|
||||
import fixture6 from '../src/__fixtures__/3-ingresses.json';
|
||||
import fixture7 from '../src/__fixtures__/2-statefulsets.json';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { StructuredMetadataTable } from '@backstage/core-components';
|
||||
|
||||
const mockEntity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
@@ -134,6 +137,12 @@ class MockKubernetesClient implements KubernetesApi {
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
testA: 'stuff',
|
||||
testB: { testC: 'stuff' },
|
||||
testD: [{ testE: 'stuff' }],
|
||||
};
|
||||
|
||||
createDevApp()
|
||||
.addPage({
|
||||
path: '/fixture-1',
|
||||
@@ -200,5 +209,35 @@ createDevApp()
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.addPage({
|
||||
path: '/fixture-6',
|
||||
title: 'Fixture 6',
|
||||
element: (
|
||||
<TestApiProvider
|
||||
apis={[[kubernetesApiRef, new MockKubernetesClient(fixture6)]]}
|
||||
>
|
||||
<EntityProvider entity={mockEntity}>
|
||||
<EntityKubernetesContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.addPage({
|
||||
path: '/fixture-7',
|
||||
title: 'Fixture 7',
|
||||
element: (
|
||||
<TestApiProvider
|
||||
apis={[[kubernetesApiRef, new MockKubernetesClient(fixture7)]]}
|
||||
>
|
||||
<EntityProvider entity={mockEntity}>
|
||||
<StructuredMetadataTable
|
||||
metadata={metadata}
|
||||
options={{ nestedValuesAsYaml: true }}
|
||||
/>
|
||||
<EntityKubernetesContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.registerPlugin(kubernetesPlugin)
|
||||
.render();
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
{
|
||||
"ingresses": [
|
||||
{
|
||||
"metadata": {
|
||||
"name": "app-service-endpoint.dice-roller.backstage.io",
|
||||
"namespace": "dice-roller",
|
||||
"uid": "00000000-0000-0000-0000-000000000000",
|
||||
"resourceVersion": "4749260703",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2023-08-16T14:52:16Z",
|
||||
"labels": {
|
||||
"backstage.io/kubernetes-id": "dice-roller",
|
||||
"ingress-class": "istio-internal"
|
||||
},
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "spec",
|
||||
"metacontroller.k8s.io/last-applied-configuration": "spec",
|
||||
"nginx.ingress.kubernetes.io/auth-signin": "https://backstage.io/authenticate",
|
||||
"nginx.ingress.kubernetes.io/configuration-snippet": "config",
|
||||
"nginx.ingress.kubernetes.io/service-upstream": "true",
|
||||
"nginx.ingress.kubernetes.io/upstream-vhost": "app-service-endpoint.dice-roller.backstage.services"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "networking.backstage.com/v1beta1",
|
||||
"kind": "MeshIntegration",
|
||||
"name": "app-service-endpoint",
|
||||
"uid": "00000000-0000-0000-0000-000000000000",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
],
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "nginx-ingress-controller",
|
||||
"operation": "Update",
|
||||
"apiVersion": "networking.k8s.io/v1",
|
||||
"time": "2023-08-16T14:52:56Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:loadBalancer": {
|
||||
"f:ingress": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
},
|
||||
{
|
||||
"manager": "kubectl-client-side-apply",
|
||||
"operation": "Update",
|
||||
"apiVersion": "networking.k8s.io/v1",
|
||||
"time": "2023-12-15T16:33:17Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:kubectl.kubernetes.io/last-applied-configuration": {},
|
||||
"f:nginx.ingress.kubernetes.io/auth-signin": {},
|
||||
"f:nginx.ingress.kubernetes.io/configuration-snippet": {},
|
||||
"f:nginx.ingress.kubernetes.io/service-upstream": {},
|
||||
"f:nginx.ingress.kubernetes.io/upstream-vhost": {}
|
||||
},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:backstage.io/kubernetes-id": {},
|
||||
"f:ingress-class": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:ingressClassName": {},
|
||||
"f:rules": {},
|
||||
"f:tls": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubectl-label",
|
||||
"operation": "Update",
|
||||
"apiVersion": "networking.k8s.io/v1",
|
||||
"time": "2024-02-21T15:00:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:labels": {
|
||||
"f:controller-uid": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "metacontroller",
|
||||
"operation": "Update",
|
||||
"apiVersion": "networking.k8s.io/v1",
|
||||
"time": "2024-02-21T15:01:01Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
"f:metacontroller.k8s.io/last-applied-configuration": {}
|
||||
},
|
||||
"f:ownerReferences": {
|
||||
".": {},
|
||||
"k:{\"uid\":\"00000000-0000-0000-0000-000000000000\"}": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"ingressClassName": "istio-internal",
|
||||
"tls": {
|
||||
"hosts": ["app-service-endpoint.dice-roller.backstage.io"],
|
||||
"secretName": "dice-roller-wildcard-certificate"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"host": "app-service-endpoint.dice-roller.backstage.io",
|
||||
"http": {
|
||||
"paths": [
|
||||
{
|
||||
"pathType": "ImplementationSpecific",
|
||||
"backend": {
|
||||
"service": {
|
||||
"name": "app-service-endpoint",
|
||||
"port": {
|
||||
"number": 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"pathType": "Second item in the list",
|
||||
"backend": {
|
||||
"service": {
|
||||
"name": "app-service-endpoint",
|
||||
"port": {
|
||||
"number": 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {
|
||||
"ingress": [
|
||||
{
|
||||
"ip": "1.2.3.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"deployments": [
|
||||
{
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"deployment.kubernetes.io/revision": "2",
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"backstage.io/kubernetes-id\":\"dice-roller\"},\"name\":\"dice-roller\",\"namespace\":\"default\"},\"spec\":{\"replicas\":10,\"selector\":{\"matchLabels\":{\"app\":\"dice-roller\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"dice-roller\",\"backstage.io/kubernetes-id\":\"dice-roller\"}},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}}}\n"
|
||||
},
|
||||
"creationTimestamp": "2020-09-23T12:00:55.000Z",
|
||||
"generation": 3,
|
||||
"labels": {
|
||||
"backstage.io/kubernetes-id": "dice-roller"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:kubectl.kubernetes.io/last-applied-configuration": {}
|
||||
},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:backstage.io/kubernetes-id": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:progressDeadlineSeconds": {},
|
||||
"f:replicas": {},
|
||||
"f:revisionHistoryLimit": {},
|
||||
"f:selector": {
|
||||
"f:matchLabels": {
|
||||
".": {},
|
||||
"f:app": {}
|
||||
}
|
||||
},
|
||||
"f:strategy": {
|
||||
"f:rollingUpdate": {
|
||||
".": {},
|
||||
"f:maxSurge": {},
|
||||
"f:maxUnavailable": {}
|
||||
},
|
||||
"f:type": {}
|
||||
},
|
||||
"f:template": {
|
||||
"f:metadata": {
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app": {},
|
||||
"f:backstage.io/kubernetes-id": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"nginx\"}": {
|
||||
".": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:ports": {
|
||||
".": {},
|
||||
"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
|
||||
".": {},
|
||||
"f:containerPort": {},
|
||||
"f:protocol": {}
|
||||
}
|
||||
},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"manager": "kubectl",
|
||||
"operation": "Update",
|
||||
"time": "2020-09-25T09:58:50.000Z"
|
||||
},
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
"f:deployment.kubernetes.io/revision": {}
|
||||
}
|
||||
},
|
||||
"f:status": {
|
||||
"f:availableReplicas": {},
|
||||
"f:conditions": {
|
||||
".": {},
|
||||
"k:{\"type\":\"Available\"}": {
|
||||
".": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:lastUpdateTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Progressing\"}": {
|
||||
".": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:lastUpdateTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:observedGeneration": {},
|
||||
"f:readyReplicas": {},
|
||||
"f:replicas": {},
|
||||
"f:updatedReplicas": {}
|
||||
}
|
||||
},
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"time": "2020-09-25T09:58:55.000Z"
|
||||
}
|
||||
],
|
||||
"name": "dice-roller",
|
||||
"namespace": "default",
|
||||
"resourceVersion": "593230",
|
||||
"selfLink": "/apis/apps/v1/namespaces/default/deployments/dice-roller",
|
||||
"uid": "7551e949-42d1-4061-83c5-9da107186e47"
|
||||
},
|
||||
"spec": {
|
||||
"progressDeadlineSeconds": 600,
|
||||
"replicas": 10,
|
||||
"revisionHistoryLimit": 10,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "dice-roller"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"rollingUpdate": {
|
||||
"maxSurge": "25%",
|
||||
"maxUnavailable": "25%"
|
||||
},
|
||||
"type": "RollingUpdate"
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "dice-roller",
|
||||
"backstage.io/kubernetes-id": "dice-roller"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "nginx:1.14.2",
|
||||
"imagePullPolicy": "IfNotPresent",
|
||||
"name": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File"
|
||||
}
|
||||
],
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"restartPolicy": "Always",
|
||||
"schedulerName": "default-scheduler",
|
||||
"securityContext": {},
|
||||
"terminationGracePeriodSeconds": 30
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"availableReplicas": 10,
|
||||
"conditions": [
|
||||
{
|
||||
"lastTransitionTime": "2020-09-23T12:00:55.000Z",
|
||||
"lastUpdateTime": "2020-09-24T11:39:28.000Z",
|
||||
"message": "ReplicaSet \"dice-roller-6c8646bfd\" has successfully progressed.",
|
||||
"reason": "NewReplicaSetAvailable",
|
||||
"status": "True",
|
||||
"type": "Progressing"
|
||||
},
|
||||
{
|
||||
"lastTransitionTime": "2020-09-25T09:58:55.000Z",
|
||||
"lastUpdateTime": "2020-09-25T09:58:55.000Z",
|
||||
"message": "Deployment has minimum availability.",
|
||||
"reason": "MinimumReplicasAvailable",
|
||||
"status": "True",
|
||||
"type": "Available"
|
||||
}
|
||||
],
|
||||
"observedGeneration": 3,
|
||||
"readyReplicas": 10,
|
||||
"replicas": 10,
|
||||
"updatedReplicas": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -991,6 +991,27 @@ describe('NunjucksWorkflowRunner', () => {
|
||||
);
|
||||
expect(fakeActionHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should validate each parameter renders to a valid value', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
each: '${{parameters.data}}',
|
||||
action: 'jest-validated-action',
|
||||
input: { foo: '${{each.value}}' },
|
||||
},
|
||||
],
|
||||
output: {},
|
||||
parameters: {},
|
||||
});
|
||||
await expect(runner.execute(task)).rejects.toThrow(
|
||||
'Invalid value on action jest-validated-action.each parameter, "${{parameters.data}}" cannot be resolved to a value',
|
||||
);
|
||||
expect(fakeActionHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('secrets', () => {
|
||||
|
||||
@@ -310,13 +310,21 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedEach =
|
||||
step.each && this.render(step.each, context, renderTemplate);
|
||||
|
||||
if (step.each && !resolvedEach) {
|
||||
throw new InputError(
|
||||
`Invalid value on action ${action.id}.each parameter, "${step.each}" cannot be resolved to a value`,
|
||||
);
|
||||
}
|
||||
|
||||
const iterations = (
|
||||
step.each
|
||||
? Object.entries(this.render(step.each, context, renderTemplate)).map(
|
||||
([key, value]) => ({
|
||||
each: { key, value },
|
||||
}),
|
||||
)
|
||||
resolvedEach
|
||||
? Object.entries(resolvedEach).map(([key, value]) => ({
|
||||
each: { key, value },
|
||||
}))
|
||||
: [{}]
|
||||
).map(i => ({
|
||||
...i,
|
||||
|
||||
@@ -20,7 +20,6 @@ import Box from '@material-ui/core/Box';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { ResizableBox } from 'react-resizable';
|
||||
import {
|
||||
ScaffolderTaskOutput,
|
||||
scaffolderApiRef,
|
||||
@@ -119,6 +118,7 @@ export const OngoingTask = (props: {
|
||||
|
||||
useEffect(() => {
|
||||
if (taskStream.completed && !taskStream.error) {
|
||||
setLogVisibleState(true);
|
||||
setButtonBarVisibleState(false);
|
||||
}
|
||||
}, [taskStream.error, taskStream.completed]);
|
||||
@@ -292,13 +292,11 @@ export const OngoingTask = (props: {
|
||||
) : null}
|
||||
|
||||
{logsVisible ? (
|
||||
<ResizableBox height={240} minConstraints={[0, 160]} axis="y">
|
||||
<Paper style={{ height: '100%' }}>
|
||||
<Box padding={2} height="100%">
|
||||
<TaskLogStream logs={taskStream.stepLogs} />
|
||||
</Box>
|
||||
</Paper>
|
||||
</ResizableBox>
|
||||
<Paper style={{ height: '100%' }}>
|
||||
<Box padding={2} height="100%">
|
||||
<TaskLogStream logs={taskStream.stepLogs} />
|
||||
</Box>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Content>
|
||||
</Page>
|
||||
|
||||
@@ -170,6 +170,7 @@ const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
|
||||
{
|
||||
defaultPath: 'docs',
|
||||
defaultTitle: 'TechDocs',
|
||||
routeRef: convertLegacyRouteRef(rootCatalogDocsRouteRef),
|
||||
loader: () =>
|
||||
import('./Router').then(({ EmbeddedDocsRouter }) =>
|
||||
compatWrapper(
|
||||
|
||||
@@ -4291,6 +4291,7 @@ __metadata:
|
||||
d3-zoom: ^3.0.0
|
||||
dagre: ^0.8.5
|
||||
history: ^5.0.0
|
||||
js-yaml: ^4.1.0
|
||||
linkify-react: 4.1.3
|
||||
linkifyjs: 4.1.3
|
||||
lodash: ^4.17.21
|
||||
@@ -19314,13 +19315,13 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.29":
|
||||
version: 3.3.33
|
||||
resolution: "@types/dockerode@npm:3.3.33"
|
||||
version: 3.3.34
|
||||
resolution: "@types/dockerode@npm:3.3.34"
|
||||
dependencies:
|
||||
"@types/docker-modem": "*"
|
||||
"@types/node": "*"
|
||||
"@types/ssh2": "*"
|
||||
checksum: 4e3ed41f3b89a213c9ba923862b653f54ee7232b2c95553410d0c3846396efb592d0291fa2f50d4b47d42a2ae0b49f422e72efcdd845d0ebb60da2e8990e9293
|
||||
checksum: 0cb35276fc76ee8ae9d6db7054198e23370f753894d06aa56efbf232032009c6f63a3bfce9d71e6272f3240040d9da2d2a56671d24f34e598ff2000df946ecbd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -20735,11 +20736,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/ws@npm:*, @types/ws@npm:^8.0.0, @types/ws@npm:^8.5.10, @types/ws@npm:^8.5.3, @types/ws@npm:^8.5.4":
|
||||
version: 8.5.13
|
||||
resolution: "@types/ws@npm:8.5.13"
|
||||
version: 8.5.14
|
||||
resolution: "@types/ws@npm:8.5.14"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: f17023ce7b89c6124249c90211803a4aaa02886e12bc2d0d2cd47fa665eeb058db4d871ce4397d8e423f6beea97dd56835dd3fdbb921030fe4d887601e37d609
|
||||
checksum: b63d25146a0d2ebb9cb35e4b68c5a01d81b8f4657d62b0a0d9470df6a357798349a44064b2f84b8f98a553ba84d9a5ee302d3053feef02c7771010c55d290ca6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -24495,7 +24496,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chalk@npm:^5.4.1, chalk@npm:~5.4.1":
|
||||
"chalk@npm:^5.4.1":
|
||||
version: 5.4.1
|
||||
resolution: "chalk@npm:5.4.1"
|
||||
checksum: 0c656f30b782fed4d99198825c0860158901f449a6b12b818b0aabad27ec970389e7e8767d0e00762175b23620c812e70c4fd92c0210e55fc2d993638b74e86e
|
||||
@@ -25167,10 +25168,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:*, commander@npm:^12.0.0, commander@npm:^12.1.0, commander@npm:~12.1.0":
|
||||
version: 12.1.0
|
||||
resolution: "commander@npm:12.1.0"
|
||||
checksum: 68e9818b00fc1ed9cdab9eb16905551c2b768a317ae69a5e3c43924c2b20ac9bb65b27e1cab36aeda7b6496376d4da908996ba2c0b5d79463e0fb1e77935d514
|
||||
"commander@npm:*, commander@npm:^13.1.0":
|
||||
version: 13.1.0
|
||||
resolution: "commander@npm:13.1.0"
|
||||
checksum: 8ca2fcb33caf2aa06fba3722d7a9440921331d54019dabf906f3603313e7bf334b009b862257b44083ff65d5a3ab19e83ad73af282bd5319f01dc228bdf87ef0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25209,6 +25210,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^12.0.0, commander@npm:^12.1.0":
|
||||
version: 12.1.0
|
||||
resolution: "commander@npm:12.1.0"
|
||||
checksum: 68e9818b00fc1ed9cdab9eb16905551c2b768a317ae69a5e3c43924c2b20ac9bb65b27e1cab36aeda7b6496376d4da908996ba2c0b5d79463e0fb1e77935d514
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^2.19.0, commander@npm:^2.20.0":
|
||||
version: 2.20.3
|
||||
resolution: "commander@npm:2.20.3"
|
||||
@@ -26459,7 +26467,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:~4.4.0":
|
||||
"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.4.0":
|
||||
version: 4.4.0
|
||||
resolution: "debug@npm:4.4.0"
|
||||
dependencies:
|
||||
@@ -28836,7 +28844,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"execa@npm:~8.0.1":
|
||||
"execa@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "execa@npm:8.0.1"
|
||||
dependencies:
|
||||
@@ -34713,7 +34721,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lilconfig@npm:~3.1.3":
|
||||
"lilconfig@npm:^3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "lilconfig@npm:3.1.3"
|
||||
checksum: 644eb10830350f9cdc88610f71a921f510574ed02424b57b0b3abb66ea725d7a082559552524a842f4e0272c196b88dfe1ff7d35ffcc6f45736777185cd67c9a
|
||||
@@ -34763,22 +34771,22 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"lint-staged@npm:^15.0.0":
|
||||
version: 15.4.1
|
||||
resolution: "lint-staged@npm:15.4.1"
|
||||
version: 15.4.2
|
||||
resolution: "lint-staged@npm:15.4.2"
|
||||
dependencies:
|
||||
chalk: ~5.4.1
|
||||
commander: ~12.1.0
|
||||
debug: ~4.4.0
|
||||
execa: ~8.0.1
|
||||
lilconfig: ~3.1.3
|
||||
listr2: ~8.2.5
|
||||
micromatch: ~4.0.8
|
||||
pidtree: ~0.6.0
|
||||
string-argv: ~0.3.2
|
||||
yaml: ~2.6.1
|
||||
chalk: ^5.4.1
|
||||
commander: ^13.1.0
|
||||
debug: ^4.4.0
|
||||
execa: ^8.0.1
|
||||
lilconfig: ^3.1.3
|
||||
listr2: ^8.2.5
|
||||
micromatch: ^4.0.8
|
||||
pidtree: ^0.6.0
|
||||
string-argv: ^0.3.2
|
||||
yaml: ^2.7.0
|
||||
bin:
|
||||
lint-staged: bin/lint-staged.js
|
||||
checksum: 6c17747e9379a08ad3d15aab869a362c750d357182c76ca88121f01143910a2a2d5b1673536f3ba9db2331bf2102ffee115532736dd9c3fa2696344732a47d3d
|
||||
checksum: bdb031d64f388aea166b3beaaff8a0a1503e50433959805985b43fcd788c9bfaf1e3a6c0852d830a6c75345b9c0809c5e6dab80d48460e3d6317b7ca0f842df8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -34789,7 +34797,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"listr2@npm:~8.2.5":
|
||||
"listr2@npm:^8.2.5":
|
||||
version: 8.2.5
|
||||
resolution: "listr2@npm:8.2.5"
|
||||
dependencies:
|
||||
@@ -36266,7 +36274,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.7, micromatch@npm:^4.0.8, micromatch@npm:~4.0.8":
|
||||
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.7, micromatch@npm:^4.0.8":
|
||||
version: 4.0.8
|
||||
resolution: "micromatch@npm:4.0.8"
|
||||
dependencies:
|
||||
@@ -39212,7 +39220,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pidtree@npm:~0.6.0":
|
||||
"pidtree@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "pidtree@npm:0.6.0"
|
||||
bin:
|
||||
@@ -43959,7 +43967,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-argv@npm:~0.3.1, string-argv@npm:~0.3.2":
|
||||
"string-argv@npm:^0.3.2, string-argv@npm:~0.3.1":
|
||||
version: 0.3.2
|
||||
resolution: "string-argv@npm:0.3.2"
|
||||
checksum: 8703ad3f3db0b2641ed2adbb15cf24d3945070d9a751f9e74a924966db9f325ac755169007233e8985a39a6a292f14d4fee20482989b89b96e473c4221508a0f
|
||||
@@ -47657,12 +47665,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3, yaml@npm:^2.3.4, yaml@npm:~2.6.1":
|
||||
version: 2.6.1
|
||||
resolution: "yaml@npm:2.6.1"
|
||||
"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3, yaml@npm:^2.3.4, yaml@npm:^2.7.0":
|
||||
version: 2.7.0
|
||||
resolution: "yaml@npm:2.7.0"
|
||||
bin:
|
||||
yaml: bin.mjs
|
||||
checksum: 5cf2627f121dcf04ccdebce8e6cbac7c9983d465c4eab314f6fbdc13cda8a07f4e8f9c2252a382b30bcabe05ee3c683647293afd52eb37cbcefbdc7b6ebde9ee
|
||||
checksum: 6e8b2f9b9d1b18b10274d58eb3a47ec223d9a93245a890dcb34d62865f7e744747190a9b9177d5f0ef4ea2e44ad2c0214993deb42e0800766203ac46f00a12dd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user