Merge branch 'master' into canon-class-renaming
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli-node': patch
|
||||
---
|
||||
|
||||
Added `type` field to `BackstagePackageJson` type.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Add support for native ESM in Node.js code. This changes the behavior of dynamic import expressions in Node.js code. Typically this can be fixed by replacing `import(...)` with `require(...)`, with an `as typeof import(...)` cast if needed for types. This is because dynamic imports will no longer be transformed to `require(...)` calls, but instead be left as-is. This in turn allows you to load ESM modules from CommonJS code using `import(...)`.
|
||||
|
||||
This change adds support for the following in Node.js packages, across type checking, package builds, runtime transforms and Jest tests:
|
||||
|
||||
- Dynamic imports that load ESM modules from CommonJS code.
|
||||
- Both `.mjs` and `.mts` files as explicit ESM files, as well as `.cjs` and `.cts` as explicit CommonJS files.
|
||||
- Support for the `"type": "module"` field in `package.json` to indicate that the package is an ESM package.
|
||||
|
||||
There are a few caveats to be aware of:
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend': patch
|
||||
'@backstage/plugin-notifications': patch
|
||||
---
|
||||
|
||||
added topic filter for notifications
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
'@backstage/backend-defaults': patch
|
||||
'@backstage/config-loader': patch
|
||||
---
|
||||
|
||||
Internal refactor to use explicit `require` for lazy-loading dependency.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
---
|
||||
|
||||
Sync feature installation compatibility logic with `@backstage/backend-app-api`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Cleanup `refresh_state_references` for entity processors and providers that are no longer in control of a `refresh_state` row for entity
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/repo-tools': patch
|
||||
---
|
||||
|
||||
Internal refactor to support native ESM.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-module-catalog': patch
|
||||
---
|
||||
|
||||
Modified the logic for generating the location URL by encoding the entity property values with `encodeURIComponent`. This enhancement improves the safety and reliability of the URL.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-dynamic-feature-service': patch
|
||||
---
|
||||
|
||||
Make sure changes are successfully tracked before starting up scanner.
|
||||
@@ -202,7 +202,7 @@ jobs:
|
||||
|
||||
env:
|
||||
CI: true
|
||||
NODE_OPTIONS: --max-old-space-size=8192 --no-node-snapshot
|
||||
NODE_OPTIONS: --max-old-space-size=8192 --no-node-snapshot --experimental-vm-modules
|
||||
INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }}
|
||||
INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }}
|
||||
INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
env:
|
||||
CI: true
|
||||
NODE_OPTIONS: --max-old-space-size=8192 --no-node-snapshot
|
||||
NODE_OPTIONS: --max-old-space-size=8192 --no-node-snapshot --experimental-vm-modules
|
||||
INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }}
|
||||
INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }}
|
||||
INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }}
|
||||
|
||||
@@ -9,11 +9,15 @@
|
||||
"watch-css": "node scripts/watch-css.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-sass": "^6.0.2",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@mdx-js/loader": "^3.1.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@next/mdx": "^15.1.4",
|
||||
"@storybook/react": "^8.4.7",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@uiw/codemirror-themes": "^4.23.7",
|
||||
"@uiw/react-codemirror": "^4.23.7",
|
||||
"next": "14.2.23",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
|
||||
@@ -35,3 +35,37 @@ iframe {
|
||||
font-weight: var(--shiki-dark-font-weight) !important;
|
||||
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.ͼ2 .cm-gutters {
|
||||
background-color: var(--canon-surface-2);
|
||||
border-right: 1px solid var(--canon-border-base);
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.ͼ1 .cm-lineNumbers .cm-gutterElement {
|
||||
padding-right: 8px;
|
||||
padding-left: 8px;
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 34px;
|
||||
}
|
||||
|
||||
.cm-line {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.ͼ1 .cm-line {
|
||||
padding-left: 12px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.cm-focused {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
||||
import { Sidebar } from '../components/Sidebar';
|
||||
import { Toolbar } from '@/components/Toolbar';
|
||||
import { Providers } from './providers';
|
||||
|
||||
import { CustomTheme } from '@/components/CustomTheme';
|
||||
import styles from './page.module.css';
|
||||
|
||||
import './globals.css';
|
||||
@@ -13,6 +13,7 @@ import '/public/backstage.css';
|
||||
export const metadata: Metadata = {
|
||||
title: 'Canon',
|
||||
description: 'UI library for Backstage',
|
||||
metadataBase: new URL('https://canon.backstage.io'),
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -35,6 +36,7 @@ export default function RootLayout({
|
||||
<Toolbar />
|
||||
{children}
|
||||
</div>
|
||||
<CustomTheme />
|
||||
</div>
|
||||
</Providers>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { sass } from '@codemirror/lang-sass';
|
||||
import styles from './styles.module.css';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { Icon } from '../../../../packages/canon';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
|
||||
const defaultTheme = `:root {
|
||||
--canon-accent: #000;
|
||||
}`;
|
||||
|
||||
const myTheme = createTheme({
|
||||
theme: 'light',
|
||||
settings: {
|
||||
background: 'var(--canon-surface-1)',
|
||||
backgroundImage: '',
|
||||
foreground: '#6182B8',
|
||||
caret: '#5d00ff',
|
||||
selection: '#036dd626',
|
||||
selectionMatch: '#036dd626',
|
||||
lineHighlight: '#8a91991a',
|
||||
gutterBackground: '#fff',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.comment, color: '#787b8099' },
|
||||
{ tag: t.variableName, color: '#0080ff' },
|
||||
{ tag: [t.string, t.special(t.brace)], color: '#6182B8' },
|
||||
{ tag: t.number, color: '#6182B8' },
|
||||
{ tag: t.bool, color: '#6182B8' },
|
||||
{ tag: t.null, color: '#6182B8' },
|
||||
{ tag: t.keyword, color: '#6182B8' },
|
||||
{ tag: t.operator, color: '#6182B8' },
|
||||
{ tag: t.className, color: '#6182B8' },
|
||||
{ tag: t.definition(t.typeName), color: '#6182B8' },
|
||||
{ tag: t.typeName, color: '#6182B8' },
|
||||
{ tag: t.angleBracket, color: '#6182B8' },
|
||||
{ tag: t.tagName, color: '#6182B8' },
|
||||
{ tag: t.attributeName, color: '#6182B8' },
|
||||
],
|
||||
});
|
||||
|
||||
export const CustomTheme = () => {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const [open, setOpen] = useState(true);
|
||||
const [customTheme, setCustomTheme] = useState<string | undefined>(undefined);
|
||||
const { selectedThemeName } = usePlayground();
|
||||
const [savedMessage, setSavedMessage] = useState<string>('Save');
|
||||
|
||||
const updateStyleElement = (theme: string) => {
|
||||
let styleElement = document.getElementById(
|
||||
'custom-theme-style',
|
||||
) as HTMLStyleElement;
|
||||
|
||||
if (!styleElement) {
|
||||
styleElement = document.createElement('style');
|
||||
styleElement.id = 'custom-theme-style';
|
||||
document.head.appendChild(styleElement);
|
||||
}
|
||||
|
||||
styleElement.textContent = theme;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedThemeName === 'custom') {
|
||||
let storedTheme = localStorage.getItem('customThemeCss');
|
||||
if (!storedTheme) {
|
||||
storedTheme = defaultTheme;
|
||||
localStorage.setItem('customThemeCss', storedTheme);
|
||||
}
|
||||
setCustomTheme(storedTheme);
|
||||
updateStyleElement(storedTheme);
|
||||
} else {
|
||||
const styleElement = document.getElementById(
|
||||
'custom-theme-style',
|
||||
) as HTMLStyleElement;
|
||||
if (styleElement) {
|
||||
styleElement.remove();
|
||||
}
|
||||
}
|
||||
}, [selectedThemeName]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const handleSave = () => {
|
||||
if (customTheme) {
|
||||
localStorage.setItem('customThemeCss', customTheme);
|
||||
updateStyleElement(customTheme);
|
||||
setSavedMessage('Saved!');
|
||||
setTimeout(() => setSavedMessage('Save'), 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = useCallback((val: string) => {
|
||||
setCustomTheme(val);
|
||||
}, []);
|
||||
|
||||
if (isClient === false) return null;
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{selectedThemeName === 'custom' && (
|
||||
<motion.div
|
||||
className={`${styles.container} ${open ? styles.open : ''}`}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerLeft}>Custom Theme</div>
|
||||
<div className={styles.headerRight}>
|
||||
{open && (
|
||||
<button className={styles.buttonSave} onClick={handleSave}>
|
||||
{savedMessage}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className={styles.buttonClose}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<Icon name={open ? 'chevronDown' : 'chevronUp'} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.editorContainer}>
|
||||
<CodeMirror
|
||||
value={customTheme}
|
||||
height="300px"
|
||||
extensions={[sass()]}
|
||||
onChange={handleChange}
|
||||
className={styles.editor}
|
||||
basicSetup={{ foldGutter: false }}
|
||||
theme={myTheme}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { CustomTheme } from './customTheme';
|
||||
@@ -0,0 +1,77 @@
|
||||
.container {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
width: 240px;
|
||||
height: 47px;
|
||||
background-color: var(--canon-surface-1);
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
transition-property: background-color, border-color, height, width;
|
||||
transition-duration: 0.2s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.open {
|
||||
width: 36%;
|
||||
height: 348px;
|
||||
}
|
||||
|
||||
.editor {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.editorContainer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 46px;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-surface-1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12px 0 16px;
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.buttonSave {
|
||||
all: unset;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.buttonClose {
|
||||
all: unset;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
background-color: var(--canon-surface-2);
|
||||
color: var(--canon-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -35,15 +35,11 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu a {
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
height: 28px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
@@ -54,12 +50,14 @@
|
||||
}
|
||||
|
||||
.line {
|
||||
text-decoration: none;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
height: 26px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import { components, overview, layoutComponents, theme } from '@/utils/data';
|
||||
import { Box } from '../../../../packages/canon/src/components/Box';
|
||||
import { Text } from '../../../../packages/canon/src/components/Text';
|
||||
import { Box } from '../../../../packages/canon';
|
||||
import { motion } from 'framer-motion';
|
||||
import styles from './Sidebar.module.css';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
box-sizing: border-box;
|
||||
padding-block: 0.25rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: canvas;
|
||||
background-color: var(--canon-surface-1);
|
||||
color: var(--color-gray-900);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
padding-inline: 0.25rem;
|
||||
|
||||
@@ -6,8 +6,9 @@ import { Icon } from '@backstage/canon';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
|
||||
const themes = [
|
||||
{ name: 'Backstage Legacy', value: 'legacy' },
|
||||
{ name: 'Backstage Default', value: 'default' },
|
||||
{ name: 'Backstage Legacy', value: 'legacy' },
|
||||
{ name: 'Custom theme', value: 'custom' },
|
||||
];
|
||||
|
||||
export const ThemeNameSelector = () => {
|
||||
@@ -31,7 +32,7 @@ export const ThemeNameSelector = () => {
|
||||
<Select.Positioner className={styles.Positioner} sideOffset={8}>
|
||||
<Select.Popup className={styles.Popup}>
|
||||
{themes.map(({ name, value }) => (
|
||||
<Select.Item className={styles.Item} value={value}>
|
||||
<Select.Item className={styles.Item} value={value} key={value}>
|
||||
<Select.ItemIndicator className={styles.ItemIndicator}>
|
||||
<Icon name="check" />
|
||||
</Select.ItemIndicator>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { ReactNode } from 'react';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import Image, { ImageProps } from 'next/image';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Heading } from '../../packages/canon/src/components/Heading';
|
||||
import { Box } from '../../packages/canon/src/components/Box';
|
||||
|
||||
export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
|
||||
@@ -7,16 +7,19 @@ import React, {
|
||||
} from 'react';
|
||||
import { components } from './data';
|
||||
|
||||
type Theme = 'light' | 'dark';
|
||||
type ThemeName = 'legacy' | 'default' | 'custom';
|
||||
|
||||
// Create a context with an empty array as the default value
|
||||
const PlaygroundContext = createContext<{
|
||||
selectedScreenSizes: string[];
|
||||
setSelectedScreenSizes: (screenSizes: string[]) => void;
|
||||
selectedComponents: string[];
|
||||
setSelectedComponents: (components: string[]) => void;
|
||||
selectedTheme: string | null;
|
||||
setSelectedTheme: (theme: string) => void;
|
||||
selectedThemeName: string;
|
||||
setSelectedThemeName: (themeName: string) => void;
|
||||
selectedTheme: Theme;
|
||||
setSelectedTheme: (theme: Theme) => void;
|
||||
selectedThemeName: ThemeName;
|
||||
setSelectedThemeName: (themeName: ThemeName) => void;
|
||||
}>({
|
||||
selectedScreenSizes: [],
|
||||
setSelectedScreenSizes: () => {},
|
||||
@@ -37,12 +40,14 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [selectedComponents, setSelectedComponents] = useState<string[]>(
|
||||
components.map(component => component.slug),
|
||||
);
|
||||
const [selectedTheme, setSelectedTheme] = useState<string | null>(() => {
|
||||
return isBrowser ? localStorage.getItem('theme') : 'light';
|
||||
});
|
||||
const [selectedThemeName, setSelectedThemeName] = useState<string>(() => {
|
||||
const [selectedTheme, setSelectedTheme] = useState<Theme>(() => {
|
||||
return isBrowser
|
||||
? localStorage.getItem('theme-name') || 'default'
|
||||
? (localStorage.getItem('theme') as Theme) || 'light'
|
||||
: 'light';
|
||||
});
|
||||
const [selectedThemeName, setSelectedThemeName] = useState<ThemeName>(() => {
|
||||
return isBrowser
|
||||
? (localStorage.getItem('theme-name') as ThemeName) || 'default'
|
||||
: 'default';
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,133 @@ __metadata:
|
||||
version: 6
|
||||
cacheKey: 8
|
||||
|
||||
"@babel/runtime@npm:^7.18.6":
|
||||
version: 7.26.0
|
||||
resolution: "@babel/runtime@npm:7.26.0"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.14.0
|
||||
checksum: c8e2c0504ab271b3467a261a8f119bf2603eb857a0d71e37791f4e3fae00f681365073cc79f141ddaa90c6077c60ba56448004ad5429d07ac73532be9f7cf28a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/autocomplete@npm:^6.0.0":
|
||||
version: 6.18.4
|
||||
resolution: "@codemirror/autocomplete@npm:6.18.4"
|
||||
dependencies:
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.17.0
|
||||
"@lezer/common": ^1.0.0
|
||||
checksum: 4216f45a17f6cfd8d33df53f940396f7d3707662570bf3a79d8d333f926e273a265fac13c362e29e3fa57ccdf444f1a047862f5f56c672cfc669c87ee975858f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/commands@npm:^6.0.0, @codemirror/commands@npm:^6.1.0":
|
||||
version: 6.8.0
|
||||
resolution: "@codemirror/commands@npm:6.8.0"
|
||||
dependencies:
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.4.0
|
||||
"@codemirror/view": ^6.27.0
|
||||
"@lezer/common": ^1.1.0
|
||||
checksum: 7d819bab4830ec7b8c5dffdec4b035dfa664bfd1d2675e639e08a459df65f45be111e1b8b569b1a8a3253d5980cf2ecf4394d8a13509996cca1b65cc16d47a4e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lang-css@npm:^6.2.0":
|
||||
version: 6.3.1
|
||||
resolution: "@codemirror/lang-css@npm:6.3.1"
|
||||
dependencies:
|
||||
"@codemirror/autocomplete": ^6.0.0
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@lezer/common": ^1.0.2
|
||||
"@lezer/css": ^1.1.7
|
||||
checksum: ed175d75d75bc0a059d1e60b3dcd8464d570da14fc97388439943c9c43e1e9146e37b83fe2ccaad9cd387420b7b411ea1d24ede78ecd1f2045a38acbb4dd36bc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lang-sass@npm:^6.0.2":
|
||||
version: 6.0.2
|
||||
resolution: "@codemirror/lang-sass@npm:6.0.2"
|
||||
dependencies:
|
||||
"@codemirror/lang-css": ^6.2.0
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@lezer/common": ^1.0.2
|
||||
"@lezer/sass": ^1.0.0
|
||||
checksum: e7665aaab70476a952522b143fd7bd59f6c025746cbf7b542f6965f94eecac483b4afd03f6da98aaa1572e379194309b241c5264eff05c681c637aa26651b9ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/language@npm:^6.0.0":
|
||||
version: 6.10.8
|
||||
resolution: "@codemirror/language@npm:6.10.8"
|
||||
dependencies:
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.23.0
|
||||
"@lezer/common": ^1.1.0
|
||||
"@lezer/highlight": ^1.0.0
|
||||
"@lezer/lr": ^1.0.0
|
||||
style-mod: ^4.0.0
|
||||
checksum: 679b69d69faa94f028f996a7005d0c6c2a2e4cd7a7a2614f615c23d7b642c31fc1837915248e864cb1ad59a2f032d1a7a8ef486b5f9904e5f6fbe6f7d2882c38
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lint@npm:^6.0.0":
|
||||
version: 6.8.4
|
||||
resolution: "@codemirror/lint@npm:6.8.4"
|
||||
dependencies:
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.35.0
|
||||
crelt: ^1.0.5
|
||||
checksum: 640e3dd44eb167d952eb5c5b8518919ba46e164aa3471776342f7f9361e676b4627a76a9f01d51b22127b97413f2bc9b8c60299d8dfdd5fc8ad0225d42de7669
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/search@npm:^6.0.0":
|
||||
version: 6.5.8
|
||||
resolution: "@codemirror/search@npm:6.5.8"
|
||||
dependencies:
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.0.0
|
||||
crelt: ^1.0.5
|
||||
checksum: 0f9633037492a7b647b606c30255ea42c4327319e643be7ea3aa2913ed8e4aa662589f457e376636521c7d4d1215fae0e8939f127db9c0790b19ae3b654c3bc4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0":
|
||||
version: 6.5.1
|
||||
resolution: "@codemirror/state@npm:6.5.1"
|
||||
dependencies:
|
||||
"@marijn/find-cluster-break": ^1.0.0
|
||||
checksum: b7d6de9a87d5b55dadfadaeb6e1c991e0a91845d3cdd0bd953391f05363fcbaf21de79a4eec2816ab8c3e31293faeca82cc2bb729d080779df94b14e28ae0d8a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/theme-one-dark@npm:^6.0.0":
|
||||
version: 6.1.2
|
||||
resolution: "@codemirror/theme-one-dark@npm:6.1.2"
|
||||
dependencies:
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.0.0
|
||||
"@lezer/highlight": ^1.0.0
|
||||
checksum: 29bc09f79534115f62658caf3d0db527fe347d058b69a8c7f580ae636827377aadd0606fd0d83dbab8d6f3b0a5df53d3253c619341b5fb93d2c8291a8efb9556
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.35.0":
|
||||
version: 6.36.2
|
||||
resolution: "@codemirror/view@npm:6.36.2"
|
||||
dependencies:
|
||||
"@codemirror/state": ^6.5.0
|
||||
style-mod: ^4.1.0
|
||||
w3c-keyname: ^2.2.4
|
||||
checksum: a58c64b623ddc65bb864917297f3b37f8e95280deec442024c43a9513b26352c829665c5d98e4dfcae104e8ecdfdb774d94a395a29da98a919c83482d2c14152
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/aix-ppc64@npm:0.24.2":
|
||||
version: 0.24.2
|
||||
resolution: "@esbuild/aix-ppc64@npm:0.24.2"
|
||||
@@ -261,6 +388,60 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0":
|
||||
version: 1.2.3
|
||||
resolution: "@lezer/common@npm:1.2.3"
|
||||
checksum: 9b5f52d949adae69d077f56c0b1c2295923108c3dfb241dd9f17654ff708f3eab81ff9fa7f0d0e4a668eabdcb9d961c73e75caca87c966ca1436e30e49130fcb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/css@npm:^1.1.7":
|
||||
version: 1.1.9
|
||||
resolution: "@lezer/css@npm:1.1.9"
|
||||
dependencies:
|
||||
"@lezer/common": ^1.2.0
|
||||
"@lezer/highlight": ^1.0.0
|
||||
"@lezer/lr": ^1.0.0
|
||||
checksum: 25c63475061a3c9f87961a7f85c5f547f14fb7e81b0864675d2206999a874a0559d676145c74c6ccde39519dbc8aa33e216265f5366d08060507b6c9e875fe0f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "@lezer/highlight@npm:1.2.1"
|
||||
dependencies:
|
||||
"@lezer/common": ^1.0.0
|
||||
checksum: a8822d7e37f79ff64669eb2df4a9f9d16580e88f2b276a646092e19a9bdccac304e92510e200e35869a8b1f6c27eba5972c508d347a277e9b722d582ab7a23d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/lr@npm:^1.0.0":
|
||||
version: 1.4.2
|
||||
resolution: "@lezer/lr@npm:1.4.2"
|
||||
dependencies:
|
||||
"@lezer/common": ^1.0.0
|
||||
checksum: 94318ad046c7dfcc8d37e26cb85b99623c39aef60aa51ec2abb30928e7a649f38fa5520f34bd5b356f1db11b6991999589f039e87c8949b0f163be3764f029d8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/sass@npm:^1.0.0":
|
||||
version: 1.0.7
|
||||
resolution: "@lezer/sass@npm:1.0.7"
|
||||
dependencies:
|
||||
"@lezer/common": ^1.2.0
|
||||
"@lezer/highlight": ^1.0.0
|
||||
"@lezer/lr": ^1.0.0
|
||||
checksum: 32d37aa5d3143033f0b886c33a293b1d28b7eecdf2cb979cf7b4cbba0587be227dfed246c27116060ac9b2b2c0fe4b09df4b672690dd0593e876529583e7ef2a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@marijn/find-cluster-break@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "@marijn/find-cluster-break@npm:1.0.2"
|
||||
checksum: 0d836de25e04d58325813401ef3c2d34caf040da985a5935fcbc9d84e7b47a21bdb15f57d70c2bf0960bd29ed3dbbb1afd00cdd0fc4fafbee7fd0ffe7d508ae1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mdx-js/loader@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "@mdx-js/loader@npm:3.1.0"
|
||||
@@ -904,6 +1085,66 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@uiw/codemirror-extensions-basic-setup@npm:4.23.7":
|
||||
version: 4.23.7
|
||||
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.7"
|
||||
dependencies:
|
||||
"@codemirror/autocomplete": ^6.0.0
|
||||
"@codemirror/commands": ^6.0.0
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/lint": ^6.0.0
|
||||
"@codemirror/search": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.0.0
|
||||
peerDependencies:
|
||||
"@codemirror/autocomplete": ">=6.0.0"
|
||||
"@codemirror/commands": ">=6.0.0"
|
||||
"@codemirror/language": ">=6.0.0"
|
||||
"@codemirror/lint": ">=6.0.0"
|
||||
"@codemirror/search": ">=6.0.0"
|
||||
"@codemirror/state": ">=6.0.0"
|
||||
"@codemirror/view": ">=6.0.0"
|
||||
checksum: 656c5f78db4c6216a9a7eeb65455f52b002b7a5426bb1b05f11b896d5dba9887c7b9ee5aee9fe369d6c7b3c69128ee842cb5c8fe029cd07c2e48ec9358039a30
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@uiw/codemirror-themes@npm:^4.23.7":
|
||||
version: 4.23.7
|
||||
resolution: "@uiw/codemirror-themes@npm:4.23.7"
|
||||
dependencies:
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.0.0
|
||||
peerDependencies:
|
||||
"@codemirror/language": ">=6.0.0"
|
||||
"@codemirror/state": ">=6.0.0"
|
||||
"@codemirror/view": ">=6.0.0"
|
||||
checksum: 8c2a9a7ee8df2de3f09ad1f18979c4b407f7dd03e1f6ddfe47837b9e4769b566a70259ec8c398473b6b6db79cdb8809d4acc2f2ed7851743e13ff95308f52e33
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@uiw/react-codemirror@npm:^4.23.7":
|
||||
version: 4.23.7
|
||||
resolution: "@uiw/react-codemirror@npm:4.23.7"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.18.6
|
||||
"@codemirror/commands": ^6.1.0
|
||||
"@codemirror/state": ^6.1.1
|
||||
"@codemirror/theme-one-dark": ^6.0.0
|
||||
"@uiw/codemirror-extensions-basic-setup": 4.23.7
|
||||
codemirror: ^6.0.0
|
||||
peerDependencies:
|
||||
"@babel/runtime": ">=7.11.0"
|
||||
"@codemirror/state": ">=6.0.0"
|
||||
"@codemirror/theme-one-dark": ">=6.0.0"
|
||||
"@codemirror/view": ">=6.0.0"
|
||||
codemirror: ">=6.0.0"
|
||||
react: ">=16.8.0"
|
||||
react-dom: ">=16.8.0"
|
||||
checksum: 5f6318b290f479c4718129578bdccbbdf64e3b588fdd79e3d80224779219eae7e249d40e4f88c864ed6ae40ea93036042313e983a022349563698593a2bf2e6c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0":
|
||||
version: 1.2.1
|
||||
resolution: "@ungap/structured-clone@npm:1.2.1"
|
||||
@@ -1254,6 +1495,8 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "canon-docs@workspace:."
|
||||
dependencies:
|
||||
"@codemirror/lang-sass": ^6.0.2
|
||||
"@lezer/highlight": ^1.2.1
|
||||
"@mdx-js/loader": ^3.1.0
|
||||
"@mdx-js/react": ^3.1.0
|
||||
"@next/mdx": ^15.1.4
|
||||
@@ -1262,6 +1505,8 @@ __metadata:
|
||||
"@types/node": ^20
|
||||
"@types/react": ^18
|
||||
"@types/react-dom": ^18
|
||||
"@uiw/codemirror-themes": ^4.23.7
|
||||
"@uiw/react-codemirror": ^4.23.7
|
||||
concurrently: ^9.1.2
|
||||
eslint: ^8
|
||||
eslint-config-next: 14.2.23
|
||||
@@ -1339,6 +1584,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"codemirror@npm:^6.0.0":
|
||||
version: 6.0.1
|
||||
resolution: "codemirror@npm:6.0.1"
|
||||
dependencies:
|
||||
"@codemirror/autocomplete": ^6.0.0
|
||||
"@codemirror/commands": ^6.0.0
|
||||
"@codemirror/language": ^6.0.0
|
||||
"@codemirror/lint": ^6.0.0
|
||||
"@codemirror/search": ^6.0.0
|
||||
"@codemirror/state": ^6.0.0
|
||||
"@codemirror/view": ^6.0.0
|
||||
checksum: 1a78f7077ac5801bdbff162aa0c61bf2b974603c7e9a477198c3ce50c789af674a061d7c293c58b73807eda345c2b5228c38ad2aabb9319d552d5486f785cbef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"collapse-white-space@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "collapse-white-space@npm:2.1.0"
|
||||
@@ -1394,6 +1654,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"crelt@npm:^1.0.5":
|
||||
version: 1.0.6
|
||||
resolution: "crelt@npm:1.0.6"
|
||||
checksum: dad842093371ad702afbc0531bfca2b0a8dd920b23a42f26e66dabbed9aad9acd5b9030496359545ef3937c3aced0fd4ac39f7a2d280a23ddf9eb7fdcb94a69f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2":
|
||||
version: 7.0.6
|
||||
resolution: "cross-spawn@npm:7.0.6"
|
||||
@@ -4380,6 +4647,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regenerator-runtime@npm:^0.14.0":
|
||||
version: 0.14.1
|
||||
resolution: "regenerator-runtime@npm:0.14.1"
|
||||
checksum: 9f57c93277b5585d3c83b0cf76be47b473ae8c6d9142a46ce8b0291a04bb2cf902059f0f8445dcabb3fb7378e5fe4bb4ea1e008876343d42e46d3b484534ce38
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regex-recursion@npm:^5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "regex-recursion@npm:5.1.1"
|
||||
@@ -4971,6 +5245,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"style-mod@npm:^4.0.0, style-mod@npm:^4.1.0":
|
||||
version: 4.1.2
|
||||
resolution: "style-mod@npm:4.1.2"
|
||||
checksum: 7c5c3e82747f9bcf5f288d8d07f50848e4630fe5ff7bfe4d94cc87d6b6a2588227cbf21b4c792ac6406e5852293300a75e710714479a5c59a06af677f0825ef8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"style-to-object@npm:^1.0.0":
|
||||
version: 1.0.8
|
||||
resolution: "style-to-object@npm:1.0.8"
|
||||
@@ -5331,6 +5612,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"w3c-keyname@npm:^2.2.4":
|
||||
version: 2.2.8
|
||||
resolution: "w3c-keyname@npm:2.2.8"
|
||||
checksum: 95bafa4c04fa2f685a86ca1000069c1ec43ace1f8776c10f226a73296caeddd83f893db885c2c220ebeb6c52d424e3b54d7c0c1e963bbf204038ff1a944fbb07
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "which-boxed-primitive@npm:1.1.1"
|
||||
|
||||
@@ -483,29 +483,40 @@ of the build system, including the bundling, tests, builds, and type checking.
|
||||
Loaders are always selected based on the file extension. The following is a list
|
||||
of all supported file extensions:
|
||||
|
||||
| Extension | Exports | Purpose |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| `.ts` | Script Module | TypeScript |
|
||||
| `.tsx` | Script Module | TypeScript and XML |
|
||||
| `.js` | Script Module | JavaScript |
|
||||
| `.jsx` | Script Module | JavaScript and XML |
|
||||
| `.mjs` | Script Module | ECMAScript Module |
|
||||
| `.cjs` | Script Module | CommonJS Module |
|
||||
| `.json` | JSON Data | JSON Data |
|
||||
| `.yml` | JSON Data | YAML Data |
|
||||
| `.yaml` | JSON Data | YAML Data |
|
||||
| `.css` | classes | Style sheet |
|
||||
| `.eot` | URL Path | Font |
|
||||
| `.ttf` | URL Path | Font |
|
||||
| `.woff2` | URL Path | Font |
|
||||
| `.woff` | URL Path | Font |
|
||||
| `.bmp` | URL Path | Image |
|
||||
| `.gif` | URL Path | Image |
|
||||
| `.jpeg` | URL Path | Image |
|
||||
| `.jpg` | URL Path | Image |
|
||||
| `.png` | URL Path | Image |
|
||||
| `.svg` | URL Path | Image |
|
||||
| `.md` | URL Path | Markdown File |
|
||||
| Extension | Exports | Purpose |
|
||||
| --------- | ------------- | ---------------------------- |
|
||||
| `.ts` | Script Module | TypeScript |
|
||||
| `.tsx` | Script Module | TypeScript and XML |
|
||||
| `.mts` | Script Module | ECMAScript Module TypeScript |
|
||||
| `.cts` | Script Module | CommonJS TypeScript |
|
||||
| `.js` | Script Module | JavaScript |
|
||||
| `.jsx` | Script Module | JavaScript and XML |
|
||||
| `.mjs` | Script Module | ECMAScript Module |
|
||||
| `.cjs` | Script Module | CommonJS Module |
|
||||
| `.json` | JSON Data | JSON Data |
|
||||
| `.yml` | JSON Data | YAML Data |
|
||||
| `.yaml` | JSON Data | YAML Data |
|
||||
| `.css` | classes | Style sheet |
|
||||
| `.eot` | URL Path | Font |
|
||||
| `.ttf` | URL Path | Font |
|
||||
| `.woff2` | URL Path | Font |
|
||||
| `.woff` | URL Path | Font |
|
||||
| `.bmp` | URL Path | Image |
|
||||
| `.gif` | URL Path | Image |
|
||||
| `.jpeg` | URL Path | Image |
|
||||
| `.jpg` | URL Path | Image |
|
||||
| `.png` | URL Path | Image |
|
||||
| `.svg` | URL Path | Image |
|
||||
| `.md` | URL Path | Markdown File |
|
||||
|
||||
## ECMAScript Modules
|
||||
|
||||
The Backstage tooling supports [ECMAScript modules (ESM)](https://nodejs.org/docs/latest-v22.x/api/esm.html) in Node.js packages. This includes support for all the script module file extensions listed above during local development, in built packages, in tests, and during type checking. [Dynamic imports](https://nodejs.org/docs/latest-v22.x/api/esm.html#import-expressions) can be used to load ESM-only packages from CommonJS and vice versa. There are however a couple of limitations to be aware of:
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
## Jest Configuration
|
||||
|
||||
|
||||
+2
-2
@@ -49,8 +49,8 @@
|
||||
"storybook": "yarn ./storybook run storybook",
|
||||
"techdocs-cli": "node scripts/techdocs-cli.js",
|
||||
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
|
||||
"test": "NODE_OPTIONS=--no-node-snapshot backstage-cli repo test",
|
||||
"test:all": "NODE_OPTIONS=--no-node-snapshot backstage-cli repo test --coverage",
|
||||
"test": "NODE_OPTIONS='--no-node-snapshot --experimental-vm-modules' backstage-cli repo test",
|
||||
"test:all": "NODE_OPTIONS='--no-node-snapshot --experimental-vm-modules' backstage-cli repo test --coverage",
|
||||
"test:e2e": "NODE_OPTIONS=--no-node-snapshot playwright test",
|
||||
"tsc": "tsc",
|
||||
"tsc:full": "backstage-cli repo clean && tsc --skipLibCheck false --incremental false"
|
||||
|
||||
@@ -103,7 +103,7 @@ export async function buildPgDatabaseConfig(
|
||||
Connector: CloudSqlConnector,
|
||||
IpAddressTypes,
|
||||
AuthTypes,
|
||||
} = await import('@google-cloud/cloud-sql-connector');
|
||||
} = require('@google-cloud/cloud-sql-connector') as typeof import('@google-cloud/cloud-sql-connector');
|
||||
const connector = new CloudSqlConnector();
|
||||
const clientOpts = await connector.getOptions({
|
||||
instanceConnectionName: config.connection.instance,
|
||||
|
||||
@@ -68,7 +68,7 @@ export class DynamicPluginManager implements DynamicPluginProvider {
|
||||
preferAlpha: options.preferAlpha,
|
||||
});
|
||||
const scannedPlugins = (await scanner.scanRoot()).packages;
|
||||
scanner.trackChanges();
|
||||
await scanner.trackChanges();
|
||||
const moduleLoader =
|
||||
options.moduleLoader ||
|
||||
new CommonJSModuleLoader({ logger: options.logger });
|
||||
|
||||
+2
-1
@@ -59,7 +59,8 @@ export async function startMemcachedContainer(
|
||||
image: string,
|
||||
): Promise<Instance> {
|
||||
// Lazy-load to avoid side-effect of importing testcontainers
|
||||
const { GenericContainer } = await import('testcontainers');
|
||||
const { GenericContainer } =
|
||||
require('testcontainers') as typeof import('testcontainers');
|
||||
|
||||
const container = await new GenericContainer(image)
|
||||
.withExposedPorts(11211)
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ export async function connectToExternalRedis(
|
||||
|
||||
export async function startRedisContainer(image: string): Promise<Instance> {
|
||||
// Lazy-load to avoid side-effect of importing testcontainers
|
||||
const { GenericContainer } = await import('testcontainers');
|
||||
const { GenericContainer } =
|
||||
require('testcontainers') as typeof import('testcontainers');
|
||||
|
||||
const container = await new GenericContainer(image)
|
||||
.withExposedPorts(6379)
|
||||
|
||||
@@ -72,7 +72,8 @@ export async function startMysqlContainer(image: string): Promise<{
|
||||
const password = uuid();
|
||||
|
||||
// Lazy-load to avoid side-effect of importing testcontainers
|
||||
const { GenericContainer } = await import('testcontainers');
|
||||
const { GenericContainer } =
|
||||
require('testcontainers') as typeof import('testcontainers');
|
||||
|
||||
const container = await new GenericContainer(image)
|
||||
.withExposedPorts(3306)
|
||||
|
||||
@@ -72,7 +72,8 @@ export async function startPostgresContainer(image: string): Promise<{
|
||||
const password = uuid();
|
||||
|
||||
// Lazy-load to avoid side-effect of importing testcontainers
|
||||
const { GenericContainer } = await import('testcontainers');
|
||||
const { GenericContainer } =
|
||||
require('testcontainers') as typeof import('testcontainers');
|
||||
|
||||
const container = await new GenericContainer(image)
|
||||
.withExposedPorts(5432)
|
||||
|
||||
@@ -210,10 +210,19 @@ function isPromise<T>(value: unknown | Promise<T>): value is Promise<T> {
|
||||
);
|
||||
}
|
||||
|
||||
// Same as in the backend-app-api, handles double defaults from dynamic imports
|
||||
function unwrapFeature(
|
||||
feature: BackendFeature | (() => BackendFeature),
|
||||
feature: BackendFeature | { default: BackendFeature },
|
||||
): BackendFeature {
|
||||
return typeof feature === 'function' ? feature() : feature;
|
||||
if ('$$type' in feature) {
|
||||
return feature;
|
||||
}
|
||||
|
||||
if ('default' in feature) {
|
||||
return feature.default;
|
||||
}
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
const backendInstancesToCleanUp = new Array<Backend>();
|
||||
|
||||
@@ -9,11 +9,7 @@ function getAbsolutePath(value: string): any {
|
||||
return dirname(require.resolve(join(value, 'package.json')));
|
||||
}
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
'../docs/**/*.mdx',
|
||||
'../src/components/**/*.mdx',
|
||||
'../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
|
||||
],
|
||||
stories: ['../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
staticDirs: ['../static'],
|
||||
addons: [
|
||||
getAbsolutePath('@storybook/addon-webpack5-compiler-swc'),
|
||||
|
||||
@@ -2,9 +2,6 @@ import React from 'react';
|
||||
import type { Preview, ReactRenderer } from '@storybook/react';
|
||||
import { withThemeByDataAttribute } from '@storybook/addon-themes';
|
||||
|
||||
// Storybook specific styles
|
||||
import '../docs/components/styles.css';
|
||||
|
||||
// Canon specific styles
|
||||
import '../src/css/core.css';
|
||||
import '../src/css/components.css';
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Unstyled } from '@storybook/blocks';
|
||||
import {
|
||||
Columns,
|
||||
Text,
|
||||
ComponentStatus,
|
||||
Banner,
|
||||
Title,
|
||||
Roadmap,
|
||||
} from './components';
|
||||
import { list } from './components/Roadmap/list';
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<img src="header.png" style={{ width: '100%', marginTop: '-16px' }} />
|
||||
|
||||
<Text style={{ marginTop: '64px', marginBottom: '24px' }}>
|
||||
Welcome to the Canon, the new design library for Backstage plugins. This
|
||||
project is still under active development but we will make sure to document
|
||||
the API as we go. We are aiming to improve the general UI of Backstage and
|
||||
plugins across Backstage. This new library will take time to build but we are
|
||||
building it incrementally with not conflict with the existing theming system.
|
||||
</Text>
|
||||
|
||||
<Banner variant="warning">
|
||||
This library is still under heavy construction. Please be aware that the API
|
||||
will change until we reach a stable release.
|
||||
</Banner>
|
||||
|
||||
<Title style={{ marginTop: '48px' }} type="h2">
|
||||
Component Status
|
||||
</Title>
|
||||
<Text>
|
||||
We are still in the process of documenting the API and building the
|
||||
components. You can use the statuses below to see what is ready and what is
|
||||
coming soon. If there is a component missing that you need, please let us know
|
||||
by opening an issue on GitHub.
|
||||
</Text>
|
||||
|
||||
<Columns style={{ marginTop: '32px' }}>
|
||||
<ComponentStatus
|
||||
name="Box"
|
||||
status="alpha"
|
||||
link="/?path=/docs/components-box--docs"
|
||||
/>
|
||||
<ComponentStatus
|
||||
name="Button"
|
||||
status="inProgress"
|
||||
link="/?path=/docs/components-button--docs"
|
||||
/>
|
||||
<ComponentStatus name="Checkbox" status="notStarted" />
|
||||
<ComponentStatus
|
||||
name="Container"
|
||||
status="alpha"
|
||||
link="/?path=/docs/components-container--docs"
|
||||
/>
|
||||
<ComponentStatus
|
||||
name="Grid"
|
||||
status="alpha"
|
||||
link="/?path=/story/components-grid--docs"
|
||||
/>
|
||||
<ComponentStatus name="Header" status="notStarted" />
|
||||
<ComponentStatus
|
||||
name="Icon"
|
||||
status="alpha"
|
||||
link="/?path=/docs/components-icon--docs"
|
||||
/>
|
||||
<ComponentStatus
|
||||
name="Inline"
|
||||
status="alpha"
|
||||
link="/?path=/story/components-inline--default"
|
||||
/>
|
||||
<ComponentStatus name="Input" status="notStarted" />
|
||||
<ComponentStatus name="Radio" status="notStarted" />
|
||||
<ComponentStatus name="Select" status="notStarted" />
|
||||
<ComponentStatus
|
||||
name="Stack"
|
||||
status="alpha"
|
||||
link="/?path=/docs/components-stack--default"
|
||||
/>
|
||||
<ComponentStatus name="Switch" status="notStarted" />
|
||||
<ComponentStatus name="Tabs" status="notStarted" />
|
||||
<ComponentStatus name="Tooltip" status="notStarted" />
|
||||
</Columns>
|
||||
|
||||
<Title style={{ marginTop: '48px' }} type="h2">
|
||||
Roadmap
|
||||
</Title>
|
||||
|
||||
<Roadmap list={list} />
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Unstyled, Source, Meta } from '@storybook/blocks';
|
||||
import { Title, Text, IconLibrary } from './components';
|
||||
|
||||
<Meta title="Core Concepts/Iconography" />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Iconography</Title>
|
||||
|
||||
<Text>
|
||||
All our default icons are provided by [Remix Icon](https://remixicon.com/). We
|
||||
don't import all icons to reduce the bundle size but we cherry pick a nice
|
||||
selection for you to use in your application. The list of names is set down
|
||||
below. To use an icon, you can use the `Icon` component and pass the name of
|
||||
the icon you want to use.
|
||||
</Text>
|
||||
|
||||
<Source code={`<Icon name="heart" />`} language="tsx" dark />
|
||||
|
||||
<IconLibrary />
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Unstyled, Source, Meta } from '@storybook/blocks';
|
||||
import { Title, Text, LayoutComponents } from './components';
|
||||
import * as Table from './components/Table';
|
||||
|
||||
<Meta title="Core Concepts/Layout" />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Layout</Title>
|
||||
<Text>
|
||||
Canon is made for extensibility. We built this library to make it easy for any
|
||||
Backstage plugin creator to be able to build their ideas at speed ensuring
|
||||
consistency across the rest of your ecosystem. Each component is designed to
|
||||
be editable to match your need but sometimes you want to have more control
|
||||
over the layout of your page. To help you with that, we created a set of
|
||||
layout components that you can use to build your own layouts. All of these
|
||||
components are built to extend on our theming system, making it easy for you
|
||||
to build your own layouts. Sometimes these components are not enough so we
|
||||
created a set of helpers to be used with any CSS-in-JS library.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Layout Components</Title>
|
||||
|
||||
<Text>
|
||||
We built a couple of layout components to help you build responsive elements
|
||||
that will be consistent with the rest of your Backstage instance. These
|
||||
components are opinionated and use TypeScript to ensure that the props you
|
||||
provide are the ones coming from the theme.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Stack direction="column" gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Inline gap="sm">
|
||||
<Box>Project 1</Box>
|
||||
<Box>Project 2</Box>
|
||||
</Inline>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<LayoutComponents />
|
||||
|
||||
<Title type="h2">Layout Helpers</Title>
|
||||
|
||||
<Text>
|
||||
Sometimes you want to use global tokens dynamically outside of React
|
||||
components. To help you with that we would like to provide a set of helpers
|
||||
that you can use in your code. These helpers are not available just yet but we
|
||||
are working on it.
|
||||
</Text>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,150 +0,0 @@
|
||||
import { Unstyled, Meta, Canvas, Source } from '@storybook/blocks';
|
||||
import {
|
||||
Columns,
|
||||
Text,
|
||||
ComponentStatus,
|
||||
Banner,
|
||||
Title,
|
||||
Roadmap,
|
||||
} from './components';
|
||||
import { list } from './components/Roadmap/list';
|
||||
import * as HeadingStories from '../src/components/Heading/Heading.stories';
|
||||
import * as TextStories from '../src/components/Text/Text.stories';
|
||||
import * as Table from './components/Table';
|
||||
import { Chip } from './components/Chip';
|
||||
|
||||
<Meta title="Core Concepts/Responsive" />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Responsive</Title>
|
||||
<Text>
|
||||
Canon is built on a responsive design system, meaning that the components are
|
||||
designed to adapt to different screen sizes. By default we offer a set of
|
||||
breakpoints that you can use to create responsive components.
|
||||
</Text>
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
Breakpoints
|
||||
</Title>
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Breakpoint prefix</Table.HeaderCell>
|
||||
<Table.HeaderCell>Minimum width</Table.HeaderCell>
|
||||
<Table.HeaderCell>CSS</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>xs</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>0px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`{ ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>sm</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>640px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`@media (min-width: 640px) { ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>md</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>768px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`@media (min-width: 768px) { ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>lg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>1024px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`@media (min-width: 1024px) { ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>xl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>1280px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`@media (min-width: 1280px) { ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>2xl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>1536px</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{`@media (min-width: 1536px) { ... }`}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
<Title type="h2">Responsive components</Title>
|
||||
|
||||
<Text>
|
||||
Canon components are designed to be responsive, meaning that they will adapt
|
||||
to different screen sizes. Not every component is responsive, but the ones
|
||||
that are will have a prop to control the responsive behavior.
|
||||
</Text>
|
||||
|
||||
<Text>
|
||||
The behaviour is the same for each component. For each prop, instead of adding
|
||||
the value, you add an object with the value and the breakpoint prefix.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`// Fixed value
|
||||
|
||||
<Button size="small">Button</Button>
|
||||
|
||||
// Responsive value
|
||||
|
||||
<Button size={{ xs: 'small', md: 'medium' }}>Button</Button>`} dark />
|
||||
|
||||
<Title type="h2">How to update breakpoints</Title>
|
||||
|
||||
<Text>
|
||||
The set of keys are not to be changed, but you can update the minimum width of
|
||||
each breakpoint in the theme provider.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<CanonProvider breakpoints={{
|
||||
xs: 0,
|
||||
sm: 640,
|
||||
md: 768,
|
||||
lg: 1024,
|
||||
xl: 1280,
|
||||
'2xl': 1536,
|
||||
}} />`}
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,231 +0,0 @@
|
||||
import { Unstyled, Source, Meta } from '@storybook/blocks';
|
||||
import { Title, Text, Chip } from './components';
|
||||
import * as Table from './components/Table';
|
||||
|
||||
<Meta title="Core Concepts/Theming" />{' '}
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Theming</Title>
|
||||
<Text>
|
||||
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.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Light & Dark modes</Title>
|
||||
<Text>
|
||||
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.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">How to create your own theme</Title>
|
||||
<Text>
|
||||
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.
|
||||
</Text>
|
||||
<Source
|
||||
code={`/** Light theme **/
|
||||
[data-theme='light'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-bg: #fff;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #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-accent: #1ed760;
|
||||
--canon-bg: #000;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #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 */
|
||||
}
|
||||
`}
|
||||
language="css"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h2">Colors</Title>
|
||||
<Text>
|
||||
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.
|
||||
</Text>
|
||||
<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-accent</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The accent color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The background color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-1</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The first surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-2</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The second surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The outline color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline-focus</Chip>
|
||||
</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>
|
||||
</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>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The secondary text color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
<Title type="h2">Typography</Title>
|
||||
<Text>
|
||||
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
|
||||
monospace font that we use for code blocks and tables.
|
||||
</Text>
|
||||
|
||||
<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-font-regular</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The sans-serif font for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-font-mono</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The monospace font for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
<Title type="h2">Spacing</Title>
|
||||
<Text>
|
||||
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.
|
||||
</Text>
|
||||
|
||||
<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-unit</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The base unit for the spacing system. Default value is `1em`
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xxs</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.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Unstyled, Meta, Canvas } from '@storybook/blocks';
|
||||
import {
|
||||
Columns,
|
||||
Text,
|
||||
ComponentStatus,
|
||||
Banner,
|
||||
Title,
|
||||
Roadmap,
|
||||
} from './components';
|
||||
import { list } from './components/Roadmap/list';
|
||||
import * as HeadingStories from '../src/components/Heading/Heading.stories';
|
||||
import * as TextStories from '../src/components/Text/Text.stories';
|
||||
|
||||
<Meta title="Core Concepts/Typography" />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Typography</Title>
|
||||
<Text>
|
||||
Canon offers a suite of typography components designed to seamlessly align
|
||||
with the rest of your Backstage instance. While you can customize their
|
||||
appearance to match your brand, the underlying API remains consistent and
|
||||
unchanged. Each component is built on a responsive structure, allowing you to
|
||||
define different typography values for various breakpoints.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Heading</Title>
|
||||
<Text>
|
||||
Headings are used to structure the content of your page. They are used to
|
||||
create a hierarchy of information and to make the content more readable. The
|
||||
best way to use add these headings to your page is to import the [Heading
|
||||
component](?path=/docs/components-heading--docs).
|
||||
</Text>
|
||||
|
||||
<Canvas of={HeadingStories.AllVariants} />
|
||||
|
||||
<Title type="h2">Text</Title>
|
||||
<Text>
|
||||
Canon provides four distinct text variants, each offering different font sizes
|
||||
carefully designed to cover the majority of use cases. These variants are
|
||||
versatile and can be paired with regular and bold of font weights. You can use
|
||||
the [Text component](?path=/docs/components-text--docs) to add text to your
|
||||
page.
|
||||
</Text>
|
||||
|
||||
<Canvas of={TextStories.AllVariants} />
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const Banner = ({
|
||||
children,
|
||||
variant = 'info',
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
variant?: 'info' | 'warning';
|
||||
}) => {
|
||||
return (
|
||||
<div className={`banner ${variant}`}>
|
||||
<div className="icon">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M4.00001 20V14C4.00001 9.58172 7.58173 6 12 6C16.4183 6 20 9.58172 20 14V20H21V22H3.00001V20H4.00001ZM6.00001 20H18V14C18 10.6863 15.3137 8 12 8C8.6863 8 6.00001 10.6863 6.00001 14V20ZM11 2H13V5H11V2ZM19.7782 4.80761L21.1924 6.22183L19.0711 8.34315L17.6569 6.92893L19.7782 4.80761ZM2.80762 6.22183L4.22183 4.80761L6.34315 6.92893L4.92894 8.34315L2.80762 6.22183ZM7.00001 14C7.00001 11.2386 9.23858 9 12 9V11C10.3432 11 9.00001 12.3431 9.00001 14H7.00001Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
.banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
|
||||
& > p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.info {
|
||||
background-color: #f2f2f2;
|
||||
border-color: #cdcdcd;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background-color: #fff2b9;
|
||||
border-color: #ffd000;
|
||||
color: #d79927;
|
||||
}
|
||||
|
||||
& .icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: rgba(215, 153, 39, 0.2);
|
||||
border-radius: 6px;
|
||||
margin-right: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const Chip = ({
|
||||
children,
|
||||
head = false,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
head?: boolean;
|
||||
}) => {
|
||||
return <span className={`chip ${head ? 'head' : ''}`}>{children}</span>;
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
padding: 0px 8px;
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
background-color: #f0f0f0;
|
||||
color: #5d5d5d;
|
||||
|
||||
&.head {
|
||||
background-color: #eaf2fd;
|
||||
color: #2563eb;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const Columns = ({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<div className="columns" style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
row-gap: 20px;
|
||||
column-gap: 80px;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const ComponentStatus = ({
|
||||
name,
|
||||
status = 'notStarted',
|
||||
style,
|
||||
link,
|
||||
}: {
|
||||
name: string;
|
||||
status: 'notStarted' | 'inProgress' | 'alpha' | 'beta' | 'stable';
|
||||
style?: React.CSSProperties;
|
||||
link?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="component-status" style={style}>
|
||||
{link ? (
|
||||
<a href={link} className="title">
|
||||
{name}
|
||||
</a>
|
||||
) : (
|
||||
<span className="title">{name}</span>
|
||||
)}
|
||||
<span className={`pill ${status}`}>
|
||||
{status === 'notStarted' && 'Not Started'}
|
||||
{status === 'inProgress' && 'In Progress'}
|
||||
{status === 'alpha' && 'Alpha'}
|
||||
{status === 'beta' && 'Beta'}
|
||||
{status === 'stable' && 'Stable'}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.component-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& .title {
|
||||
color: #3b59ff;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
& .pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
border-radius: 40px;
|
||||
padding: 0px 8px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
margin-left: 8px;
|
||||
|
||||
&.notStarted {
|
||||
background-color: #f2f2f2;
|
||||
border-color: #cdcdcd;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
&.inProgress {
|
||||
background-color: #fff2b9;
|
||||
border-color: #ffd000;
|
||||
color: #d79927;
|
||||
}
|
||||
|
||||
&.alpha {
|
||||
background-color: #d7f9d7;
|
||||
border-color: #4ed14a;
|
||||
color: #3a9837;
|
||||
}
|
||||
|
||||
&.beta {
|
||||
background-color: #d7f9d7;
|
||||
border-color: #4ed14a;
|
||||
color: #3a9837;
|
||||
}
|
||||
|
||||
&.stable {
|
||||
background-color: #d7f9d7;
|
||||
border-color: #4ed14a;
|
||||
color: #3a9837;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Icon } from '@backstage/canon';
|
||||
import type { IconNames } from '@backstage/canon';
|
||||
import { icons } from '../../../src/components/Icon/icons';
|
||||
import { Text } from '../Text/Text';
|
||||
|
||||
export const IconLibrary = () => {
|
||||
const iconsList = Object.keys(icons);
|
||||
|
||||
return (
|
||||
<div className="icon-library">
|
||||
{iconsList.map(icon => (
|
||||
<div key={icon} className="icon-library-item">
|
||||
<div className="icon-library-item-icon">
|
||||
<Icon name={icon as IconNames} />
|
||||
</div>
|
||||
<Text>{icon}</Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { IconLibrary } from './IconLibrary';
|
||||
@@ -1,22 +0,0 @@
|
||||
.icon-library {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.icon-library-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon-library-item-icon {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
border: 1px solid #d3d3d3;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { BoxSvg } from './svgs/box';
|
||||
import { StackSvg } from './svgs/stack';
|
||||
import { GridSvg } from './svgs/grid';
|
||||
import { InlineSvg } from './svgs/inline';
|
||||
import { ContainerSvg } from './svgs/container';
|
||||
|
||||
export const LayoutComponents = () => {
|
||||
return (
|
||||
<div className="layout-components">
|
||||
<div className="box">
|
||||
<a className="content" href="/?path=/docs/components-box--docs">
|
||||
<BoxSvg />
|
||||
</a>
|
||||
<div className="title">Box</div>
|
||||
<div className="description">The most basic layout component</div>
|
||||
</div>
|
||||
<div className="box">
|
||||
<a className="content" href="/?path=/docs/components-stack--docs">
|
||||
<StackSvg />
|
||||
</a>
|
||||
<div className="title">Stack</div>
|
||||
<div className="description">Arrange your components vertically</div>
|
||||
</div>
|
||||
<div className="box">
|
||||
<a className="content" href="/?path=/docs/components-grid--docs">
|
||||
<GridSvg />
|
||||
</a>
|
||||
<div className="title">Grid</div>
|
||||
<div className="description">Arrange your components in a grid</div>
|
||||
</div>
|
||||
<div className="box">
|
||||
<a className="content" href="/?path=/docs/components-inline--docs">
|
||||
<InlineSvg />
|
||||
</a>
|
||||
<div className="title">Inline</div>
|
||||
<div className="description">Arrange your components in a row</div>
|
||||
</div>
|
||||
<div className="box">
|
||||
<a className="content" href="/?path=/docs/components-container--docs">
|
||||
<ContainerSvg />
|
||||
</a>
|
||||
<div className="title">Container</div>
|
||||
<div className="description">A container for your components</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { LayoutComponents } from './LayoutComponents';
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.layout-components {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& .box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(33.33% - 0.67rem);
|
||||
margin-bottom: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
& .content {
|
||||
flex: none;
|
||||
background: linear-gradient(180deg, #f3f3f3 0%, #fff 100%);
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
& .title {
|
||||
font-size: 16px;
|
||||
transition: color 0.2s ease-in-out;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
& .description {
|
||||
font-size: 16px;
|
||||
color: #9e9e9e;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const BoxSvg = () => {
|
||||
return (
|
||||
<svg
|
||||
width="100"
|
||||
height="60"
|
||||
viewBox="0 0 100 60"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="100" height="60" rx="4" fill="black" fill-opacity="0.06" />
|
||||
<path
|
||||
d="M94.5 0.5H96C97.933 0.5 99.5 2.067 99.5 4V5.5"
|
||||
stroke="#4765FF"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M99.5 54.5L99.5 56C99.5 57.933 97.933 59.5 96 59.5L94.5 59.5"
|
||||
stroke="#4765FF"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.5 59.5L4 59.5C2.067 59.5 0.5 57.933 0.5 56L0.5 54.5"
|
||||
stroke="#4765FF"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.5 5.5L0.5 4C0.5 2.067 2.067 0.5 4 0.5L5.5 0.500001"
|
||||
stroke="#4765FF"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const ContainerSvg = () => {
|
||||
return (
|
||||
<svg
|
||||
width="126"
|
||||
height="111"
|
||||
viewBox="0 0 126 111"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask
|
||||
id="mask0_1922_1559"
|
||||
// style="mask-type:alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="13"
|
||||
height="111"
|
||||
>
|
||||
<rect width="13" height="111" fill="url(#paint0_linear_1922_1559)" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_1922_1559)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 44.7563L13.0259 51.6995L12.5259 52.5655L0.5 45.6224L1 44.7563Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 50.5655L13.0259 57.5087L12.5259 58.3747L0.5 51.4316L1 50.5655Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 56.3747L13.0259 63.3179L12.5259 64.1839L0.5 57.2408L1 56.3747Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 62.1839L13.0259 69.1271L12.5259 69.9931L0.5 63.05L1 62.1839Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 67.9931L13.0259 74.9363L12.5259 75.8023L0.5 68.8592L1 67.9931Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 73.8023L13.0259 80.7455L12.5259 81.6116L0.5 74.6684L1 73.8023Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 79.6115L13.0259 86.5547L12.5259 87.4208L0.5 80.4776L1 79.6115Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 85.4208L13.0259 92.3639L12.5259 93.23L0.5 86.2868L1 85.4208Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 91.23L13.0259 98.1731L12.5259 99.0392L0.5 92.096L1 91.23Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 97.0392L13.0259 103.982L12.5259 104.848L0.5 97.9052L1 97.0392Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 102.848L13.0259 109.792L12.5259 110.658L0.5 103.714L1 102.848Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 108.658L13.0259 115.601L12.5259 116.467L0.5 109.524L1 108.658Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 38.9471L13.0259 45.8903L12.5259 46.7563L0.5 39.8131L1 38.9471Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 33.1379L13.0259 40.0811L12.5259 40.9471L0.5 34.0039L1 33.1379Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 27.3287L13.0259 34.2719L12.5259 35.1379L0.5 28.1947L1 27.3287Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 21.5195L13.0259 28.4627L12.5259 29.3287L0.5 22.3855L1 21.5195Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 15.7103L13.0259 22.6535L12.5259 23.5195L0.5 16.5763L1 15.7103Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 9.90109L13.0259 16.8443L12.5259 17.7103L0.5 10.7671L1 9.90109Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 4.09188L13.0259 11.0351L12.5259 11.9011L0.5 4.95791L1 4.09188Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 -1.71732L13.0259 5.22586L12.5259 6.09188L0.5 -0.851298L1 -1.71732Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M1 -7.52653L13.0259 -0.583351L12.5259 0.282675L0.5 -6.66051L1 -7.52653Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M0 131L0 -17H1L1 131H0Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 131L12 -17H13L13 131H12Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
</g>
|
||||
<rect
|
||||
x="13"
|
||||
y="25.5"
|
||||
width="100"
|
||||
height="60"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<mask
|
||||
id="mask1_1922_1559"
|
||||
// style="mask-type:alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="113"
|
||||
y="0"
|
||||
width="13"
|
||||
height="111"
|
||||
>
|
||||
<rect
|
||||
x="113"
|
||||
width="13"
|
||||
height="111"
|
||||
fill="url(#paint1_linear_1922_1559)"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_1922_1559)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 44.7563L126.026 51.6995L125.526 52.5655L113.5 45.6224L114 44.7563Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 50.5655L126.026 57.5087L125.526 58.3747L113.5 51.4316L114 50.5655Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 56.3747L126.026 63.3179L125.526 64.1839L113.5 57.2408L114 56.3747Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 62.1839L126.026 69.1271L125.526 69.9931L113.5 63.05L114 62.1839Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 67.9931L126.026 74.9363L125.526 75.8023L113.5 68.8592L114 67.9931Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 73.8023L126.026 80.7455L125.526 81.6116L113.5 74.6684L114 73.8023Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 79.6115L126.026 86.5547L125.526 87.4208L113.5 80.4776L114 79.6115Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 85.4208L126.026 92.3639L125.526 93.23L113.5 86.2868L114 85.4208Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 91.23L126.026 98.1731L125.526 99.0392L113.5 92.096L114 91.23Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 97.0392L126.026 103.982L125.526 104.848L113.5 97.9052L114 97.0392Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 102.848L126.026 109.792L125.526 110.658L113.5 103.714L114 102.848Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 108.658L126.026 115.601L125.526 116.467L113.5 109.524L114 108.658Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 38.9471L126.026 45.8903L125.526 46.7563L113.5 39.8131L114 38.9471Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 33.1379L126.026 40.0811L125.526 40.9471L113.5 34.0039L114 33.1379Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 27.3287L126.026 34.2719L125.526 35.1379L113.5 28.1947L114 27.3287Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 21.5195L126.026 28.4627L125.526 29.3287L113.5 22.3855L114 21.5195Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 15.7103L126.026 22.6535L125.526 23.5195L113.5 16.5763L114 15.7103Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 9.90109L126.026 16.8443L125.526 17.7103L113.5 10.7671L114 9.90109Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 4.09188L126.026 11.0351L125.526 11.9011L113.5 4.95791L114 4.09188Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 -1.71732L126.026 5.22586L125.526 6.09188L113.5 -0.851298L114 -1.71732Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M114 -7.52653L126.026 -0.583351L125.526 0.282675L113.5 -6.66051L114 -7.52653Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M113 131L113 -17H114L114 131H113Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M125 131L125 -17H126L126 131H125Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1922_1559"
|
||||
x1="6.5"
|
||||
y1="0"
|
||||
x2="6.5"
|
||||
y2="111"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.05" stop-color="white" stop-opacity="0" />
|
||||
<stop offset="0.2" stop-color="white" />
|
||||
<stop offset="0.8" stop-color="white" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_1922_1559"
|
||||
x1="119.5"
|
||||
y1="0"
|
||||
x2="119.5"
|
||||
y2="111"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.05" stop-color="white" stop-opacity="0" />
|
||||
<stop offset="0.2" stop-color="white" />
|
||||
<stop offset="0.8" stop-color="white" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const GridSvg = () => {
|
||||
return (
|
||||
<svg
|
||||
width="166"
|
||||
height="61"
|
||||
viewBox="0 0 166 61"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
width="52"
|
||||
height="61"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M53.5 30L64.5 30V31L53.5 31L53.5 30Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64.5 24.5C64.5 24.2239 64.7239 24 65 24C65.2761 24 65.5 24.2239 65.5 24.5L65.5 36.5C65.5 36.7761 65.2761 37 65 37C64.7239 37 64.5 36.7761 64.5 36.5L64.5 24.5Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M52.5 24.5C52.5 24.2239 52.7239 24 53 24C53.2761 24 53.5 24.2239 53.5 24.5L53.5 36.5C53.5 36.7761 53.2761 37 53 37C52.7239 37 52.5 36.7761 52.5 36.5L52.5 24.5Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<rect
|
||||
x="65.5"
|
||||
y="0.5"
|
||||
width="100"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M115 36.5L115 25.5L116 25.5L116 36.5L115 36.5Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M109.5 25.5C109.224 25.5 109 25.2761 109 25V25C109 24.7239 109.224 24.5 109.5 24.5L121.5 24.5C121.776 24.5 122 24.7239 122 25V25C122 25.2761 121.776 25.5 121.5 25.5L109.5 25.5Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M109.5 36.5C109.224 36.5 109 36.2761 109 36V36C109 35.7239 109.224 35.5 109.5 35.5L121.5 35.5C121.776 35.5 122 35.7239 122 36V36C122 36.2761 121.776 36.5 121.5 36.5L109.5 36.5Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<rect
|
||||
x="65.5"
|
||||
y="36.5"
|
||||
width="100"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const InlineSvg = () => {
|
||||
return (
|
||||
<svg
|
||||
width="159"
|
||||
height="56"
|
||||
viewBox="0 0 159 56"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="24" height="24" rx="4" fill="black" fill-opacity="0.06" />
|
||||
<rect
|
||||
x="32"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
x="64"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
x="96"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
x="128"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
y="32"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<rect
|
||||
x="64"
|
||||
y="32"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<path
|
||||
d="M12 12H138.811C141.238 12 141.569 15.5106 139.185 15.9647L12.8145 40.0353C10.4306 40.4894 10.762 44 13.1888 44H77"
|
||||
stroke="#4765FF"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
export const StackSvg = () => {
|
||||
return (
|
||||
<svg
|
||||
width="100"
|
||||
height="96"
|
||||
viewBox="0 0 100 96"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="100" height="24" rx="4" fill="black" fill-opacity="0.06" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M49.5 36L49.5 25L50.5 25L50.5 36L49.5 36Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M44 25C43.7239 25 43.5 24.7761 43.5 24.5V24.5C43.5 24.2239 43.7239 24 44 24L56 24C56.2761 24 56.5 24.2239 56.5 24.5V24.5C56.5 24.7761 56.2761 25 56 25L44 25Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M44 36C43.7239 36 43.5 35.7761 43.5 35.5V35.5C43.5 35.2239 43.7239 35 44 35L56 35C56.2761 35 56.5 35.2239 56.5 35.5V35.5C56.5 35.7761 56.2761 36 56 36L44 36Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<rect
|
||||
y="36"
|
||||
width="100"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M49.5 72L49.5 61L50.5 61L50.5 72L49.5 72Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M44 61C43.7239 61 43.5 60.7761 43.5 60.5V60.5C43.5 60.2239 43.7239 60 44 60L56 60C56.2761 60 56.5 60.2239 56.5 60.5V60.5C56.5 60.7761 56.2761 61 56 61L44 61Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M44 72C43.7239 72 43.5 71.7761 43.5 71.5V71.5C43.5 71.2239 43.7239 71 44 71L56 71C56.2761 71 56.5 71.2239 56.5 71.5V71.5C56.5 71.7761 56.2761 72 56 72L44 72Z"
|
||||
fill="#4765FF"
|
||||
/>
|
||||
<rect
|
||||
y="72"
|
||||
width="100"
|
||||
height="24"
|
||||
rx="4"
|
||||
fill="black"
|
||||
fill-opacity="0.06"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import * as Table from '..';
|
||||
import { Chip } from '..';
|
||||
|
||||
// Modify the PropsTable component to accept a generic type
|
||||
export const PropsTable = <T extends Record<string, any>>({
|
||||
data,
|
||||
}: {
|
||||
data: T;
|
||||
}) => {
|
||||
return (
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell>Responsive</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(data).map(n => (
|
||||
<Table.Row key={n}>
|
||||
<Table.Cell>
|
||||
<Chip head>{n}</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{Array.isArray(data[n].type) ? (
|
||||
data[n].type.map((t: any) => <Chip key={t}>{t}</Chip>)
|
||||
) : (
|
||||
<Chip>{data[n].type}</Chip>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{data[n].responsive ? 'Yes' : 'No'}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
);
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export function getProps(styles: Record<string, any>) {
|
||||
return Object.keys(styles).reduce(
|
||||
(acc: Record<string, { type: any[]; responsive: boolean }>, n) => {
|
||||
const style = styles[n];
|
||||
|
||||
let values: string[] = [];
|
||||
|
||||
if (style.values) {
|
||||
// If values exist, use them
|
||||
values = Object.keys(style.values);
|
||||
} else if (style.mappings && style.mappings.length > 0) {
|
||||
// If mappings exist, use the first mapping's values
|
||||
const firstMapping = style.mappings[0];
|
||||
values = Object.keys(styles[firstMapping].values);
|
||||
} else {
|
||||
// Default to an empty array if neither values nor mappings exist
|
||||
values = [];
|
||||
}
|
||||
|
||||
acc[n] = {
|
||||
type: values,
|
||||
responsive: true,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, { type: string[]; responsive: boolean }>,
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { PropsTable } from './PropsTable';
|
||||
export { getProps } from './getProps';
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { RoadmapItem } from './list';
|
||||
|
||||
export const Roadmap = ({ list }: { list: RoadmapItem[] }) => {
|
||||
const orderList = ['inProgress', 'notStarted', 'completed'];
|
||||
return (
|
||||
<div className="roadmap">
|
||||
{list
|
||||
.sort(
|
||||
(a, b) => orderList.indexOf(a.status) - orderList.indexOf(b.status),
|
||||
)
|
||||
.map(Item)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({
|
||||
title,
|
||||
status = 'notStarted',
|
||||
}: {
|
||||
title: string;
|
||||
status: 'notStarted' | 'inProgress' | 'inReview' | 'completed';
|
||||
}) => {
|
||||
return (
|
||||
<div className={['roadmap-item', status].join(' ')}>
|
||||
<div className="left">
|
||||
<div className="dot" />
|
||||
<div className="title">{title}</div>
|
||||
</div>
|
||||
<span className="pill">
|
||||
{status === 'notStarted' && 'Not Started'}
|
||||
{status === 'inProgress' && 'In Progress'}
|
||||
{status === 'inReview' && 'Ready for Review'}
|
||||
{status === 'completed' && 'Completed'}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Roadmap } from './Roadmap';
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export type RoadmapItem = {
|
||||
title: string;
|
||||
status: 'notStarted' | 'inProgress' | 'inReview' | 'completed';
|
||||
};
|
||||
|
||||
export const list: RoadmapItem[] = [
|
||||
{
|
||||
title: 'Remove Vanilla Extract and use pure CSS instead',
|
||||
status: 'inProgress',
|
||||
},
|
||||
{
|
||||
title: 'Add collapsing across breakpoints for the Inline component',
|
||||
status: 'notStarted',
|
||||
},
|
||||
{
|
||||
title: 'Add reversing the order for the Inline component',
|
||||
status: 'notStarted',
|
||||
},
|
||||
{
|
||||
title: 'Set up Storybook',
|
||||
status: 'completed',
|
||||
},
|
||||
{
|
||||
title: 'Set up iconography',
|
||||
status: 'completed',
|
||||
},
|
||||
{
|
||||
title: 'Set up global tokens',
|
||||
status: 'inProgress',
|
||||
},
|
||||
{
|
||||
title: 'Set up theming system',
|
||||
status: 'inProgress',
|
||||
},
|
||||
{
|
||||
title: 'Create first pass at box component',
|
||||
status: 'completed',
|
||||
},
|
||||
{
|
||||
title: 'Create first pass at stack component',
|
||||
status: 'completed',
|
||||
},
|
||||
{
|
||||
title: 'Create first pass at inline component',
|
||||
status: 'completed',
|
||||
},
|
||||
];
|
||||
@@ -1,100 +0,0 @@
|
||||
.roadmap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item .left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item .dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.notStarted {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inProgress {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inReview {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.completed .title {
|
||||
color: #a2a2a2;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.notStarted .dot {
|
||||
background-color: #d1d1d1;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inProgress .dot {
|
||||
background-color: #ffd000;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inReview .dot {
|
||||
background-color: #4ed14a;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.completed .dot {
|
||||
background-color: #4ed14a;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item .title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item .pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
padding: 0px 8px;
|
||||
border-radius: 40px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-left: 8px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.notStarted .pill {
|
||||
background-color: #f2f2f2;
|
||||
border-color: #cdcdcd;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inProgress .pill {
|
||||
background-color: #fff2b9;
|
||||
border-color: #ffd000;
|
||||
color: #d79927;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.inReview .pill {
|
||||
background-color: #d7f9d7;
|
||||
border-color: #4ed14a;
|
||||
color: #3a9837;
|
||||
}
|
||||
|
||||
.roadmap .roadmap-item.completed .pill {
|
||||
background-color: #d7f9d7;
|
||||
border-color: #4ed14a;
|
||||
color: #3a9837;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const Root = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className="sb-table-wrapper">
|
||||
<table className="sb-table">{children}</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Header = ({ children }: { children: React.ReactNode }) => {
|
||||
return <thead>{children}</thead>;
|
||||
};
|
||||
|
||||
export const Body = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tbody>{children}</tbody>;
|
||||
};
|
||||
|
||||
export const HeaderRow = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tr>{children}</tr>;
|
||||
};
|
||||
|
||||
export const HeaderCell = ({ children }: { children: React.ReactNode }) => {
|
||||
return <th className="sb-table-cell sb-table-header-cell">{children}</th>;
|
||||
};
|
||||
|
||||
export const Row = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tr className="sb-table-row">{children}</tr>;
|
||||
};
|
||||
|
||||
export const Cell = ({ children }: { children: React.ReactNode }) => {
|
||||
return <td className="sb-table-cell">{children}</td>;
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Root, Header, Body, Row, Cell, HeaderRow, HeaderCell } from './Table';
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.sb-table-wrapper {
|
||||
border: 1px solid #e7e7e7;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
& .sb-table {
|
||||
width: 100%;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border-spacing: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
& .sb-table-cell {
|
||||
padding: 12px 16px !important;
|
||||
border: none !important;
|
||||
text-align: left;
|
||||
background-color: white !important;
|
||||
font-size: 16px;
|
||||
|
||||
& p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .sb-table-header-cell {
|
||||
background-color: #f5f5f5 !important;
|
||||
border-bottom: 1px solid #e7e7e7 !important;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
& .sb-table-row {
|
||||
border: none;
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
& .sb-table-chip {
|
||||
display: inline-block;
|
||||
font-size: 14px !important;
|
||||
border: 1px solid #e7e7e7;
|
||||
border-radius: 6px;
|
||||
padding: 0px 6px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
& .sb-table-type {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const Text = ({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<div className="sb-text" style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.sb-text {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
margin: 0;
|
||||
margin-bottom: 16px;
|
||||
color: #4f4f4f;
|
||||
|
||||
& p {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& code {
|
||||
font-size: 13px;
|
||||
color: #215cff;
|
||||
background-color: #f1f3fc;
|
||||
padding: 4px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
& a {
|
||||
color: #215cff;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid #215cff;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const Title = ({
|
||||
children,
|
||||
style,
|
||||
type = 'h1',
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
type?: 'h1' | 'h2' | 'h3';
|
||||
}) => {
|
||||
let Component = 'h1';
|
||||
if (type === 'h1') Component = 'h1';
|
||||
if (type === 'h2') Component = 'h2';
|
||||
if (type === 'h3') Component = 'h3';
|
||||
|
||||
return React.createElement(Component, {
|
||||
className: `sb-title ${type}`,
|
||||
style,
|
||||
children,
|
||||
});
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.sb-title {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
|
||||
&.h1 {
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
margin-bottom: 24px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
&.h2 {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 12px;
|
||||
margin-top: 52px;
|
||||
}
|
||||
|
||||
&.h3 {
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 12px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './Banner';
|
||||
export * from './Chip';
|
||||
export * from './Columns';
|
||||
export * from './ComponentStatus';
|
||||
export * from './IconLibrary';
|
||||
export * from './LayoutComponents';
|
||||
export * from './PropsTable';
|
||||
export * from './Roadmap';
|
||||
export * from './Table';
|
||||
export * from './Text';
|
||||
export * from './Title';
|
||||
@@ -1,15 +0,0 @@
|
||||
.sbdocs-content {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
|
||||
Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
|
||||
@import './Banner/styles.css';
|
||||
@import './Chip/styles.css';
|
||||
@import './Columns/styles.css';
|
||||
@import './ComponentStatus/styles.css';
|
||||
@import './IconLibrary/styles.css';
|
||||
@import './LayoutComponents/styles.css';
|
||||
@import './Roadmap/styles.css';
|
||||
@import './Table/styles.css';
|
||||
@import './Text/styles.css';
|
||||
@import './Title/styles.css';
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const spacePropsList = [
|
||||
'margin',
|
||||
'marginBottom',
|
||||
'marginLeft',
|
||||
'marginRight',
|
||||
'marginTop',
|
||||
'marginX',
|
||||
'marginY',
|
||||
'padding',
|
||||
'paddingBottom',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
'paddingTop',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
].reduce(
|
||||
(acc: { [key: string]: { type: string[]; responsive: boolean } }, prop) => {
|
||||
acc[prop] = {
|
||||
type: ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl'],
|
||||
responsive: true,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
+16
-11
@@ -27,8 +27,10 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"build": "yarn build:app && yarn build:css",
|
||||
"build-storybook": "storybook build",
|
||||
"build:app": "backstage-cli package build",
|
||||
"build:css": "node scripts/build-css.mjs",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
@@ -37,32 +39,35 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui-components/react": "^1.0.0-alpha.4",
|
||||
"@base-ui-components/react": "^1.0.0-alpha.5",
|
||||
"@remixicon/react": "^4.5.0",
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@chromatic-com/storybook": "^3.2.2",
|
||||
"@storybook/addon-essentials": "^8.4.7",
|
||||
"@storybook/addon-interactions": "^8.4.7",
|
||||
"@storybook/addon-essentials": "^8.5.0",
|
||||
"@storybook/addon-interactions": "^8.5.0",
|
||||
"@storybook/addon-styling-webpack": "^1.0.1",
|
||||
"@storybook/addon-themes": "^8.4.7",
|
||||
"@storybook/addon-webpack5-compiler-swc": "^1.0.5",
|
||||
"@storybook/blocks": "^8.4.7",
|
||||
"@storybook/react": "^8.4.7",
|
||||
"@storybook/react-webpack5": "^8.4.7",
|
||||
"@storybook/test": "^8.4.7",
|
||||
"@storybook/addon-themes": "^8.5.0",
|
||||
"@storybook/addon-webpack5-compiler-swc": "^2.0.0",
|
||||
"@storybook/blocks": "^8.5.0",
|
||||
"@storybook/react": "^8.5.0",
|
||||
"@storybook/react-webpack5": "^8.5.0",
|
||||
"@storybook/test": "^8.5.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"chalk": "^5.4.1",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"glob": "^11.0.1",
|
||||
"globals": "^15.11.0",
|
||||
"lightningcss": "^1.29.1",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"react": "^18.0.2",
|
||||
"react-dom": "^18.0.2",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"storybook": "^8.4.7"
|
||||
"storybook": "^8.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import { transform, bundle } from 'lightningcss';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { glob } from 'glob';
|
||||
/* eslint-enable no-restricted-imports */
|
||||
|
||||
// Check if core.css and components.css exist
|
||||
const cssDir = 'src/css';
|
||||
const distDir = 'dist/css';
|
||||
const componentsDir = 'src/components';
|
||||
|
||||
// Core files
|
||||
const cssFiles = [
|
||||
{ path: `${cssDir}/core.css`, newName: 'core.css' },
|
||||
{ path: `${cssDir}/components.css`, newName: 'components.css' },
|
||||
];
|
||||
|
||||
// Components files
|
||||
const componentsFiles = glob
|
||||
.sync('**/*.css', { cwd: componentsDir })
|
||||
.map(file => {
|
||||
const folderName = file.split('/')[0].toLocaleLowerCase('en-US');
|
||||
return { path: `${componentsDir}/${file}`, newName: `${folderName}.css` };
|
||||
});
|
||||
|
||||
// Combine core and components files
|
||||
cssFiles.push(...componentsFiles);
|
||||
|
||||
// Check if files exist
|
||||
cssFiles.forEach(file => {
|
||||
if (!fs.existsSync(file.path)) {
|
||||
console.error(`${file.originalName} does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the dist/css directory exists
|
||||
if (!fs.existsSync(distDir)) {
|
||||
fs.mkdirSync(distDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Bundle and transform files
|
||||
cssFiles.forEach(file => {
|
||||
let { code: bundleCode } = bundle({
|
||||
filename: file.path,
|
||||
});
|
||||
|
||||
let { code, map } = transform({
|
||||
filename: `${distDir}/${file.newName}`,
|
||||
code: bundleCode,
|
||||
minify: true,
|
||||
sourceMap: true,
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${distDir}/${file.newName}`, code);
|
||||
fs.writeFileSync(`${distDir}/${file.newName}.map`, map);
|
||||
|
||||
console.log(chalk.blue('CSS bundled: ') + file.newName);
|
||||
});
|
||||
|
||||
console.log(chalk.green('CSS files bundled successfully!'));
|
||||
@@ -1,112 +0,0 @@
|
||||
import { Meta, Unstyled, Source, Canvas } from '@storybook/blocks';
|
||||
import * as BoxStories from './Box.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components';
|
||||
import { spacePropsList } from '../../../docs/spaceProps';
|
||||
|
||||
<Meta of={BoxStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Box</Title>
|
||||
<Text>
|
||||
Box is the lowest-level component in Canon. We use it internally to build all
|
||||
of our components. It provides a consistent API for styling and layout.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Box } from "@backstage/canon";
|
||||
|
||||
<Box>Hello World!</Box>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2">API reference</Title>
|
||||
|
||||
<Title type="h3">Box</Title>
|
||||
<Text>
|
||||
This is the Box component, our lowest-level component. Here are all the
|
||||
available properties.
|
||||
</Text>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
alignItems: {
|
||||
type: ['stretch', 'start', 'center', 'end'],
|
||||
responsive: true,
|
||||
},
|
||||
border: {
|
||||
type: ['none', 'base', 'error', 'warning', 'selected'],
|
||||
responsive: true,
|
||||
},
|
||||
borderRadius: {
|
||||
type: ['none', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
display: {
|
||||
type: ['none', 'flex', 'block', 'inline'],
|
||||
responsive: true,
|
||||
},
|
||||
flexDirection: {
|
||||
type: ['row', 'column'],
|
||||
responsive: true,
|
||||
},
|
||||
flexWrap: {
|
||||
type: ['wrap', 'nowrap', 'wrap-reverse'],
|
||||
responsive: true,
|
||||
},
|
||||
gap: {
|
||||
type: ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl'],
|
||||
responsive: true,
|
||||
},
|
||||
justifyContent: {
|
||||
type: ['stretch', 'start', 'center', 'end', 'around', 'between'],
|
||||
responsive: true,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text style={{ marginTop: '16px' }}>
|
||||
Padding and margin are used to create space around your component using our
|
||||
predefined spacing tokens. We would recommend to use padding over margin to
|
||||
avoid collapsing margins but both are available.
|
||||
</Text>
|
||||
|
||||
<Canvas of={BoxStories.Padding} />
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
<Text>Here are some examples of how you can use the Box component.</Text>
|
||||
|
||||
<Title type="h3">Simple example</Title>
|
||||
<Text>A simple example of how to use the Box component.</Text>
|
||||
<Source
|
||||
code={`<Box padding="md" borderRadius="md">Hello World</Box>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
Most of the values can be defined per breakpoint, making it easy to create
|
||||
responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Box padding={{ xs: 'sm', md: 'md' }} borderRadius={{ xs: 'sm', md: 'md' }}>Hello World</Box>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,135 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as ButtonStories from './Button.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components/PropsTable/PropsTable';
|
||||
|
||||
<Meta of={ButtonStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Button</Title>
|
||||
<Text>A button component that can be used to trigger actions.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.Variants} />
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Button } from "@backstage/canon";
|
||||
|
||||
<Button>Click me</Button>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
size: {
|
||||
type: ['small', 'medium'],
|
||||
responsive: true,
|
||||
},
|
||||
variant: {
|
||||
type: ['primary', 'secondary', 'tertiary'],
|
||||
responsive: true,
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
|
||||
<Title type="h3">Variants</Title>
|
||||
<Text>Here's a view when buttons have different variants.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.Variants} />
|
||||
|
||||
<Source
|
||||
code={`<Inline alignY="center">
|
||||
<Button variant="primary">Primary</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="tertiary">Tertiary</Button>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Sizes</Title>
|
||||
<Text>Here's a view when buttons have different sizes.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.Sizes} />
|
||||
|
||||
<Source
|
||||
code={`<Inline alignY="center">
|
||||
<Button size="medium">Medium</Button>
|
||||
<Button size="small">Small</Button>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">With Icons</Title>
|
||||
<Text>Here's a view when buttons have icons.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.WithIcons} />
|
||||
|
||||
<Source
|
||||
code={`<Inline alignY="center">
|
||||
<Button iconStart="cloud">Button</Button>
|
||||
<Button iconEnd="chevronRight">Button</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Full width</Title>
|
||||
<Text>Here's a view when buttons are full width.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.FullWidth} />
|
||||
|
||||
<Source
|
||||
code={`<Stack style={{ width: '300px' }}>
|
||||
<Button iconStart="cloud">Button</Button>
|
||||
<Button iconEnd="chevronRight">Button</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Disabled</Title>
|
||||
<Text>Here's a view when buttons are disabled.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.Disabled} />
|
||||
|
||||
<Source code={`<Button disabled>Button</Button>`} language="tsx" dark />
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>Here's a view when buttons are responsive.</Text>
|
||||
|
||||
<Canvas of={ButtonStories.Responsive} />
|
||||
|
||||
<Source
|
||||
code={`<Button variant={{ xs: 'primary', sm: 'secondary', md: 'tertiary' }} size={{ xs: 'small', sm: 'medium' }}>
|
||||
Button
|
||||
</Button>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as CheckboxStories from './Checkbox.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components/PropsTable';
|
||||
|
||||
<Meta of={CheckboxStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Checkbox</Title>
|
||||
<Text>A checkbox component that can be used to trigger actions.</Text>
|
||||
|
||||
<Canvas of={CheckboxStories.Default} />
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Checkbox } from "@backstage/canon";
|
||||
|
||||
<Checkbox label="Checkbox" />
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
label: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
defaultChecked: {
|
||||
type: ['boolean', "'indeterminate'"],
|
||||
responsive: false,
|
||||
},
|
||||
checked: {
|
||||
type: ['boolean', "'indeterminate'"],
|
||||
responsive: false,
|
||||
},
|
||||
onChange: {
|
||||
type: "(checked: boolean | 'indeterminate') => void",
|
||||
responsive: false,
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
required: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,99 +0,0 @@
|
||||
import { Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as ContainerStories from './Container.stories';
|
||||
import { Title, Text, PropsTable, getProps } from '../../../docs/components';
|
||||
import { spacePropsList } from '../../../docs/spaceProps';
|
||||
|
||||
<Meta of={ContainerStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Container</Title>
|
||||
|
||||
<Text>
|
||||
The container component let you use our default max-width and center the
|
||||
content on the page.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source
|
||||
code={`import { Container } from "@backstage/canon";
|
||||
|
||||
<Container>Hello World!</Container>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
marginY: {
|
||||
type: spacePropsList.marginY.type,
|
||||
responsive: spacePropsList.marginY.responsive,
|
||||
},
|
||||
marginBottom: {
|
||||
type: spacePropsList.marginBottom.type,
|
||||
responsive: spacePropsList.marginBottom.responsive,
|
||||
},
|
||||
marginTop: {
|
||||
type: spacePropsList.marginTop.type,
|
||||
responsive: spacePropsList.marginTop.responsive,
|
||||
},
|
||||
paddingY: {
|
||||
type: spacePropsList.paddingY.type,
|
||||
responsive: spacePropsList.paddingY.responsive,
|
||||
},
|
||||
paddingBottom: {
|
||||
type: spacePropsList.paddingBottom.type,
|
||||
responsive: spacePropsList.paddingBottom.responsive,
|
||||
},
|
||||
paddingTop: {
|
||||
type: spacePropsList.paddingTop.type,
|
||||
responsive: spacePropsList.paddingTop.responsive,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
|
||||
<Title type="h3">Simple</Title>
|
||||
<Text>A simple example of how to use the Container component.</Text>
|
||||
|
||||
<Source
|
||||
code={`<Container>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Container>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive padding & margin</Title>
|
||||
<Text>
|
||||
The Container component also supports responsive values, making it easy to
|
||||
create responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Container paddingY={{ xs: 'sm', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Container>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,199 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as GridStories from './Grid.stories';
|
||||
import { Title, Text, PropsTable } from '../../../docs/components';
|
||||
import { spacePropsList } from '../../../docs/spaceProps';
|
||||
|
||||
<Meta of={GridStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Grid</Title>
|
||||
<Text>
|
||||
A layout component that helps to create simple column-based layouts as well as
|
||||
more complex ones.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Grid } from "@backstage/canon";
|
||||
|
||||
<Grid>
|
||||
<Grid.Item>Hello World</Grid.Item>
|
||||
</Grid>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2">API reference</Title>
|
||||
|
||||
<Title type="h3">Grid</Title>
|
||||
<Text>
|
||||
This is the grid container component. It will help to define the number of
|
||||
columns that will be used in the grid. You can also define the gap between the
|
||||
columns. All values are responsive.
|
||||
</Text>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
columns: {
|
||||
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'auto'],
|
||||
responsive: true,
|
||||
},
|
||||
gap: {
|
||||
type: ['xs', 'sm', 'md', 'lg', 'xl'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
required: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
required: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text style={{ marginTop: '16px' }}>
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
</Text>
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
<Title type="h3">Grid Item</Title>
|
||||
<Text>
|
||||
If you need more control over the columns, you can use the grid item
|
||||
component. This will give you access to `rowSpan`, `colSpan`, `start` and
|
||||
`end`. All values are responsive. This component is optional, you can use any
|
||||
elements directly if you prefer.
|
||||
</Text>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
colSpan: {
|
||||
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'full'],
|
||||
responsive: true,
|
||||
},
|
||||
rowSpan: {
|
||||
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'full'],
|
||||
responsive: true,
|
||||
},
|
||||
start: {
|
||||
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'auto'],
|
||||
responsive: true,
|
||||
},
|
||||
end: {
|
||||
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'auto'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
required: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
required: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
|
||||
<Title type="h3">Simple grid</Title>
|
||||
<Text>This is a simple grid with 3 columns and a gap of md.</Text>
|
||||
<Source
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Complex grid</Title>
|
||||
<Text>
|
||||
You can also use the grid item to create more complex layouts. In this example
|
||||
the first column will span 1 column and the second column will span 2 columns.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Grid.Item colSpan={1}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={2}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Mixing rows and columns</Title>
|
||||
<Text>
|
||||
The grid item component also supports the `rowSpan` prop, which allows you to
|
||||
span multiple rows within the grid layout. In this example, the first item
|
||||
will span 2 rows to achieve a dynamic and flexible grid structure.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Grid.Item colSpan={1} rowSpan={2}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={2}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={2}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
The grid component also supports responsive values. In this example the grid
|
||||
will have 1 column on small screens and 3 on large screens, with a gap of xs
|
||||
on small screens and md on large screens.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Grid columns={{ xs: 1, md: 3 }} gap={{ xs: 'xs', md: 'md' }}>
|
||||
<Grid.Item colSpan={{ xs: 1, md: 2 }}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={{ xs: 1, md: 1 }}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Start and End</Title>
|
||||
<Text>
|
||||
The start and end props can be used to position the item in the grid.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Grid.Item start={2} end={4}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as HeadingStories from './Heading.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components/PropsTable/PropsTable';
|
||||
|
||||
<Meta of={HeadingStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Heading</Title>
|
||||
|
||||
<Text>Headings are used to structure the content of your page.</Text>
|
||||
|
||||
<Canvas of={HeadingStories.Title1} />
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Heading } from "@backstage/canon";
|
||||
|
||||
<Heading variant="title1">Hello World!</Heading>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
variant: {
|
||||
type: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
<Text>Here are some examples of how you can use the Heading component.</Text>
|
||||
|
||||
<Title type="h3">All variants</Title>
|
||||
<Text>
|
||||
The `Heading` component has a `variant` prop that can be used to change the
|
||||
appearance of the heading.
|
||||
</Text>
|
||||
|
||||
<Canvas of={HeadingStories.AllVariants} />
|
||||
|
||||
<Source
|
||||
code={`<Stack gap="md">
|
||||
<Heading variant="title1">Title 1 Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
<Heading variant="title2">Title 2 Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
<Heading variant="title3">Title 3 Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
<Heading variant="title4">Title 4 Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
<Heading variant="title5">Title 5 Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
<Heading variant="display">Display Lorem ipsum dolor sit amet consectetur...</Heading>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
on the screen size.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Heading variant={{ xs: 'title1', md: 'title2' }}>Responsive Lorem ipsum dolor sit amet consectetur...</Heading>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as IconStories from './Icon.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components/PropsTable/PropsTable';
|
||||
import { icons } from './icons';
|
||||
|
||||
<Meta of={IconStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Icon</Title>
|
||||
|
||||
<Text>Icons are used to represent an action or a state.</Text>
|
||||
|
||||
<Canvas of={IconStories.Default} />
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Icon } from "@backstage/canon";
|
||||
|
||||
<Icon name="icon-name" />
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
name: {
|
||||
type: Object.keys(icons),
|
||||
responsive: false,
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,124 +0,0 @@
|
||||
import { Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as InlineStories from './Inline.stories';
|
||||
import { Title, Text, PropsTable, getProps } from '../../../docs/components';
|
||||
import { spacePropsList } from '../../../docs/spaceProps';
|
||||
|
||||
<Meta of={InlineStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Inline</Title>
|
||||
|
||||
<Text>
|
||||
The Inline component is used to create a horizontal layout of elements. By
|
||||
default it uses flex and flexWrap to make sure that your content always flows
|
||||
responsively.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source
|
||||
code={`import { Inline } from "@backstage/canon";
|
||||
|
||||
<Inline>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
align: {
|
||||
type: 'start | center | end',
|
||||
responsive: true,
|
||||
},
|
||||
alignY: {
|
||||
type: 'start | center | end',
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text style={{ marginTop: '16px' }}>
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
</Text>
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
|
||||
<Title type="h3">Simple</Title>
|
||||
<Text>A simple example of how to use the Inline component.</Text>
|
||||
|
||||
<Source
|
||||
code={`<Inline>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
The Inline component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Inline gap={{ xs: 'sm', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Align</Title>
|
||||
<Text>
|
||||
The Inline component also supports responsive alignment, making it easy to
|
||||
create responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Inline align={{ xs: 'left', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Align vertically</Title>
|
||||
<Text>
|
||||
The Inline component also supports responsive vertical alignment, making it
|
||||
easy to create responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Inline alignY={{ xs: 'top', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,126 +0,0 @@
|
||||
import { Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as StackStories from './Stack.stories';
|
||||
import { Title, Text, PropsTable, getProps } from '../../../docs/components';
|
||||
import { spacePropsList } from '../../../docs/spaceProps';
|
||||
|
||||
<Meta of={StackStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Stack</Title>
|
||||
|
||||
<Text>
|
||||
This is the stack container component. It will help to define the number of
|
||||
columns that will be used in the grid. You can also define the gap between the
|
||||
columns. All values are responsive.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source
|
||||
code={`import { Stack } from "@backstage/canon";
|
||||
|
||||
<Stack>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Stack>
|
||||
`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
align: {
|
||||
type: ['start', 'center', 'end'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
required: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
required: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text style={{ marginTop: '16px' }}>
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
</Text>
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
<Title type="h2">Common questions</Title>
|
||||
|
||||
<Title type="h3">Can I stack horizontally?</Title>
|
||||
<Text>
|
||||
The Stack component only allows for stacking elements vertically. If you want
|
||||
to create a column layout, please use the Grid component.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Grid>
|
||||
`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
|
||||
<Title type="h3">Simple</Title>
|
||||
<Text>
|
||||
A simple example of how to use the Stack component with a medium gap.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Stack gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
The Stack component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Stack gap={{ xs: 'sm', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Align</Title>
|
||||
<Text>
|
||||
The Stack component also supports responsive alignment, making it easy to
|
||||
create responsive designs.
|
||||
</Text>
|
||||
<Source
|
||||
code={`<Stack align={{ xs: 'left', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -1,5 +0,0 @@
|
||||
import { Meta, Controls } from '@storybook/blocks';
|
||||
|
||||
<Meta title="Components/Table" />
|
||||
|
||||
# Table
|
||||
@@ -1,101 +0,0 @@
|
||||
import { Canvas, Meta, Unstyled, Source } from '@storybook/blocks';
|
||||
import * as TextStories from './Text.stories';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { PropsTable } from '../../../docs/components/PropsTable/PropsTable';
|
||||
|
||||
<Meta of={TextStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Text</Title>
|
||||
|
||||
<Text>The `Text` component is used to display content on your page.</Text>
|
||||
|
||||
<Canvas of={TextStories.Default} />
|
||||
|
||||
<Title type="h2">Usage</Title>
|
||||
<Source code={`import { Text } from "@backstage/canon";
|
||||
|
||||
<Text>Hello World!</Text>`} language="tsx" dark />
|
||||
|
||||
<Title type="h2" style={{ marginBottom: '16px' }}>
|
||||
API reference
|
||||
</Title>
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
variant: {
|
||||
type: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
|
||||
responsive: true,
|
||||
},
|
||||
weight: {
|
||||
type: ['regular', 'bold'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Title type="h2">Examples</Title>
|
||||
<Text>Here are some examples of how you can use the Text component.</Text>
|
||||
|
||||
<Title type="h3">All variants</Title>
|
||||
<Text>
|
||||
The `Text` component has a `variant` prop that can be used to change the
|
||||
appearance of the text.
|
||||
</Text>
|
||||
|
||||
<Canvas of={TextStories.AllVariants} />
|
||||
|
||||
<Source
|
||||
code={`<Stack gap="md">
|
||||
<Text variant="subtitle">Subtitle Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
<Text variant="body">Body Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
<Text variant="caption">Caption Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
<Text variant="label">Label Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">All weights</Title>
|
||||
<Text>
|
||||
The `Text` component has a `weight` prop that can be used to change the
|
||||
appearance of the text.
|
||||
</Text>
|
||||
|
||||
<Canvas of={TextStories.AllWeights} />
|
||||
|
||||
<Source
|
||||
code={`<Stack gap="md">
|
||||
<Text weight="regular">Regular Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
<Text weight="bold">Bold Lorem ipsum dolor sit amet consectetur...</Text>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h3">Responsive</Title>
|
||||
<Text>
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
on the screen size.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Text variant={{ xs: 'label', md: 'body' }}>Responsive Lorem ipsum dolor sit amet consectetur...</Text>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
</Unstyled>
|
||||
@@ -72,6 +72,8 @@ export interface BackstagePackageJson {
|
||||
[key: string]: string;
|
||||
};
|
||||
// (undocumented)
|
||||
type?: 'module' | 'commonjs';
|
||||
// (undocumented)
|
||||
types?: string;
|
||||
// (undocumented)
|
||||
typesVersions?: Record<string, Record<string, string[]>>;
|
||||
|
||||
@@ -43,6 +43,8 @@ export interface BackstagePackageJson {
|
||||
// that the package bundles all of its dependencies in its build output.
|
||||
bundled?: boolean;
|
||||
|
||||
type?: 'module' | 'commonjs';
|
||||
|
||||
backstage?: {
|
||||
role?: PackageRole;
|
||||
moved?: string;
|
||||
|
||||
+98
-62
@@ -31,6 +31,14 @@ const FRONTEND_ROLES = [
|
||||
'frontend-plugin-module',
|
||||
];
|
||||
|
||||
const NODE_ROLES = [
|
||||
'backend',
|
||||
'cli',
|
||||
'node-library',
|
||||
'backend-plugin',
|
||||
'backend-plugin-module',
|
||||
];
|
||||
|
||||
const envOptions = {
|
||||
oldTests: Boolean(process.env.BACKSTAGE_OLD_TESTS),
|
||||
};
|
||||
@@ -130,11 +138,97 @@ const transformIgnorePattern = [
|
||||
].join('|');
|
||||
|
||||
// Provides additional config that's based on the role of the target package
|
||||
function getRoleConfig(role) {
|
||||
function getRoleConfig(role, pkgJson) {
|
||||
// Only Node.js package roles support native ESM modules, frontend and common
|
||||
// packages are always transpiled to CommonJS.
|
||||
const moduleOpts = NODE_ROLES.includes(role)
|
||||
? {
|
||||
module: {
|
||||
ignoreDynamic: true,
|
||||
exportInteropAnnotation: true,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const transform = {
|
||||
'\\.(mjs|cjs|js)$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
...moduleOpts,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'ecmascript',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.jsx$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'ecmascript',
|
||||
jsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.(mts|cts|ts)$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
...moduleOpts,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.tsx$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.(bmp|gif|jpg|jpeg|png|ico|webp|frag|xml|svg|eot|woff|woff2|ttf)$':
|
||||
require.resolve('./jestFileTransform.js'),
|
||||
'\\.(yaml)$': require.resolve('./jestYamlTransform'),
|
||||
};
|
||||
if (FRONTEND_ROLES.includes(role)) {
|
||||
return { testEnvironment: require.resolve('jest-environment-jsdom') };
|
||||
return {
|
||||
testEnvironment: require.resolve('jest-environment-jsdom'),
|
||||
transform,
|
||||
};
|
||||
}
|
||||
return { testEnvironment: require.resolve('jest-environment-node') };
|
||||
return {
|
||||
testEnvironment: require.resolve('jest-environment-node'),
|
||||
moduleFileExtensions: [...SRC_EXTS, 'json', 'node'],
|
||||
// Jest doesn't let us dynamically detect type=module per transformed file,
|
||||
// so we have to assume that if the entry point is ESM, all TS files are
|
||||
// ESM.
|
||||
//
|
||||
// This means you can't switch a package to type=module until all of its
|
||||
// monorepo dependencies are also type=module or does not contain any .ts
|
||||
// files.
|
||||
extensionsToTreatAsEsm:
|
||||
pkgJson.type === 'module' ? ['.ts', '.mts'] : ['.mts'],
|
||||
transform,
|
||||
};
|
||||
}
|
||||
|
||||
async function getProjectConfig(targetPath, extraConfig, extraOptions) {
|
||||
@@ -160,64 +254,6 @@ async function getProjectConfig(targetPath, extraConfig, extraOptions) {
|
||||
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
|
||||
},
|
||||
|
||||
transform: {
|
||||
'\\.(mjs|cjs|js)$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'ecmascript',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.jsx$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'ecmascript',
|
||||
jsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.ts$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.tsx$': [
|
||||
require.resolve('./jestSwcTransform'),
|
||||
{
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'\\.(bmp|gif|jpg|jpeg|png|ico|webp|frag|xml|svg|eot|woff|woff2|ttf)$':
|
||||
require.resolve('./jestFileTransform.js'),
|
||||
'\\.(yaml)$': require.resolve('./jestYamlTransform'),
|
||||
},
|
||||
|
||||
// A bit more opinionated
|
||||
testMatch: [`**/*.test.{${SRC_EXTS.join(',')}}`],
|
||||
|
||||
@@ -226,7 +262,7 @@ async function getProjectConfig(targetPath, extraConfig, extraOptions) {
|
||||
: require.resolve('./jestCachingModuleLoader'),
|
||||
|
||||
transformIgnorePatterns: [`/node_modules/(?:${transformIgnorePattern})/`],
|
||||
...getRoleConfig(pkgJson.backstage?.role),
|
||||
...getRoleConfig(pkgJson.backstage?.role, pkgJson),
|
||||
};
|
||||
|
||||
options.setupFilesAfterEnv = options.setupFilesAfterEnv || [];
|
||||
|
||||
@@ -19,6 +19,11 @@ const { default: JestRuntime } = require('jest-runtime');
|
||||
const scriptTransformCache = new Map();
|
||||
|
||||
module.exports = class CachingJestRuntime extends JestRuntime {
|
||||
constructor(config, ...restAgs) {
|
||||
super(config, ...restAgs);
|
||||
this.allowLoadAsEsm = config.extensionsToTreatAsEsm.includes('.mts');
|
||||
}
|
||||
|
||||
// This may or may not be a good idea. Theoretically I don't know why this would impact
|
||||
// test correctness and flakiness, but it seems like it may introduce flakiness and strange failures.
|
||||
// It does seem to speed up test execution by a fair amount though.
|
||||
@@ -33,4 +38,13 @@ module.exports = class CachingJestRuntime extends JestRuntime {
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
// Unfortunately we need to use this unstable API to make sure that .js files
|
||||
// are only loaded as modules where ESM is supported, i.e. Node.js packages.
|
||||
unstable_shouldLoadAsEsm(path, ...restArgs) {
|
||||
if (!this.allowLoadAsEsm) {
|
||||
return false;
|
||||
}
|
||||
return super.unstable_shouldLoadAsEsm(path, ...restArgs);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,10 +23,16 @@ function createTransformer(config) {
|
||||
...config,
|
||||
});
|
||||
const process = (source, filePath, jestOptions) => {
|
||||
// Skip transformation of .js files without ESM syntax, we never transform from CJS to ESM
|
||||
if (filePath.endsWith('.js') && !ESM_REGEX.test(source)) {
|
||||
return { code: source };
|
||||
}
|
||||
|
||||
// Skip transformation of .mjs files, they should only be used if ESM support is available
|
||||
if (filePath.endsWith('.mjs')) {
|
||||
return { code: source };
|
||||
}
|
||||
|
||||
return swcTransformer.process(source, filePath, jestOptions);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { pathToFileURL } = require('url');
|
||||
const { transformSync } = require('@swc/core');
|
||||
const { addHook } = require('pirates');
|
||||
const { Module } = require('module');
|
||||
@@ -55,7 +56,10 @@ addHook(
|
||||
const transformed = transformSync(code, {
|
||||
filename,
|
||||
sourceMaps: 'inline',
|
||||
module: { type: 'commonjs' },
|
||||
module: {
|
||||
type: 'commonjs',
|
||||
ignoreDynamic: true,
|
||||
},
|
||||
jsc: {
|
||||
target: 'es2022',
|
||||
parser: {
|
||||
@@ -76,3 +80,8 @@ addHook(
|
||||
},
|
||||
{ extensions: ['.js', '.cjs'], ignoreNodeModules: true },
|
||||
);
|
||||
|
||||
// Register module hooks, used by "type": "module" in package.json, .mjs and
|
||||
// .mts files, as well as dynamic import(...)s, although dynamic imports will be
|
||||
// handled be the CommonJS hooks in this file if what it points to is CommonJS.
|
||||
Module.register('./nodeTransformHooks.mjs', pathToFileURL(__filename));
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { dirname, extname, resolve as resolvePath } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { transformFile } from '@swc/core';
|
||||
import { isBuiltin } from 'node:module';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
// @ts-check
|
||||
|
||||
// No explicit file extension, no type in package.json
|
||||
const DEFAULT_MODULE_FORMAT = 'commonjs';
|
||||
|
||||
// Source file extensions to look for when using bundle resolution strategy
|
||||
const SRC_EXTS = ['.ts', '.js'];
|
||||
const TS_EXTS = ['.ts', '.mts', '.cts'];
|
||||
const moduleTypeTable = {
|
||||
'.mjs': 'module',
|
||||
'.mts': 'module',
|
||||
'.cjs': 'commonjs',
|
||||
'.cts': 'commonjs',
|
||||
'.ts': undefined,
|
||||
'.js': undefined,
|
||||
};
|
||||
|
||||
/** @type {import('module').ResolveHook} */
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
// Built-in modules are handled by the default resolver
|
||||
if (isBuiltin(specifier)) {
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
|
||||
const ext = extname(specifier);
|
||||
|
||||
// Unless there's an explicit import attribute, JSON files are loaded with our custom loader that's defined below.
|
||||
if (ext === '.json' && !context.importAttributes?.type) {
|
||||
const jsonResult = await nextResolve(specifier, context);
|
||||
return {
|
||||
...jsonResult,
|
||||
format: 'commonjs',
|
||||
importAttributes: { type: 'json' },
|
||||
};
|
||||
}
|
||||
|
||||
// Anything else with an explicit extension is handled by the default
|
||||
// resolver, except that we help determine the module type where needed.
|
||||
if (ext !== '') {
|
||||
return withDetectedModuleType(await nextResolve(specifier, context));
|
||||
}
|
||||
|
||||
// Other external modules are handled by the default resolver, but again we
|
||||
// help determine the module type where needed.
|
||||
if (!specifier.startsWith('.')) {
|
||||
return withDetectedModuleType(await nextResolve(specifier, context));
|
||||
}
|
||||
|
||||
// The rest of this function handles the case of resolving imports that do not
|
||||
// specify any extension and might point to a directory with an `index.*`
|
||||
// file. We resolve those using the same logic as most JS bundlers would, with
|
||||
// the addition of checking if there's an explicit module format listed in the
|
||||
// closest `package.json` file.
|
||||
//
|
||||
// We use a bundle resolution strategy in order to keep code consistent across
|
||||
// Backstage codebases that contains code both for Web and Node.js, and to
|
||||
// support packages with common code that can be used in both environments.
|
||||
try {
|
||||
// This is expected to throw, but in the event that this module specifier is
|
||||
// supported we prefer to use the default resolver.
|
||||
return await nextResolve(specifier, context);
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
|
||||
const spec = `${specifier}${specifier.endsWith('/') ? '' : '/'}index`;
|
||||
const resolved = await resolveWithoutExt(spec, context, nextResolve);
|
||||
if (resolved) {
|
||||
return withDetectedModuleType(resolved);
|
||||
}
|
||||
} else if (error.code === 'ERR_MODULE_NOT_FOUND') {
|
||||
const resolved = await resolveWithoutExt(specifier, context, nextResolve);
|
||||
if (resolved) {
|
||||
return withDetectedModuleType(resolved);
|
||||
}
|
||||
}
|
||||
|
||||
// Unexpected error or no resolution found
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the `format` field in the resolved object based on the closest `package.json` file.
|
||||
*
|
||||
* @param {import('module').ResolveFnOutput} resolved
|
||||
* @returns {Promise<import('module').ResolveFnOutput>}
|
||||
*/
|
||||
async function withDetectedModuleType(resolved) {
|
||||
// Already has an explicit format
|
||||
if (resolved.format) {
|
||||
return resolved;
|
||||
}
|
||||
// Happens in Node.js v22 when there's a package.json without an explicit "type" field. Use the default.
|
||||
if (resolved.format === null) {
|
||||
return { ...resolved, format: DEFAULT_MODULE_FORMAT };
|
||||
}
|
||||
|
||||
const ext = extname(resolved.url);
|
||||
|
||||
const explicitFormat = moduleTypeTable[ext];
|
||||
if (explicitFormat) {
|
||||
return {
|
||||
...resolved,
|
||||
format: explicitFormat,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(Rugvip): Afaik this should never happen and we can remove this check, but want it here for a little while to verify.
|
||||
if (ext === '.js') {
|
||||
throw new Error('Unexpected .js file without explicit format');
|
||||
}
|
||||
|
||||
// TODO(Rugvip): Does this need caching? kept it simple for now but worth exploring
|
||||
const packageJsonPath = await findPackageJSON(fileURLToPath(resolved.url));
|
||||
if (!packageJsonPath) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
||||
return {
|
||||
...resolved,
|
||||
format: packageJson.type ?? DEFAULT_MODULE_FORMAT,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest package.json file from the given path.
|
||||
*
|
||||
* TODO(Rugvip): This can be replaced with the Node.js built-in with the same name once it is stable.
|
||||
* @param {string} startPath
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
async function findPackageJSON(startPath) {
|
||||
let path = startPath;
|
||||
|
||||
// Some confidence check to avoid infinite loop
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const packagePath = resolvePath(path, 'package.json');
|
||||
if (existsSync(packagePath)) {
|
||||
return packagePath;
|
||||
}
|
||||
|
||||
const newPath = dirname(path);
|
||||
if (newPath === path) {
|
||||
return undefined;
|
||||
}
|
||||
path = newPath;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Iteration limit reached when searching for package.json at ${startPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {import('module').ResolveHook} */
|
||||
async function resolveWithoutExt(specifier, context, nextResolve) {
|
||||
for (const tryExt of SRC_EXTS) {
|
||||
try {
|
||||
const resolved = await nextResolve(specifier + tryExt, {
|
||||
...context,
|
||||
format: 'commonjs',
|
||||
});
|
||||
return {
|
||||
...resolved,
|
||||
format: moduleTypeTable[tryExt] ?? resolved.format,
|
||||
};
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @type {import('module').LoadHook} */
|
||||
export async function load(url, context, nextLoad) {
|
||||
// Non-file URLs are handled by the default loader
|
||||
if (!url.startsWith('file://')) {
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
|
||||
// JSON files loaded as CommonJS are handled by this custom loader, because
|
||||
// the default one doesn't work. For JSON loading to work we'd need the
|
||||
// synchronous hooks that aren't supported yet, or avoid using the CommonJS
|
||||
// compatibility.
|
||||
if (
|
||||
context.format === 'commonjs' &&
|
||||
context.importAttributes?.type === 'json'
|
||||
) {
|
||||
try {
|
||||
// TODO(Rugvip): Make sure this is valid JSON
|
||||
const content = await readFile(fileURLToPath(url), 'utf8');
|
||||
return {
|
||||
source: `module.exports = (${content})`,
|
||||
format: 'commonjs',
|
||||
shortCircuit: true,
|
||||
};
|
||||
} catch {
|
||||
// Let the default loader generate the error
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
}
|
||||
|
||||
const ext = extname(url);
|
||||
|
||||
// Non-TS files are handled by the default loader
|
||||
if (!TS_EXTS.includes(ext)) {
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
|
||||
const format = context.format ?? DEFAULT_MODULE_FORMAT;
|
||||
|
||||
// We have two choices at this point, we can either transform CommonJS files
|
||||
// and return the transformed source code, or let the default loader handle
|
||||
// them. If we transform them ourselves we will enter CommonJS compatibility
|
||||
// mode in the new module system in Node.js, this effectively means all
|
||||
// CommonJS loaded via `require` calls from this point will all be treated as
|
||||
// if it was loaded via `import` calls from modules.
|
||||
//
|
||||
// The CommonJS compatibility layer will try to identify named exports and
|
||||
// make them available directly, which is convenient as it avoids things like
|
||||
// `import(...).then(m => m.default.foo)`, allowing you to instead write
|
||||
// `import(...).then(m => m.foo)`. The compatibility layer doesn't always work
|
||||
// all that well though, and can lead to module loading issues in many cases,
|
||||
// especially for older code.
|
||||
|
||||
// This `if` block opts-out of using CommonJS compatibility mode by default,
|
||||
// and instead leaves it to our existing loader to transform CommonJS. We do
|
||||
// however use compatibility mode for the more explicit .cts file extension,
|
||||
// allows for a way to opt-in to the new behavior.
|
||||
//
|
||||
// TODO(Rugvip): Once the synchronous hooks API is available for us to use, we might be able to adopt that instead
|
||||
if (format === 'commonjs' && ext !== '.cts') {
|
||||
return nextLoad(url, { ...context, format });
|
||||
}
|
||||
|
||||
const transformed = await transformFile(fileURLToPath(url), {
|
||||
sourceMaps: 'inline',
|
||||
module: {
|
||||
type: format === 'module' ? 'es6' : 'commonjs',
|
||||
ignoreDynamic: true,
|
||||
|
||||
// This helps the Node.js CommonJS compat layer identify named exports.
|
||||
exportInteropAnnotation: true,
|
||||
},
|
||||
jsc: {
|
||||
target: 'es2022',
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
...context,
|
||||
shortCircuit: true,
|
||||
source: transformed.code,
|
||||
format,
|
||||
responseURL: url,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowJs": true,
|
||||
"declaration": true,
|
||||
"declarationMap": false,
|
||||
|
||||
@@ -24,6 +24,6 @@ import chalk from 'chalk';
|
||||
),
|
||||
);
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(import('./modules/config/alpha').then(m => m.default));
|
||||
initializer.add(import('./modules/config/alpha'));
|
||||
await initializer.run();
|
||||
})();
|
||||
|
||||
@@ -43,7 +43,7 @@ export function registerRepoCommand(program: Command) {
|
||||
'--minify',
|
||||
'Minify the generated code. Does not apply to app package (app is minified by default).',
|
||||
)
|
||||
.action(lazy(() => import('./repo/build').then(m => m.command)));
|
||||
.action(lazy(() => import('./repo/build'), 'command'));
|
||||
|
||||
command
|
||||
.command('lint')
|
||||
@@ -70,7 +70,7 @@ export function registerRepoCommand(program: Command) {
|
||||
'Set the success cache location, (default: node_modules/.cache/backstage-cli)',
|
||||
)
|
||||
.option('--fix', 'Attempt to automatically fix violations')
|
||||
.action(lazy(() => import('./repo/lint').then(m => m.command)));
|
||||
.action(lazy(() => import('./repo/lint'), 'command'));
|
||||
|
||||
command
|
||||
.command('fix')
|
||||
@@ -83,20 +83,18 @@ export function registerRepoCommand(program: Command) {
|
||||
'--check',
|
||||
'Fail if any packages would have been changed by the command',
|
||||
)
|
||||
.action(lazy(() => import('./repo/fix').then(m => m.command)));
|
||||
.action(lazy(() => import('./repo/fix'), 'command'));
|
||||
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache and output directories')
|
||||
.action(lazy(() => import('./repo/clean').then(m => m.command)));
|
||||
.action(lazy(() => import('./repo/clean'), 'command'));
|
||||
|
||||
command
|
||||
.command('list-deprecations')
|
||||
.description('List deprecations')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(
|
||||
lazy(() => import('./repo/list-deprecations').then(m => m.command)),
|
||||
);
|
||||
.action(lazy(() => import('./repo/list-deprecations'), 'command'));
|
||||
|
||||
command
|
||||
.command('test')
|
||||
@@ -118,7 +116,7 @@ export function registerRepoCommand(program: Command) {
|
||||
'Show help for Jest CLI options, which are passed through',
|
||||
)
|
||||
.description('Run tests, forwarding args to Jest, defaulting to watch mode')
|
||||
.action(lazy(() => import('./repo/test').then(m => m.command)));
|
||||
.action(lazy(() => import('./repo/test'), 'command'));
|
||||
}
|
||||
|
||||
export function registerScriptCommand(program: Command) {
|
||||
@@ -139,7 +137,7 @@ export function registerScriptCommand(program: Command) {
|
||||
)
|
||||
.option('--require <path>', 'Add a --require argument to the node process')
|
||||
.option('--link <path>', 'Link an external workspace for module resolution')
|
||||
.action(lazy(() => import('./start').then(m => m.command)));
|
||||
.action(lazy(() => import('./start'), 'command'));
|
||||
|
||||
command
|
||||
.command('build')
|
||||
@@ -163,7 +161,7 @@ export function registerScriptCommand(program: Command) {
|
||||
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
|
||||
Array<string>(),
|
||||
)
|
||||
.action(lazy(() => import('./build').then(m => m.command)));
|
||||
.action(lazy(() => import('./build'), 'command'));
|
||||
|
||||
command
|
||||
.command('lint [directories...]')
|
||||
@@ -182,29 +180,29 @@ export function registerScriptCommand(program: Command) {
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: 0)',
|
||||
)
|
||||
.description('Lint a package')
|
||||
.action(lazy(() => import('./lint').then(m => m.default)));
|
||||
.action(lazy(() => import('./lint'), 'default'));
|
||||
|
||||
command
|
||||
.command('test')
|
||||
.allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args
|
||||
.helpOption(', --backstage-cli-help') // Let Jest handle help
|
||||
.description('Run tests, forwarding args to Jest, defaulting to watch mode')
|
||||
.action(lazy(() => import('./test').then(m => m.default)));
|
||||
.action(lazy(() => import('./test'), 'default'));
|
||||
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache directories')
|
||||
.action(lazy(() => import('./clean/clean').then(m => m.default)));
|
||||
.action(lazy(() => import('./clean/clean'), 'default'));
|
||||
|
||||
command
|
||||
.command('prepack')
|
||||
.description('Prepares a package for packaging before publishing')
|
||||
.action(lazy(() => import('./pack').then(m => m.pre)));
|
||||
.action(lazy(() => import('./pack'), 'pre'));
|
||||
|
||||
command
|
||||
.command('postpack')
|
||||
.description('Restores the changes made by the prepack command')
|
||||
.action(lazy(() => import('./pack').then(m => m.post)));
|
||||
.action(lazy(() => import('./pack'), 'post'));
|
||||
}
|
||||
|
||||
export function registerMigrateCommand(program: Command) {
|
||||
@@ -215,39 +213,31 @@ export function registerMigrateCommand(program: Command) {
|
||||
command
|
||||
.command('package-roles')
|
||||
.description(`Add package role field to packages that don't have it`)
|
||||
.action(lazy(() => import('./migrate/packageRole').then(m => m.default)));
|
||||
.action(lazy(() => import('./migrate/packageRole'), 'default'));
|
||||
|
||||
command
|
||||
.command('package-scripts')
|
||||
.description('Set package scripts according to each package role')
|
||||
.action(
|
||||
lazy(() => import('./migrate/packageScripts').then(m => m.command)),
|
||||
);
|
||||
.action(lazy(() => import('./migrate/packageScripts'), 'command'));
|
||||
|
||||
command
|
||||
.command('package-exports')
|
||||
.description('Synchronize package subpath export definitions')
|
||||
.action(
|
||||
lazy(() => import('./migrate/packageExports').then(m => m.command)),
|
||||
);
|
||||
.action(lazy(() => import('./migrate/packageExports'), 'command'));
|
||||
|
||||
command
|
||||
.command('package-lint-configs')
|
||||
.description(
|
||||
'Migrates all packages to use @backstage/cli/config/eslint-factory',
|
||||
)
|
||||
.action(
|
||||
lazy(() => import('./migrate/packageLintConfigs').then(m => m.command)),
|
||||
);
|
||||
.action(lazy(() => import('./migrate/packageLintConfigs'), 'command'));
|
||||
|
||||
command
|
||||
.command('react-router-deps')
|
||||
.description(
|
||||
'Migrates the react-router dependencies for all packages to be peer dependencies',
|
||||
)
|
||||
.action(
|
||||
lazy(() => import('./migrate/reactRouterDeps').then(m => m.command)),
|
||||
);
|
||||
.action(lazy(() => import('./migrate/reactRouterDeps'), 'command'));
|
||||
}
|
||||
|
||||
export function registerCommands(program: Command) {
|
||||
@@ -281,7 +271,7 @@ export function registerCommands(program: Command) {
|
||||
'The license to use for any new packages (default: Apache-2.0)',
|
||||
)
|
||||
.option('--no-private', 'Do not mark new packages as private')
|
||||
.action(lazy(() => import('./new/new').then(m => m.default)));
|
||||
.action(lazy(() => import('./new/new'), 'default'));
|
||||
|
||||
registerConfigCommands(program);
|
||||
registerRepoCommand(program);
|
||||
@@ -302,7 +292,7 @@ export function registerCommands(program: Command) {
|
||||
.option('--skip-install', 'Skips yarn install step')
|
||||
.option('--skip-migrate', 'Skips migration of any moved packages')
|
||||
.description('Bump Backstage packages to the latest versions')
|
||||
.action(lazy(() => import('./versions/bump').then(m => m.default)));
|
||||
.action(lazy(() => import('./versions/bump'), 'default'));
|
||||
|
||||
program
|
||||
.command('versions:migrate')
|
||||
@@ -317,7 +307,7 @@ export function registerCommands(program: Command) {
|
||||
.description(
|
||||
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
|
||||
)
|
||||
.action(lazy(() => import('./versions/migrate').then(m => m.default)));
|
||||
.action(lazy(() => import('./versions/migrate'), 'default'));
|
||||
|
||||
program
|
||||
.command('build-workspace <workspace-dir> [packages...]')
|
||||
@@ -334,17 +324,17 @@ export function registerCommands(program: Command) {
|
||||
'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)',
|
||||
)
|
||||
.description('Builds a temporary dist workspace from the provided packages')
|
||||
.action(lazy(() => import('./buildWorkspace').then(m => m.default)));
|
||||
.action(lazy(() => import('./buildWorkspace'), 'default'));
|
||||
|
||||
program
|
||||
.command('create-github-app <github-org>')
|
||||
.description('Create new GitHub App in your organization.')
|
||||
.action(lazy(() => import('./create-github-app').then(m => m.default)));
|
||||
.action(lazy(() => import('./create-github-app'), 'default'));
|
||||
|
||||
program
|
||||
.command('info')
|
||||
.description('Show helpful information for debugging and reporting bugs')
|
||||
.action(lazy(() => import('./info').then(m => m.default)));
|
||||
.action(lazy(() => import('./info'), 'default'));
|
||||
|
||||
// Notifications for removed commands
|
||||
program
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user