Merge branch 'master' into canon-field
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Exported `QuickStartCard` component.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
---
|
||||
|
||||
Updates to allow users to subscribe to the newly created repository within GitHub to mimic similar functionality found within the GitHub UI.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Fixed incompatible `@typescript-eslint` versions with current `eslint@8.x.x`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Bumped create-app version.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Bumped create-app version.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-openapi': patch
|
||||
---
|
||||
|
||||
Refactor to no longer use backend-common
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
|
||||
'@backstage/plugin-devtools-backend': patch
|
||||
---
|
||||
|
||||
Remove the error handler middleware, since that is now provided by the framework
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-signals-backend': minor
|
||||
---
|
||||
|
||||
Removed support for the old backend system. If you were using the old `createRouter` export, please migrate to [the new backend system](https://backstage.io/docs/backend-system/).
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-gitlab': patch
|
||||
'@backstage/backend-defaults': patch
|
||||
---
|
||||
|
||||
Go back to using `node-fetch` for gitlab
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Added the ability to use `${{ context.task.id }}` in nunjucks templating, as well as `ctx.task.id` in actions to get the current task ID.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Creates new CatalogAutocomplete component in catalog-react that aligns with Select component UI for consistent a dropdown UI for all catalog filters.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Allow passing down `withSearch` prop to `EntityTechdocsContent` component since it was `true` by default, now user can use the `EntityTechdocsContent` component _without_ showing the search field on top of the content.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend-module-email': patch
|
||||
---
|
||||
|
||||
Added more examples of the plugin configuration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Corrected spelling mistake in error message
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
---
|
||||
|
||||
fix(config): add missing parameters in config schema
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Fix field extension validation not working when field is in dependencies in an array field
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/core-plugin-api': patch
|
||||
'@backstage/types': patch
|
||||
---
|
||||
|
||||
Move `Expand` and `ExpandRecursive` to `@backstage/types`
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/config-loader': patch
|
||||
'@backstage/backend-defaults': patch
|
||||
---
|
||||
|
||||
Exclude `@backstage/backend-common` from schema collection if `@backstage/backend-defaults` is present
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Fixed an issue where the `<EntityListProvider />` in `offset` mode would unnecessarily re-fetch data when the filter didn't change, causing a flicker effect.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-gitlab-org': patch
|
||||
'@backstage/plugin-permission-backend': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/plugin-permission-node': patch
|
||||
'@backstage/plugin-events-backend': patch
|
||||
'@backstage/plugin-app-backend': patch
|
||||
'@backstage/plugin-auth-node': patch
|
||||
---
|
||||
|
||||
Remove some internal usages of the backend-common package
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Added `--jobs unlimited` to `dev` script to help cases where the backend does not start up during local development
|
||||
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"example-app": "0.2.104",
|
||||
"@backstage/app-defaults": "1.5.15",
|
||||
"example-app-next": "0.0.18",
|
||||
"app-next-example-plugin": "0.0.18",
|
||||
"example-backend": "0.0.33",
|
||||
"@backstage/backend-app-api": "1.1.0",
|
||||
"@backstage/backend-defaults": "0.6.0",
|
||||
"@backstage/backend-dev-utils": "0.1.5",
|
||||
"@backstage/backend-dynamic-feature-service": "0.5.2",
|
||||
"example-backend-legacy": "0.2.105",
|
||||
"@backstage/backend-openapi-utils": "0.4.0",
|
||||
"@backstage/backend-plugin-api": "1.1.0",
|
||||
"@backstage/backend-test-utils": "1.2.0",
|
||||
"@backstage/canon": "0.0.0",
|
||||
"@backstage/catalog-client": "1.9.0",
|
||||
"@backstage/catalog-model": "1.7.2",
|
||||
"@backstage/cli": "0.29.4",
|
||||
"@backstage/cli-common": "0.1.15",
|
||||
"@backstage/cli-node": "0.2.11",
|
||||
"@backstage/codemods": "0.1.52",
|
||||
"@backstage/config": "1.3.1",
|
||||
"@backstage/config-loader": "1.9.3",
|
||||
"@backstage/core-app-api": "1.15.3",
|
||||
"@backstage/core-compat-api": "0.3.4",
|
||||
"@backstage/core-components": "0.16.2",
|
||||
"@backstage/core-plugin-api": "1.10.2",
|
||||
"@backstage/create-app": "0.5.23",
|
||||
"@backstage/dev-utils": "1.1.5",
|
||||
"e2e-test": "0.2.23",
|
||||
"@backstage/e2e-test-utils": "0.1.1",
|
||||
"@backstage/errors": "1.2.6",
|
||||
"@backstage/eslint-plugin": "0.1.10",
|
||||
"@backstage/frontend-app-api": "0.10.3",
|
||||
"@backstage/frontend-defaults": "0.1.4",
|
||||
"@internal/frontend": "0.0.4",
|
||||
"@backstage/frontend-plugin-api": "0.9.3",
|
||||
"@backstage/frontend-test-utils": "0.2.4",
|
||||
"@backstage/integration": "1.16.0",
|
||||
"@backstage/integration-aws-node": "0.1.14",
|
||||
"@backstage/integration-react": "1.2.2",
|
||||
"@internal/opaque": "0.0.1",
|
||||
"@backstage/release-manifests": "0.0.12",
|
||||
"@backstage/repo-tools": "0.12.0",
|
||||
"@internal/scaffolder": "0.0.4",
|
||||
"@techdocs/cli": "1.8.24",
|
||||
"techdocs-cli-embedded-app": "0.2.103",
|
||||
"@backstage/test-utils": "1.7.3",
|
||||
"@backstage/theme": "0.6.3",
|
||||
"@backstage/types": "1.2.0",
|
||||
"@backstage/version-bridge": "1.0.10",
|
||||
"yarn-plugin-backstage": "0.0.4",
|
||||
"@backstage/plugin-api-docs": "0.12.2",
|
||||
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.8",
|
||||
"@backstage/plugin-app": "0.1.4",
|
||||
"@backstage/plugin-app-backend": "0.4.3",
|
||||
"@backstage/plugin-app-node": "0.1.28",
|
||||
"@backstage/plugin-app-visualizer": "0.1.14",
|
||||
"@backstage/plugin-auth-backend": "0.24.1",
|
||||
"@backstage/plugin-auth-backend-module-atlassian-provider": "0.3.3",
|
||||
"@backstage/plugin-auth-backend-module-auth0-provider": "0.1.3",
|
||||
"@backstage/plugin-auth-backend-module-aws-alb-provider": "0.3.1",
|
||||
"@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-bitbucket-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.1.3",
|
||||
"@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.3.3",
|
||||
"@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.3.3",
|
||||
"@backstage/plugin-auth-backend-module-github-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-gitlab-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-google-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-guest-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-microsoft-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-oauth2-provider": "0.3.3",
|
||||
"@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-oidc-provider": "0.3.3",
|
||||
"@backstage/plugin-auth-backend-module-okta-provider": "0.1.3",
|
||||
"@backstage/plugin-auth-backend-module-onelogin-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-pinniped-provider": "0.2.3",
|
||||
"@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.4.2",
|
||||
"@backstage/plugin-auth-node": "0.5.5",
|
||||
"@backstage/plugin-auth-react": "0.1.10",
|
||||
"@backstage/plugin-bitbucket-cloud-common": "0.2.26",
|
||||
"@backstage/plugin-catalog": "1.26.0",
|
||||
"@backstage/plugin-catalog-backend": "1.29.0",
|
||||
"@backstage/plugin-catalog-backend-module-aws": "0.4.6",
|
||||
"@backstage/plugin-catalog-backend-module-azure": "0.3.0",
|
||||
"@backstage/plugin-catalog-backend-module-backstage-openapi": "0.4.3",
|
||||
"@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.4.3",
|
||||
"@backstage/plugin-catalog-backend-module-bitbucket-server": "0.3.0",
|
||||
"@backstage/plugin-catalog-backend-module-gcp": "0.3.3",
|
||||
"@backstage/plugin-catalog-backend-module-gerrit": "0.2.5",
|
||||
"@backstage/plugin-catalog-backend-module-github": "0.7.8",
|
||||
"@backstage/plugin-catalog-backend-module-github-org": "0.3.5",
|
||||
"@backstage/plugin-catalog-backend-module-gitlab": "0.6.0",
|
||||
"@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.4",
|
||||
"@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.6.1",
|
||||
"@backstage/plugin-catalog-backend-module-ldap": "0.11.0",
|
||||
"@backstage/plugin-catalog-backend-module-logs": "0.1.5",
|
||||
"@backstage/plugin-catalog-backend-module-msgraph": "0.6.5",
|
||||
"@backstage/plugin-catalog-backend-module-openapi": "0.2.5",
|
||||
"@backstage/plugin-catalog-backend-module-puppetdb": "0.2.5",
|
||||
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.3",
|
||||
"@backstage/plugin-catalog-backend-module-unprocessed": "0.5.3",
|
||||
"@backstage/plugin-catalog-common": "1.1.2",
|
||||
"@backstage/plugin-catalog-graph": "0.4.14",
|
||||
"@backstage/plugin-catalog-import": "0.12.8",
|
||||
"@backstage/plugin-catalog-node": "1.15.0",
|
||||
"@backstage/plugin-catalog-react": "1.15.0",
|
||||
"@backstage/plugin-catalog-unprocessed-entities": "0.2.12",
|
||||
"@backstage/plugin-catalog-unprocessed-entities-common": "0.0.6",
|
||||
"@backstage/plugin-config-schema": "0.1.63",
|
||||
"@backstage/plugin-devtools": "0.1.22",
|
||||
"@backstage/plugin-devtools-backend": "0.5.0",
|
||||
"@backstage/plugin-devtools-common": "0.1.14",
|
||||
"@backstage/plugin-events-backend": "0.4.0",
|
||||
"@backstage/plugin-events-backend-module-aws-sqs": "0.4.6",
|
||||
"@backstage/plugin-events-backend-module-azure": "0.2.15",
|
||||
"@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.15",
|
||||
"@backstage/plugin-events-backend-module-gerrit": "0.2.15",
|
||||
"@backstage/plugin-events-backend-module-github": "0.2.15",
|
||||
"@backstage/plugin-events-backend-module-gitlab": "0.2.15",
|
||||
"@backstage/plugin-events-backend-test-utils": "0.1.39",
|
||||
"@backstage/plugin-events-node": "0.4.6",
|
||||
"@internal/plugin-todo-list": "1.0.34",
|
||||
"@internal/plugin-todo-list-backend": "1.0.34",
|
||||
"@internal/plugin-todo-list-common": "1.0.23",
|
||||
"@backstage/plugin-home": "0.8.3",
|
||||
"@backstage/plugin-home-react": "0.1.21",
|
||||
"@backstage/plugin-kubernetes": "0.12.2",
|
||||
"@backstage/plugin-kubernetes-backend": "0.19.1",
|
||||
"@backstage/plugin-kubernetes-cluster": "0.0.20",
|
||||
"@backstage/plugin-kubernetes-common": "0.9.1",
|
||||
"@backstage/plugin-kubernetes-node": "0.2.1",
|
||||
"@backstage/plugin-kubernetes-react": "0.5.2",
|
||||
"@backstage/plugin-notifications": "0.5.0",
|
||||
"@backstage/plugin-notifications-backend": "0.5.0",
|
||||
"@backstage/plugin-notifications-backend-module-email": "0.3.4",
|
||||
"@backstage/plugin-notifications-common": "0.0.7",
|
||||
"@backstage/plugin-notifications-node": "0.2.10",
|
||||
"@backstage/plugin-org": "0.6.34",
|
||||
"@backstage/plugin-org-react": "0.1.33",
|
||||
"@backstage/plugin-permission-backend": "0.5.52",
|
||||
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.3",
|
||||
"@backstage/plugin-permission-common": "0.8.3",
|
||||
"@backstage/plugin-permission-node": "0.8.6",
|
||||
"@backstage/plugin-permission-react": "0.4.29",
|
||||
"@backstage/plugin-proxy-backend": "0.5.9",
|
||||
"@backstage/plugin-scaffolder": "1.27.2",
|
||||
"@backstage/plugin-scaffolder-backend": "1.28.0",
|
||||
"@backstage/plugin-scaffolder-backend-module-azure": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.5",
|
||||
"@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.5",
|
||||
"@backstage/plugin-scaffolder-backend-module-gcp": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-gitea": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-github": "0.5.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-gitlab": "0.7.0",
|
||||
"@backstage/plugin-scaffolder-backend-module-notifications": "0.1.5",
|
||||
"@backstage/plugin-scaffolder-backend-module-rails": "0.5.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-sentry": "0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.5",
|
||||
"@backstage/plugin-scaffolder-common": "1.5.8",
|
||||
"@backstage/plugin-scaffolder-node": "0.6.2",
|
||||
"@backstage/plugin-scaffolder-node-test-utils": "0.1.17",
|
||||
"@backstage/plugin-scaffolder-react": "1.14.2",
|
||||
"@backstage/plugin-search": "1.4.21",
|
||||
"@backstage/plugin-search-backend": "1.8.0",
|
||||
"@backstage/plugin-search-backend-module-catalog": "0.2.6",
|
||||
"@backstage/plugin-search-backend-module-elasticsearch": "1.6.3",
|
||||
"@backstage/plugin-search-backend-module-explore": "0.2.6",
|
||||
"@backstage/plugin-search-backend-module-pg": "0.5.39",
|
||||
"@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.4",
|
||||
"@backstage/plugin-search-backend-module-techdocs": "0.3.4",
|
||||
"@backstage/plugin-search-backend-node": "1.3.6",
|
||||
"@backstage/plugin-search-common": "1.2.16",
|
||||
"@backstage/plugin-search-react": "1.8.4",
|
||||
"@backstage/plugin-signals": "0.0.14",
|
||||
"@backstage/plugin-signals-backend": "0.2.4",
|
||||
"@backstage/plugin-signals-node": "0.1.15",
|
||||
"@backstage/plugin-signals-react": "0.0.8",
|
||||
"@backstage/plugin-techdocs": "1.12.0",
|
||||
"@backstage/plugin-techdocs-addons-test-utils": "1.0.43",
|
||||
"@backstage/plugin-techdocs-backend": "1.11.4",
|
||||
"@backstage/plugin-techdocs-common": "0.1.0",
|
||||
"@backstage/plugin-techdocs-module-addons-contrib": "1.1.19",
|
||||
"@backstage/plugin-techdocs-node": "1.12.15",
|
||||
"@backstage/plugin-techdocs-react": "1.2.12",
|
||||
"@backstage/plugin-user-settings": "0.8.17",
|
||||
"@backstage/plugin-user-settings-backend": "0.2.28",
|
||||
"@backstage/plugin-user-settings-common": "0.0.1"
|
||||
},
|
||||
"changesets": [
|
||||
"big-seals-drum",
|
||||
"breezy-coats-sort",
|
||||
"calm-tigers-boil",
|
||||
"create-app-1735034814",
|
||||
"create-app-1736262177",
|
||||
"cuddly-lions-knock",
|
||||
"cyan-frogs-count",
|
||||
"dry-hounds-study",
|
||||
"famous-balloons-punch",
|
||||
"famous-dryers-protect",
|
||||
"gentle-deers-return",
|
||||
"gorgeous-zebras-tan",
|
||||
"khaki-fireants-begin",
|
||||
"lemon-students-care",
|
||||
"light-wasps-unite",
|
||||
"little-scissors-fold",
|
||||
"long-parrots-hide",
|
||||
"metal-ravens-hammer",
|
||||
"nasty-pears-taste",
|
||||
"nervous-bottles-occur",
|
||||
"nice-waves-count",
|
||||
"orange-icons-sell",
|
||||
"quick-poems-cover",
|
||||
"red-hotels-sneeze",
|
||||
"renovate-dcf1169",
|
||||
"rich-penguins-stare",
|
||||
"rich-vans-hope",
|
||||
"shiny-walls-press",
|
||||
"silly-bottles-raise",
|
||||
"smart-plants-sip",
|
||||
"spicy-tomatoes-hammer",
|
||||
"strong-students-beg",
|
||||
"tall-actors-clap",
|
||||
"twenty-laws-tie",
|
||||
"two-wasps-mix",
|
||||
"weak-frogs-nail"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-module-catalog': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed support for the old backend system. Please [migrate to the new backend system](https://backstage.io/docs/backend-system/) and enable [the catalog collator](https://backstage.io/docs/features/search/collators#catalog) there.
|
||||
|
||||
As part of this, the `/alpha` export path is gone too. Just import the module from the root of the package as usual instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Fixing spelling mistake in translation
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
'@backstage/plugin-home-react': patch
|
||||
'@backstage/plugin-home': patch
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Updated dependency `@rjsf/utils` to `5.23.2`.
|
||||
Updated dependency `@rjsf/core` to `5.23.2`.
|
||||
Updated dependency `@rjsf/material-ui` to `5.23.2`.
|
||||
Updated dependency `@rjsf/validator-ajv8` to `5.23.2`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Fixed scaffolder form fields not resolving correctly in the `useCustomFieldExtensions` hook.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/backend-dynamic-feature-service': patch
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/backend-defaults': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
'@backstage/plugin-app-backend': patch
|
||||
'@backstage/plugin-auth-node': patch
|
||||
---
|
||||
|
||||
Remove usages of `PluginDatabaseManager` and `PluginEndpointDiscovery` and replace with their equivalent service types
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/backend-defaults': minor
|
||||
---
|
||||
|
||||
Ensure that an error handler middleware exists at the end of each plugin `httpRouter` handler chain. This makes it so that exceptions thrown by plugin routes are caught and encoded in the standard error format.
|
||||
|
||||
If you were using the standard `MiddlewareFactory` just to put an `error` middleware in you router, you can now remove that at your earliest convenience since it's redundant. If you have custom error handlers in your plugin router, those will continue to function as previously. If you were relying on thrown errors propagating all the way down to the root HTTP router, you will find that they no longer do that, and may want to hoist your error handling up to the plugin level instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-api-docs': patch
|
||||
---
|
||||
|
||||
Fix typo in default path of api docs definition route
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Removed the long-deprecated `DefaultCatalogCollatorFactory` and `DefaultCatalogCollatorFactoryOptions` exports, which now no longer exist in the search plugin's offerings. If you were using these, you want to migrate to [the new backend system](https://backstage.io/docs/backend-system/) and use the [catalog collator](https://backstage.io/docs/features/search/collators#catalog) directly.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Deprecate the `logStream` option in `executeShellCommand`, replacing it with a logger instance.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Fix issue with `secrets` not being forwarded properly to the backend when creating a task
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Updated condition in `resolveCodeOwner` to fix a bug where `normalizeCodeOwner` could potentially be called with an invalid argument causing an error in `CodeOwnersProcessor`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-module-addons-contrib': patch
|
||||
---
|
||||
|
||||
Internal refactor for safer handling of possible null value.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
---
|
||||
|
||||
Fixes an issue in `GithubMultiOrgEntityProvider` that caused an error when processing teams without a parent.
|
||||
@@ -7,6 +7,7 @@
|
||||
**/.git/**
|
||||
**/public/**
|
||||
**/microsite/**
|
||||
**/canon-docs/**
|
||||
**/templates/**
|
||||
**/sample-templates/**
|
||||
playwright.config.ts
|
||||
|
||||
@@ -384,6 +384,7 @@ sdks
|
||||
seb
|
||||
semlas
|
||||
semver
|
||||
sendmail
|
||||
serializable
|
||||
Serverless
|
||||
shoutout
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
|
||||
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo $PR_NUMBER > ./pr/pr_number
|
||||
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
- uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: pr_number-${{ github.event.pull_request.number }}
|
||||
path: pr/
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: 'Upload artifact'
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
name: Sync Canon Storybook
|
||||
name: Sync Canon Docs
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
sync-canon-storybook:
|
||||
sync-canon-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@@ -26,31 +26,36 @@ jobs:
|
||||
with:
|
||||
cache-prefix: ${{ runner.os }}-v20.x
|
||||
|
||||
- name: Checkout backstage/canon-storybook
|
||||
- name: Checkout backstage/canon-docs
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
repository: backstage/canon-storybook
|
||||
path: canon-storybook
|
||||
repository: backstage/canon-docs
|
||||
path: canon-external-docs
|
||||
token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.email noreply@backstage.io
|
||||
git config --global user.name 'Github Canon Storybook workflow'
|
||||
git config --global user.name 'Github Canon Docs workflow'
|
||||
|
||||
- name: Build Canon Storybook
|
||||
- name: Install dependencies
|
||||
working-directory: canon-docs
|
||||
run: yarn install
|
||||
|
||||
- name: Build Canon Docs
|
||||
working-directory: canon-docs
|
||||
run: |
|
||||
yarn workspace @backstage/canon run build-storybook
|
||||
yarn build
|
||||
|
||||
- name: Replace contents of canon-storybook repo with Storybook build output
|
||||
working-directory: canon-storybook
|
||||
- name: Replace contents of canon-external-docs repo with docs build output
|
||||
working-directory: canon-external-docs
|
||||
run: |
|
||||
git rm -rf .
|
||||
cp -R ../packages/canon/storybook-static/. .
|
||||
cp -R ../canon-docs/dist/. .
|
||||
|
||||
- name: Commit to canon-storybook repo
|
||||
working-directory: canon-storybook
|
||||
working-directory: canon-external-docs
|
||||
run: |
|
||||
git add .
|
||||
git commit -am "Canon Storybook build for backstage/backstage@${{ github.sha }}"
|
||||
git commit -am "Canon Docs build for backstage/backstage@${{ github.sha }}"
|
||||
git push
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.yarn
|
||||
dist
|
||||
microsite
|
||||
canon-docs/.next
|
||||
canon-docs/public
|
||||
canon-docs/out
|
||||
coverage
|
||||
*.hbs
|
||||
templates
|
||||
|
||||
@@ -280,3 +280,6 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
|
||||
| [New10](https://www.new10.com/) | [Cleber Gasparoto](https://github.com/chgasparoto) | We use Backstage as our internal developer portal to catalog services across teams, implement custom Tech Insights for ensuring service standards adoption, and centralize API specifications to streamline development processes and cross-team collaboration.
|
||||
| [Tele2 Kazakhstan](https://tele2.kz/) / [Altel Digital](https://digital.altel.kz/) | [Kuanysh Bekkulov](https://www.linkedin.com/in/kuanysh-bekkulov/), [Alexandr Puzeyev](https://www.linkedin.com/in/alexandr-puzeyev/) | Developer portal: services, components, APIs, and resources; includes documentation (Docs-as-Code), service templates, and plugins for integration with internal systems and tools.
|
||||
| [Axelerant](https://www.axelerant.com/) | [Zeshan Ziya](https://github.com/zeshanziya) | Developer portal, main gateway to infrastructure, documentation, internal tooling, and service catalog with a focus on project visibility.
|
||||
| [KnowBe4](https://www.knowbe4.com) | [Brandon Vicinus](https://www.linkedin.com/in/bvicinus/) | Internal Developer Portal managed by the Developer Experience Team. IDP hosts many different internal tools as plugins created by multiple teams, many of which are directly integrated with the Service Catalog. |
|
||||
| [Scalepoint](https://scalepoint.com) | [Yuriy Ostapenko](https://github.com/yuriyostapenko), [Dmytro Pasko](https://github.com/dimapasko), [Yuri Sedykh ](https://github.com/yuiri-sedykh), [Bartosz Wisniewski](https://github.com/Epgor) | Internal Developer Portal: Software Catalog with infrastructure resources and dependencies, Software Templates, TechDocs, Tech Radar, Kubernetes and Observability, as well as custom CI/CD components |
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript"],
|
||||
"rules": {
|
||||
"notice/notice": "off",
|
||||
"react/forbid-elements": "off",
|
||||
"jsx-a11y/alt-text": "off"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# next.js
|
||||
/.next/
|
||||
/dist/
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,15 @@
|
||||
# Canon Docs
|
||||
|
||||
Canon is our internal UI library built for Backstage. We built this website to document the library and its components. You can view this website [here](https://canon.backstage.io).
|
||||
|
||||
## How to run locally
|
||||
|
||||
This website is built with Next.js and it is hosted on Github pages. To run it locally, you can run the following command:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Deployments are done automatically when a PR is merged into the `master` branch. We host the website using Github pages.
|
||||
@@ -0,0 +1,15 @@
|
||||
import createMDX from '@next/mdx';
|
||||
|
||||
const nextConfig = {
|
||||
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
|
||||
output: 'export',
|
||||
assetPrefix: '/',
|
||||
distDir: 'dist',
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
};
|
||||
|
||||
const withMDX = createMDX({});
|
||||
|
||||
export default withMDX(nextConfig);
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "canon-docs",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"lint": "next lint",
|
||||
"start": "concurrently \"next dev\" \"yarn watch-css\"",
|
||||
"watch-css": "node scripts/watch-css.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"next": "14.2.23",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-frame-component": "^5.2.7",
|
||||
"shiki": "^1.26.1",
|
||||
"storybook": "^8.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"concurrently": "^9.1.2",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.23",
|
||||
"lightningcss": "^1.28.2",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
canon.backstage.io
|
||||
@@ -0,0 +1,71 @@
|
||||
[data-theme-name="legacy"][data-theme="light"], [data-theme-name="legacy"][data-theme="dark"] {
|
||||
--canon-font-regular: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
|
||||
|
||||
& .cn-button {
|
||||
text-transform: uppercase;
|
||||
box-shadow: none;
|
||||
background-color: #1f5493;
|
||||
border-radius: 4px;
|
||||
padding: 6px 16px;
|
||||
font-size: .875rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.75;
|
||||
transition: background-color .25s cubic-bezier(.4, 0, .2, 1), box-shadow .25s cubic-bezier(.4, 0, .2, 1), border .25s cubic-bezier(.4, 0, .2, 1);
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.cn-button-primary {
|
||||
color: #fff;
|
||||
background-color: #153a66;
|
||||
box-shadow: 0 3px 1px -2px #0003, 0 2px 2px #00000024, 0 1px 5px #0000001f;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 4px -1px #0003, 0 4px 5px #00000024, 0 1px 10px #0000001f;
|
||||
}
|
||||
}
|
||||
|
||||
&.cn-button-secondary {
|
||||
color: #1f5493;
|
||||
background-color: #0000;
|
||||
border: 1px solid #1f549380;
|
||||
|
||||
&:hover {
|
||||
background-color: #1f54930a;
|
||||
border: 1px solid #1f5493;
|
||||
}
|
||||
}
|
||||
|
||||
&.cn-button-tertiary {
|
||||
color: #1f5493;
|
||||
background-color: #0000;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme-name="legacy"][data-theme="light"] {
|
||||
--canon-accent: #2e77d0;
|
||||
--canon-background: #f8f8f8;
|
||||
--canon-surface-1: #fff;
|
||||
--canon-surface-2: #f4f4f4;
|
||||
--canon-text-primary: #000;
|
||||
--canon-text-primary-on-accent: #fff;
|
||||
--canon-text-secondary: #646464;
|
||||
}
|
||||
|
||||
[data-theme-name="legacy"][data-theme="dark"] {
|
||||
--canon-accent: #fff;
|
||||
--canon-background: #000;
|
||||
--canon-surface-1: #121212;
|
||||
--canon-surface-2: #1a1a1a;
|
||||
--canon-border-base: #fff3;
|
||||
--canon-border-warning: #f50000;
|
||||
--canon-border-error: #f87503;
|
||||
--canon-border-selected: #25d262;
|
||||
--canon-error: #f50000;
|
||||
--canon-text-primary: #fff;
|
||||
--canon-text-primary-on-accent: #000;
|
||||
--canon-text-secondary: #b3b3b3;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
.cn-button {
|
||||
all: unset;
|
||||
user-select: none;
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
font-size: var(--canon-font-size-body);
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
transition: all .15s;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.cn-button-primary {
|
||||
background-color: var(--canon-accent);
|
||||
color: var(--canon-text-primary-on-accent);
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-focus);
|
||||
color: var(--canon-text-primary);
|
||||
background-color: #0000;
|
||||
}
|
||||
}
|
||||
|
||||
.cn-button-secondary {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
color: var(--canon-text-primary);
|
||||
background-color: #0000;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.cn-button-tertiary {
|
||||
color: var(--canon-text-primary);
|
||||
background-color: #0000;
|
||||
|
||||
&:hover {
|
||||
color: var(--canon-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.cn-button-small {
|
||||
height: 32px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.cn-button-medium {
|
||||
height: 40px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.cn-button-content {
|
||||
align-items: center;
|
||||
gap: var(--canon-spacing-xs);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cn-button-content-icon-both {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.canon-stack {
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-inline {
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.canon-container {
|
||||
max-width: var(--canon-container-max-width);
|
||||
padding: 0 var(--canon-container-padding);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.icon-12 {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.icon-16 {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon-24 {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--canon-spacing-xs);
|
||||
font-size: var(--canon-font-size-xs);
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
user-select: none;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
|
||||
&:hover {
|
||||
& .checkbox {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
all: unset;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all .2s ease-in-out;
|
||||
display: flex;
|
||||
|
||||
&[data-checked] {
|
||||
background-color: var(--canon-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-indicator {
|
||||
color: var(--canon-text-primary-on-accent);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table {
|
||||
background-color: var(--canon-surface-1);
|
||||
border-radius: var(--canon-border-radius-xs);
|
||||
width: 100%;
|
||||
padding-bottom: var(--canon-spacing-5xs);
|
||||
padding-top: var(--canon-spacing-5xs);
|
||||
font-size: var(--canon-font-size-body);
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
|
||||
& table {
|
||||
caption-side: bottom;
|
||||
width: 100%;
|
||||
font-size: var(--canon-font-size-sm);
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
||||
.table-head {
|
||||
text-align: left;
|
||||
padding: var(--canon-spacing-xs);
|
||||
}
|
||||
|
||||
.table-body {
|
||||
& tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.table-row {
|
||||
transition: color .2s ease-in-out;
|
||||
|
||||
&:hover td {
|
||||
background-color: var(--canon-surface-3);
|
||||
}
|
||||
|
||||
& .table-cell:first-child {
|
||||
border-top-left-radius: var(--canon-border-radius-xs);
|
||||
border-bottom-left-radius: var(--canon-border-radius-xs);
|
||||
box-shadow: inset 4px 2px 0 0 var(--canon-surface-1), inset 4px -2px 0 0 var(--canon-surface-1);
|
||||
padding-left: var(--canon-spacing-xs);
|
||||
}
|
||||
|
||||
& .table-cell:last-child {
|
||||
border-top-right-radius: var(--canon-border-radius-xs);
|
||||
border-bottom-right-radius: var(--canon-border-radius-xs);
|
||||
box-shadow: inset -4px 2px 0 0 var(--canon-surface-1), inset -4px -2px 0 0 var(--canon-surface-1);
|
||||
padding-right: var(--canon-spacing-xs);
|
||||
}
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding: var(--canon-spacing-xs);
|
||||
background-color: var(--canon-surface-2);
|
||||
box-shadow: inset 0px 2px 0 0 var(--canon-surface-1), inset 0px -2px 0 0 var(--canon-surface-1);
|
||||
}
|
||||
|
||||
.text {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
&.text-body {
|
||||
font-size: var(--canon-font-size-body);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
&.text-subtitle {
|
||||
font-size: var(--canon-font-size-subtitle);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
&.text-caption {
|
||||
font-size: var(--canon-font-size-caption);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
&.text-label {
|
||||
font-size: var(--canon-font-size-label);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
&.text-regular {
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
}
|
||||
|
||||
&.text-bold {
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
&.text-display {
|
||||
font-size: var(--canon-font-size-display);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
&.text-title1 {
|
||||
font-size: var(--canon-font-size-title1);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
&.text-title2 {
|
||||
font-size: var(--canon-font-size-title2);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
&.text-title3 {
|
||||
font-size: var(--canon-font-size-title3);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
&.text-title4 {
|
||||
font-size: var(--canon-font-size-title4);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
&.text-title5 {
|
||||
font-size: var(--canon-font-size-title5);
|
||||
line-height: 100%;
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
@@ -0,0 +1,55 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chokidar = require('chokidar');
|
||||
const { bundle } = require('lightningcss');
|
||||
|
||||
const source = '../../packages/canon/src/css';
|
||||
const destination = '../public';
|
||||
|
||||
const source1 = path.join(__dirname, `${source}/core.css`);
|
||||
const destination1 = path.join(__dirname, `${destination}/core.css`);
|
||||
const source2 = path.join(__dirname, `${source}/components.css`);
|
||||
const destination2 = path.join(__dirname, `${destination}/components.css`);
|
||||
const source3 = path.join(
|
||||
__dirname,
|
||||
`../../packages/canon/.storybook/themes/backstage.css`,
|
||||
);
|
||||
const destination3 = path.join(__dirname, `${destination}/backstage.css`);
|
||||
|
||||
// Function to bundle and copy the CSS file
|
||||
const bundleAndCopyFile = async (source, destination) => {
|
||||
try {
|
||||
const result = await bundle({
|
||||
filename: source,
|
||||
minify: false,
|
||||
});
|
||||
|
||||
fs.writeFileSync(destination, result.code);
|
||||
console.log('File bundled and copied successfully!');
|
||||
} catch (err) {
|
||||
console.error('Error bundling file:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Watch the source file for changes
|
||||
chokidar.watch(source1).on('change', () => {
|
||||
console.log('Detected change in core.css, bundling and copying...');
|
||||
bundleAndCopyFile(source1, destination1);
|
||||
});
|
||||
|
||||
// Watch the components.css file for changes
|
||||
chokidar.watch(source2).on('change', () => {
|
||||
console.log('Detected change in components.css, bundling and copying...');
|
||||
bundleAndCopyFile(source2, destination2);
|
||||
});
|
||||
|
||||
// Watch the backstage.css file for changes
|
||||
chokidar.watch(source3).on('change', () => {
|
||||
console.log('Detected change in backstage.css, bundling and copying...');
|
||||
bundleAndCopyFile(source3, destination3);
|
||||
});
|
||||
|
||||
// Initial bundle and copy
|
||||
bundleAndCopyFile(source1, destination1);
|
||||
bundleAndCopyFile(source2, destination2);
|
||||
bundleAndCopyFile(source3, destination3);
|
||||
@@ -0,0 +1,32 @@
|
||||
# About Canon
|
||||
|
||||
Canon is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS.
|
||||
This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Canon
|
||||
is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users.
|
||||
|
||||
## Philosophy
|
||||
|
||||
Backstage empowers product teams to build software faster and with greater quality. Its extensibility,
|
||||
however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is
|
||||
to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while
|
||||
still allowing customization to match individual brands.
|
||||
|
||||
Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless
|
||||
component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive
|
||||
and flexible theming system.
|
||||
|
||||
## Team
|
||||
|
||||
Canon is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in
|
||||
crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive
|
||||
user experiences for the music industry, we've created a design system that looks great and works seamlessly.
|
||||
|
||||
## Community
|
||||
|
||||
Canon is an open-source project and we welcome contributions from the community. If you are interested in
|
||||
contributing to Canon, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
|
||||
and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md).
|
||||
|
||||
## License
|
||||
|
||||
Canon is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details.
|
||||
@@ -0,0 +1,112 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { spacePropsList } from '@/utils/spaceProps';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { BoxPreview } from '@/snippets/box';
|
||||
|
||||
# Box
|
||||
|
||||
Box is the lowest-level component in Canon. It provides a consistent API for styling and layout.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<BoxPreview />}
|
||||
code={`<Box>
|
||||
<DecorativeBox />
|
||||
</Box>`}
|
||||
align="center"
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock code={`import { Box } from "@backstage/canon";
|
||||
|
||||
<Box />`} />
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
### Box
|
||||
|
||||
This is the Box component, our lowest-level component. Here are all the
|
||||
available properties.
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
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.
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple example
|
||||
|
||||
A simple example of how to use the Box component.
|
||||
|
||||
<CodeBlock code={`<Box padding="md" borderRadius="md">Hello World</Box>`} />
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's a view when buttons are responsive.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Box
|
||||
padding={{ xs: 'sm', md: 'md' }}
|
||||
borderRadius={{ xs: 'sm', md: 'md' }}>
|
||||
Hello World
|
||||
</Box>`}
|
||||
/>
|
||||
@@ -0,0 +1,175 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
ButtonPreview,
|
||||
ButtonSizes,
|
||||
ButtonWithIcons,
|
||||
ButtonFullWidth,
|
||||
ButtonDisabled,
|
||||
ButtonResponsive,
|
||||
} from '@/snippets/button';
|
||||
import { buttonVariants } from '@/snippets/_snippets';
|
||||
|
||||
# Button
|
||||
|
||||
A button component that can be used to trigger actions.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<ButtonPreview />}
|
||||
code={`<Inline alignY="center">
|
||||
<Button iconStart="cloud" variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Button } from '@backstage/canon';
|
||||
|
||||
<Button />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Button className="button" />`}
|
||||
/>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### Variants
|
||||
|
||||
Here's a view when buttons have different variants.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonPreview />}
|
||||
code={buttonVariants}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a view when buttons have different sizes.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSizes />}
|
||||
code={`<Inline alignY="center">
|
||||
<Button size="small">Small</Button>
|
||||
<Button size="medium">Medium</Button>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
### With Icons
|
||||
|
||||
Here's a view when buttons have icons.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonWithIcons />}
|
||||
code={`<Inline alignY="center">
|
||||
<Button iconStart="cloud">Button</Button>
|
||||
<Button iconEnd="chevronRight">Button</Button>
|
||||
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
### Full width
|
||||
|
||||
Here's a view when buttons are full width.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonFullWidth />}
|
||||
code={`<Stack style={{ width: '300px' }}>
|
||||
<Button fullWidth>Full width</Button>
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
Here's a view when buttons are disabled.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonDisabled />}
|
||||
code={`<Button disabled>Button</Button>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's a view when buttons are responsive.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonResponsive />}
|
||||
code={`<Button
|
||||
variant={{ xs: 'primary', sm: 'secondary', md: 'tertiary' }}
|
||||
size={{ xs: 'small', sm: 'medium' }}>
|
||||
Button
|
||||
</Button>`}
|
||||
/>
|
||||
@@ -0,0 +1,107 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CheckboxPreview, CheckboxAllVariants } from '@/snippets/checkbox';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
|
||||
|
||||
# Checkbox
|
||||
|
||||
A checkbox component that can be used to trigger actions.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<CheckboxPreview />}
|
||||
code={`<Checkbox label="Accept terms and conditions" />`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Checkbox } from '@backstage/canon';
|
||||
|
||||
<Checkbox />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Checkbox className="checkbox" />`}
|
||||
/>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<BaseUI href="https://base-ui.com/react/components/checkbox" />
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### All variants
|
||||
|
||||
Here's a view when checkboxes have different variants.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<CheckboxAllVariants />}
|
||||
code={`<Inline alignY="center">
|
||||
<Checkbox />
|
||||
<Checkbox checked />
|
||||
<Checkbox label="Checkbox" />
|
||||
<Checkbox label="Checkbox" checked />
|
||||
</Inline>`}
|
||||
/>
|
||||
@@ -0,0 +1,101 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { spacePropsList } from '@/utils/spaceProps';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { ContainerPreview } from '@/snippets/container';
|
||||
|
||||
# Container
|
||||
|
||||
The container component let you use our default max-width and center the
|
||||
content on the page.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<ContainerPreview />}
|
||||
code={`<Container>
|
||||
<DecorativeBox />
|
||||
</Container>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock code={`import { Container } from "@backstage/canon";
|
||||
|
||||
<Container>Hello World!</Container>
|
||||
`} />
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
A simple example of how to use the Container component.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Container>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Container>`}
|
||||
/>
|
||||
|
||||
### Responsive padding & margin
|
||||
|
||||
The Container component also supports responsive values, making it easy to
|
||||
create responsive designs.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Container paddingY={{ xs: 'sm', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Container>`}
|
||||
/>
|
||||
@@ -0,0 +1,191 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { spacePropsList } from '@/utils/spaceProps';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { grid } from '@/snippets/_snippets';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { GridPreview } from '@/snippets/grid';
|
||||
|
||||
# Grid
|
||||
|
||||
A layout component that helps to create simple column-based layouts as well as
|
||||
more complex ones.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<GridPreview />}
|
||||
code={`<Grid>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Grid>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock code={grid} />
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
### Grid
|
||||
|
||||
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.
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
### Grid.Item
|
||||
|
||||
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.
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple grid
|
||||
|
||||
A simple grid with 3 columns and a gap of md.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Grid>
|
||||
`}
|
||||
/>
|
||||
|
||||
### Complex grid
|
||||
|
||||
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.
|
||||
|
||||
<CodeBlock
|
||||
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>
|
||||
`}
|
||||
/>
|
||||
|
||||
### Mixing rows and columns
|
||||
|
||||
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.
|
||||
|
||||
<CodeBlock
|
||||
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>
|
||||
`}
|
||||
/>
|
||||
|
||||
### Responsive grid
|
||||
|
||||
The grid component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
|
||||
<CodeBlock
|
||||
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>
|
||||
`}
|
||||
/>
|
||||
|
||||
### Start and End
|
||||
|
||||
The start and end props can be used to position the item in the grid.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Grid columns={3} gap="md">
|
||||
<Grid.Item start={2} end={4}>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
`}
|
||||
/>
|
||||
@@ -0,0 +1,97 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import {
|
||||
HeadingPreview,
|
||||
HeadingAllVariants,
|
||||
HeadingResponsive,
|
||||
} from '@/snippets/heading';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
# Heading
|
||||
|
||||
Headings are used to structure the content of your page.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeadingPreview />}
|
||||
code={`<Heading variant="title1">Hello World!</Heading>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Heading } from '@backstage/canon';
|
||||
|
||||
<Heading />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Heading className="heading" />`}
|
||||
/>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### All variants
|
||||
|
||||
The `Heading` component has a `variant` prop that can be used to change the
|
||||
appearance of the heading.
|
||||
|
||||
<Snippet
|
||||
py={2}
|
||||
open
|
||||
preview={<HeadingAllVariants />}
|
||||
code={`<Stack gap="md">
|
||||
<Heading variant="display">Display</Heading>
|
||||
<Heading variant="title1">Title 1</Heading>
|
||||
<Heading variant="title2">Title 2</Heading>
|
||||
<Heading variant="title3">Title 3</Heading>
|
||||
<Heading variant="title4">Title 4</Heading>
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
on the screen size.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
open
|
||||
preview={<HeadingResponsive />}
|
||||
code={`<Heading variant={{ xs: 'title1', md: 'title2' }}>Responsive</Heading>`}
|
||||
/>
|
||||
@@ -0,0 +1,63 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { icons } from '@backstage/canon';
|
||||
import { IconPreview } from '@/snippets/icon';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
# Icon
|
||||
|
||||
Icons are used to represent an action or a state.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
align="center"
|
||||
preview={<IconPreview />}
|
||||
code={`<Icon name="heart" />`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Icon } from '@backstage/canon';
|
||||
|
||||
<Icon />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Icon className="icon" />`}
|
||||
/>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
name: {
|
||||
type: Object.keys(icons),
|
||||
responsive: false,
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -0,0 +1,118 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { spacePropsList } from '@/utils/spaceProps';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { InlinePreview } from '@/snippets/inline';
|
||||
|
||||
# Inline
|
||||
|
||||
The Inline component is used to create a horizontal layout of elements.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<InlinePreview />}
|
||||
code={`<Inline>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Inline } from '@backstage/canon';
|
||||
|
||||
<Inline />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
A simple example of how to use the Inline component.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Inline>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
The Inline component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Inline gap={{ xs: 'sm', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
### Align
|
||||
|
||||
The Inline component also supports responsive alignment, making it easy to
|
||||
create responsive designs.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Inline align={{ xs: 'left', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
/>
|
||||
|
||||
### Align vertically
|
||||
|
||||
The Inline component also supports responsive vertical alignment, making it
|
||||
easy to create responsive designs.
|
||||
|
||||
<CodeBlock
|
||||
code={`<Inline alignY={{ xs: 'top', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Inline>`}
|
||||
/>
|
||||
@@ -0,0 +1,101 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { spacePropsList } from '@/utils/spaceProps';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
stackFAQ1,
|
||||
stackSimple,
|
||||
stackResponsive,
|
||||
stackAlign,
|
||||
} from '@/snippets/_snippets';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { StackPreview } from '@/snippets/stack';
|
||||
|
||||
# Stack
|
||||
|
||||
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.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
align="center"
|
||||
preview={<StackPreview />}
|
||||
code={`<Stack>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Stack } from '@backstage/canon';
|
||||
|
||||
<Stack />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
align: {
|
||||
type: ['start', 'center', 'end'],
|
||||
responsive: true,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
required: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
required: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacePropsList} />
|
||||
|
||||
## Common questions
|
||||
|
||||
### Can I stack horizontally?
|
||||
|
||||
The Stack component only allows for stacking elements vertically. If you want
|
||||
to create a column layout, please use the Grid component.
|
||||
|
||||
<CodeBlock code={stackFAQ1} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
A simple example of how to use the Stack component.
|
||||
|
||||
<CodeBlock code={stackSimple} />
|
||||
|
||||
### Responsive
|
||||
|
||||
The Stack component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
|
||||
<CodeBlock code={stackResponsive} />
|
||||
|
||||
### Align
|
||||
|
||||
The Stack component also supports responsive alignment, making it easy to
|
||||
create responsive designs.
|
||||
|
||||
<CodeBlock code={stackAlign} />
|
||||
@@ -0,0 +1 @@
|
||||
# Table
|
||||
@@ -0,0 +1,141 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import {
|
||||
TextPreview,
|
||||
TextAllVariants,
|
||||
TextResponsive,
|
||||
TextAllWeights,
|
||||
} from '@/snippets/text';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Tabs } from '@/components/Tabs';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
# Text
|
||||
|
||||
The `Text` component is used to display content on your page.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<TextPreview />}
|
||||
code={`<Text style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is in
|
||||
the painting?
|
||||
</Text>`}
|
||||
/>
|
||||
|
||||
<Tabs.Root>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab>Usage</Tabs.Tab>
|
||||
<Tabs.Tab>Theming</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel>
|
||||
<CodeBlock
|
||||
code={`import { Text } from '@backstage/canon';
|
||||
|
||||
<Text />
|
||||
`}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel>
|
||||
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
|
||||
your brand. For additional flexibility, you can use the provided class names for each element listed below.
|
||||
<CodeBlock
|
||||
code={`<Text className="text" />`}
|
||||
/>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs.Root>
|
||||
|
||||
## API reference
|
||||
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### All variants
|
||||
|
||||
The `Text` component has a `variant` prop that can be used to change the
|
||||
appearance of the text.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextAllVariants />}
|
||||
code={`<Stack gap="md">
|
||||
<Text variant="subtitle" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="body" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="caption" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="label" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
### All weights
|
||||
|
||||
The `Text` component has a `weight` prop that can be used to change the
|
||||
appearance of the text.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextAllWeights />}
|
||||
code={`<Stack gap="md">
|
||||
<Text weight="regular" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text weight="bold" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
on the screen size.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextResponsive />}
|
||||
code={`<Text variant={{ xs: 'label', md: 'body' }}>Responsive</Text>`}
|
||||
/>
|
||||
@@ -0,0 +1,6 @@
|
||||
.pageContainer {
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 64px;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import styles from './layout.module.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Canon',
|
||||
description: 'UI library for Backstage',
|
||||
};
|
||||
|
||||
export default function DocsLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return <div className={styles.pageContainer}>{children}</div>;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
<img
|
||||
src="header.png"
|
||||
style={{ width: '100%', marginBottom: '32px', marginTop: '64px' }}
|
||||
/>
|
||||
|
||||
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.
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Install canon
|
||||
|
||||
Install Canon using a package manager.
|
||||
|
||||
<CodeBlock lang="shell" code={`npm install @backstage/canon`} />
|
||||
|
||||
### 2. Import the css files
|
||||
|
||||
Import the global CSS file at the root of your application.
|
||||
|
||||
```tsx
|
||||
import '@backstage/canon/css/core.css';
|
||||
import '@backstage/canon/css/components.css';
|
||||
```
|
||||
|
||||
### 3. Add the theme provider
|
||||
|
||||
Add the theme provider to your application.
|
||||
|
||||
```tsx
|
||||
import { ThemeProvider } from '@backstage/canon';
|
||||
|
||||
<ThemeProvider>
|
||||
<App />
|
||||
</ThemeProvider>;
|
||||
```
|
||||
|
||||
### 4. Start building ✨
|
||||
|
||||
Now you can start building your plugin using the new design system.
|
||||
|
||||
```tsx
|
||||
import { Stack, Button } from '@backstage/canon';
|
||||
|
||||
<Stack>
|
||||
<Text>Hello World</Text>
|
||||
<Button>Click me</Button>
|
||||
</Stack>;
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
You can check our roadmap [on GitHub](https://github.com/orgs/backstage/projects/10). We'll do our best to keep you updated on the progress.
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that you have the basics down, you can start building your plugin using the new design system.
|
||||
Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system.
|
||||
If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2).
|
||||
@@ -0,0 +1,16 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { IconLibrary } from '@/components/IconLibrary';
|
||||
|
||||
# Iconography
|
||||
|
||||
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.
|
||||
|
||||
<CodeBlock title="Icon component" code={`<Icon name="heart" />`} />
|
||||
|
||||
## Icon library
|
||||
|
||||
<IconLibrary />
|
||||
@@ -0,0 +1,42 @@
|
||||
import { LayoutComponents } from '@/components/LayoutComponents';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
# Layout
|
||||
|
||||
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.
|
||||
|
||||
## Layout Components
|
||||
|
||||
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.
|
||||
|
||||
<CodeBlock
|
||||
title="Layout components"
|
||||
code={`<Stack direction="column" gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Inline gap="sm">
|
||||
<Box>Project 1</Box>
|
||||
<Box>Project 2</Box>
|
||||
</Inline>
|
||||
</Stack>
|
||||
`}
|
||||
/>
|
||||
|
||||
<LayoutComponents />
|
||||
|
||||
## Layout Helpers
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,123 @@
|
||||
import * as Table from '@/components/Table';
|
||||
import { Chip } from '@/components/Chip';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
|
||||
# Responsive
|
||||
|
||||
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.
|
||||
|
||||
## Breakpoints
|
||||
|
||||
<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>
|
||||
|
||||
## Responsive components
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
<CodeBlock
|
||||
code={`// Fixed value
|
||||
|
||||
<Button size="small">Button</Button>
|
||||
|
||||
// Responsive value
|
||||
|
||||
<Button size={{ xs: 'small', md: 'medium' }}>Button</Button>`} />
|
||||
|
||||
## How to update breakpoints
|
||||
|
||||
The set of keys are not to be changed, but you can update the minimum width of
|
||||
each breakpoint in the theme provider.
|
||||
|
||||
<CodeBlock
|
||||
code={`<CanonProvider breakpoints={{
|
||||
xs: 0,
|
||||
sm: 640,
|
||||
md: 768,
|
||||
lg: 1024,
|
||||
xl: 1280,
|
||||
'2xl': 1536,
|
||||
}} />`}
|
||||
/>
|
||||
@@ -0,0 +1,220 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import * as Table from '@/components/Table';
|
||||
import { Chip } from '@/components/Chip';
|
||||
|
||||
# Theming
|
||||
|
||||
Backstage ships with a default theme with a light and dark mode variant. The
|
||||
themes are provided as a part of the `@backstage/canon` package, which also
|
||||
includes utilities for customizing the default theme, or creating completely
|
||||
new themes.
|
||||
|
||||
## Light & Dark modes
|
||||
|
||||
By default we are supporting both light and dark modes. Each user can opt to
|
||||
choose what theme they want to use or to use their system decide what theme to
|
||||
use. If you want to create your own theme, you will have to set both light and
|
||||
dark themes following the instructions below. If you only set one of them, the
|
||||
other mode will fallback to the default theme.
|
||||
|
||||
## How to create your own theme
|
||||
|
||||
To create your own theme, you will have to define the variables below. To do
|
||||
that, create a theme.css file and import it in your application. Here's an
|
||||
example below on how to set your light and dark mode.
|
||||
|
||||
<CodeBlock
|
||||
lang="css"
|
||||
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 */
|
||||
}
|
||||
`}
|
||||
/>
|
||||
|
||||
## Colors
|
||||
|
||||
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.
|
||||
|
||||
<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>
|
||||
|
||||
## Typography
|
||||
|
||||
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.
|
||||
|
||||
<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>
|
||||
|
||||
## Spacing
|
||||
|
||||
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.
|
||||
|
||||
<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>
|
||||
@@ -0,0 +1,66 @@
|
||||
import { HeadingAllVariants } from '@/snippets/heading';
|
||||
import { TextAllVariants } from '@/snippets/text';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
|
||||
# Typography
|
||||
|
||||
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.
|
||||
|
||||
## Headings
|
||||
|
||||
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).
|
||||
|
||||
<Snippet
|
||||
py={2}
|
||||
open
|
||||
preview={<HeadingAllVariants />}
|
||||
code={`<Stack gap="md">
|
||||
<Heading variant="display">Display</Heading>
|
||||
<Heading variant="title1">Title 1</Heading>
|
||||
<Heading variant="title2">Title 2</Heading>
|
||||
<Heading variant="title3">Title 3</Heading>
|
||||
<Heading variant="title4">Title 4</Heading>
|
||||
</Stack>`}
|
||||
/>
|
||||
|
||||
## 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.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextAllVariants />}
|
||||
code={`<Stack gap="md">
|
||||
<Text variant="subtitle" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="body" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="caption" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
<Text variant="label" style={{ maxWidth: '600px' }}>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters I
|
||||
have none, but that man's father is my father's son.” Who is
|
||||
in the painting?
|
||||
</Text>
|
||||
</Stack>`}
|
||||
/>
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Grid, Stack, Text } from '../../../../../packages/canon';
|
||||
import { screenSizes } from '@/utils/data';
|
||||
import { Frame } from '@/components/Frame';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
import { ButtonPlayground } from '@/snippets/button';
|
||||
import { CheckboxPlayground } from '@/snippets/checkbox';
|
||||
import { HeadingPlayground } from '@/snippets/heading';
|
||||
import { TextPlayground } from '@/snippets/text';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function PlaygroundPage() {
|
||||
const { selectedScreenSizes } = usePlayground();
|
||||
|
||||
const filteredScreenSizes = screenSizes.filter(item =>
|
||||
selectedScreenSizes.includes(item.slug),
|
||||
);
|
||||
|
||||
if (filteredScreenSizes.length === 0) {
|
||||
return (
|
||||
<div className={styles.containerEmpty}>
|
||||
<Content />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{filteredScreenSizes.map(screenSize => (
|
||||
<div
|
||||
className={styles.breakpointContainer}
|
||||
style={{ width: screenSize.width }}
|
||||
key={screenSize.slug}
|
||||
>
|
||||
<Text>
|
||||
{screenSize.title} - {screenSize.width}px
|
||||
</Text>
|
||||
<div className={styles.breakpointContent}>
|
||||
<Frame>
|
||||
<Content />
|
||||
</Frame>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Content = () => {
|
||||
const { selectedComponents } = usePlayground();
|
||||
|
||||
return (
|
||||
<Stack gap="xl">
|
||||
{selectedComponents.find(c => c === 'button') && (
|
||||
<Line content={<ButtonPlayground />} title="Button" />
|
||||
)}
|
||||
{selectedComponents.find(c => c === 'checkbox') && (
|
||||
<Line content={<CheckboxPlayground />} title="Checkbox" />
|
||||
)}
|
||||
{selectedComponents.find(c => c === 'heading') && (
|
||||
<Line content={<HeadingPlayground />} title="Heading" />
|
||||
)}
|
||||
{selectedComponents.find(c => c === 'text') && (
|
||||
<Line content={<TextPlayground />} title="Text" />
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const Line = ({ content, title }: { content: ReactNode; title: string }) => {
|
||||
return (
|
||||
<Grid gap={{ xs: 'xs', md: 'xl' }}>
|
||||
<Grid.Item colSpan={2}>
|
||||
<Text>{title}</Text>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={10}>{content}</Grid.Item>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
.container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24px;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.containerEmpty {
|
||||
padding: 78px 40px;
|
||||
}
|
||||
|
||||
.breakpointContainer {
|
||||
flex-shrink: 0;
|
||||
margin-top: 40px;
|
||||
height: calc(100vh - 86px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 48px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.breakpointContent {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-background-base);
|
||||
padding: 16px;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--canon-background);
|
||||
color: var(--canon-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shiki {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shiki,
|
||||
.shiki span {
|
||||
background-color: transparent !important;
|
||||
font-family: var(--canon-font-monospace);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .shiki,
|
||||
[data-theme='dark'] .shiki span {
|
||||
color: var(--shiki-dark) !important;
|
||||
/* Optional, if you also want font styles */
|
||||
font-style: var(--shiki-dark-font-style) !important;
|
||||
font-weight: var(--shiki-dark-font-weight) !important;
|
||||
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="640" height="640" viewBox="0 0 640 640" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M370.766 153.765C377.563 166.716 371.236 182.114 359.357 190.647C323.005 216.758 299.326 259.411 299.326 307.597C299.326 365.997 334.107 416.27 384.077 438.817C395.235 443.852 403.478 454.368 403.478 466.609V640H382.864C224.433 640 96 511.517 96 353.025L96 20.6143C96 9.22934 105.229 0 116.614 0V0C226.982 3.14552e-06 322.792 62.3523 370.766 153.765Z" fill="black"/>
|
||||
<path d="M438.772 411.19C496.335 411.19 543 364.454 543 306.803C543 249.152 496.335 202.417 438.772 202.417C381.208 202.417 334.543 249.152 334.543 306.803C334.543 364.454 381.208 411.19 438.772 411.19Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 702 B |
@@ -0,0 +1,43 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { Sidebar } from '../components/Sidebar';
|
||||
|
||||
import styles from './page.module.css';
|
||||
import { Providers } from './providers';
|
||||
|
||||
import './globals.css';
|
||||
import '/public/core.css';
|
||||
import '/public/components.css';
|
||||
import '/public/backstage.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Canon',
|
||||
description: 'UI library for Backstage',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
// const cookieStore = await cookies();
|
||||
// const theme = cookieStore.get('theme')?.value || 'light';
|
||||
// const themeName = cookieStore.get('theme-name')?.value || 'default';
|
||||
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
data-theme="light"
|
||||
data-theme-name="default"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body>
|
||||
<Providers>
|
||||
<div className={styles.global}>
|
||||
<Sidebar />
|
||||
<div className={styles.container}>{children}</div>
|
||||
</div>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
.global {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.page p {
|
||||
color: var(--canon-text-primary);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 64px 24px 64px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.pageContainer {
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 64px;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { CanonProvider } from '../../../packages/canon/src/contexts/canon';
|
||||
import { PlaygroundProvider } from '@/utils/playground-context';
|
||||
|
||||
export const Providers = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<CanonProvider>
|
||||
<PlaygroundProvider>{children}</PlaygroundProvider>
|
||||
</CanonProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 styles from './styles.module.css';
|
||||
|
||||
export const Banner = ({
|
||||
variant = 'info',
|
||||
text,
|
||||
}: {
|
||||
variant?: 'info' | 'warning';
|
||||
text?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className={`${styles.banner} ${styles[variant]}`}>
|
||||
<div className={styles.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>
|
||||
<div className={styles.text}>{text}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+1
-15
@@ -13,18 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TableFooter } from '../Table';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/Components/TableFooter',
|
||||
component: TableFooter,
|
||||
} satisfies Meta<typeof TableFooter>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
};
|
||||
export { Banner } from './Banner';
|
||||
@@ -0,0 +1,41 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
.text {
|
||||
line-height: 1.5;
|
||||
}
|
||||
+14
-12
@@ -14,17 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TableHead } from '../Table';
|
||||
import React from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/Components/TableHead',
|
||||
component: TableHead,
|
||||
} satisfies Meta<typeof TableHead>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
export const Chip = ({
|
||||
children,
|
||||
head = false,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
head?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<span className={`${styles.chip} ${head ? styles.head : ''}`}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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 { Chip } from './Chip';
|
||||
+24
-13
@@ -14,19 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Table } from '../Table';
|
||||
.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;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/Components/Table',
|
||||
component: Table,
|
||||
} satisfies Meta<typeof Table>;
|
||||
.head {
|
||||
background-color: #eaf2fd;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
[data-theme='dark'] .chip {
|
||||
background-color: #2c2c2c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: "Hello World! I'm a basic table",
|
||||
},
|
||||
};
|
||||
[data-theme='dark'] .chip.head {
|
||||
background-color: #26417b;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import type { BundledLanguage } from 'shiki';
|
||||
import { codeToHtml } from 'shiki';
|
||||
import { Text } from '../../../../packages/canon/src/components/Text';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
interface CodeBlockProps {
|
||||
lang?: BundledLanguage;
|
||||
title?: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export async function CodeBlock({ lang = 'tsx', title, code }: CodeBlockProps) {
|
||||
const out = await codeToHtml(code || '', {
|
||||
lang: lang,
|
||||
themes: {
|
||||
light: 'min-light',
|
||||
dark: 'min-dark',
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.codeBlock}>
|
||||
{title && (
|
||||
<div className={styles.title}>
|
||||
<Text variant="body">{title}</Text>
|
||||
</div>
|
||||
)}
|
||||
<div dangerouslySetInnerHTML={{ __html: out }} className={styles.code} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.codeBlock {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
position: relative;
|
||||
background: transparent;
|
||||
overflow-x: auto;
|
||||
font-family: var(--canon-font-monospace);
|
||||
background-color: #fff;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .codeBlock {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.title {
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
padding: 12px 20px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--canon-text-secondary);
|
||||
}
|
||||
|
||||
.code {
|
||||
padding: 20px;
|
||||
}
|
||||
+13
-12
@@ -14,17 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TableCell } from '../Table';
|
||||
import React from 'react';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/Components/TableCell',
|
||||
component: TableCell,
|
||||
} satisfies Meta<typeof TableCell>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
export const Columns = ({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<div className="columns" style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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 { Columns } from './Columns';
|
||||
+6
-5
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { default as feature } from './module';
|
||||
|
||||
/** @alpha */
|
||||
const _feature = feature;
|
||||
export default _feature;
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
row-gap: 20px;
|
||||
column-gap: 80px;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export const DecorativeBox = () => {
|
||||
return <div className={styles.box} />;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
.box {
|
||||
min-width: 64px;
|
||||
min-height: 64px;
|
||||
background-color: #eaf2fd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px #2563eb;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%232563eb' fill-opacity='0.3' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable @next/next/no-css-tags */
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import ReactFrame from 'react-frame-component';
|
||||
|
||||
export const Frame = ({ children }: { children: React.ReactNode }) => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setShow(true);
|
||||
}, []);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<ReactFrame
|
||||
loading="lazy"
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
initialContent={`<!DOCTYPE html><html data-theme="light"><head></head><body><div class="frame-root"></div></body></html>`}
|
||||
mountTarget=".frame-root"
|
||||
head={
|
||||
<>
|
||||
<link rel="stylesheet" href="/core.css" />
|
||||
<link rel="stylesheet" href="/components.css" />
|
||||
<link rel="stylesheet" href="/backstage.css" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</ReactFrame>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import styles from './styles.module.css';
|
||||
import { Text } from '../../../../packages/canon/src/components/Text';
|
||||
import { Icon } from '../../../../packages/canon/src/components/Icon';
|
||||
|
||||
interface BaseUIProps {
|
||||
href: string;
|
||||
}
|
||||
|
||||
export const BaseUI = ({ href }: BaseUIProps) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<svg
|
||||
width="17"
|
||||
height="30"
|
||||
viewBox="0 0 17 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={styles.icon}
|
||||
>
|
||||
<path d="M8 12.8V15V26C3.58172 26 0 22.0601 0 17.2V15V4C4.41828 4 8 7.93989 8 12.8Z" />
|
||||
<path d="M9.5001 10.0154C9.2245 9.99843 9 10.2239 9 10.5V26.0001C13.4183 26.0001 17 22.4184 17 18.0001C17 13.7498 13.6854 10.2736 9.5001 10.0154Z" />
|
||||
</svg>
|
||||
<div className={styles.content}>
|
||||
<Text variant="subtitle" weight="bold">
|
||||
Base UI
|
||||
</Text>
|
||||
<div className={styles.description}>
|
||||
<Text variant="subtitle">
|
||||
This component is using Base UI under the hood. While most of the
|
||||
original props are available, we have made some changes to the API
|
||||
to fit our needs.
|
||||
</Text>
|
||||
</div>
|
||||
{href && (
|
||||
<a className={styles.button} href={href} target="_blank">
|
||||
Discover more
|
||||
<Icon name="externalLink" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
.container {
|
||||
display: flex;
|
||||
background-color: var(--canon-surface-1);
|
||||
padding: var(--canon-spacing-md);
|
||||
border-radius: var(--canon-border-radius-xs);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
margin-bottom: var(--canon-spacing-md);
|
||||
gap: var(--canon-spacing-md);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: var(--canon-text-primary);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.description {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.button {
|
||||
all: unset;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background-color: var(--canon-surface-2);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border-radius: var(--canon-border-radius-xs);
|
||||
padding: 0 var(--canon-spacing-xs);
|
||||
height: 28px;
|
||||
border-radius: 100px;
|
||||
margin-top: var(--canon-spacing-xs);
|
||||
font-size: var(--canon-font-size-body);
|
||||
color: var(--canon-text-primary);
|
||||
gap: var(--canon-spacing-2xs);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 '../../../../packages/canon/src/components/Icon';
|
||||
import type { IconNames } from '../../../../packages/canon/src/components/Icon';
|
||||
import { Text } from '../../../../packages/canon/src/components/Text';
|
||||
import { icons } from '../../../../packages/canon/src/components/Icon/icons';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export const IconLibrary = () => {
|
||||
const list = Object.keys(icons);
|
||||
|
||||
return (
|
||||
<div className={styles.library}>
|
||||
{list.map(icon => (
|
||||
<div key={icon} className={styles.item}>
|
||||
<div className={styles.icon}>
|
||||
<Icon name={icon as IconNames} />
|
||||
</div>
|
||||
<Text variant="body">{icon}</Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user