From 7909300e75dd2072a76339bab2969e953f8aef96 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 17 Mar 2025 06:38:50 +0000 Subject: [PATCH 01/99] Add render prop to Link Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Link/Link.tsx | 35 +++++++++++---------- packages/canon/src/components/Link/types.ts | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx index 8763575bf9..8b88009a86 100644 --- a/packages/canon/src/components/Link/Link.tsx +++ b/packages/canon/src/components/Link/Link.tsx @@ -28,28 +28,31 @@ export const Link = forwardRef((props, ref) => { weight = 'regular', style, className, + render, ...restProps } = props; - // Get the responsive values for the variant and weight const responsiveVariant = useResponsiveValue(variant); const responsiveWeight = useResponsiveValue(weight); - return ( - - {children} - - ); + const linkProps = { + ref, + className: clsx( + 'canon-Link', + responsiveVariant && `canon-Link--variant-${responsiveVariant}`, + responsiveWeight && `canon-Link--weight-${responsiveWeight}`, + className, + ), + style, + children, + ...restProps, + }; + + if (render) { + return render(linkProps); + } + + return ; }); Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts index 4c5b34e14e..7bde893de2 100644 --- a/packages/canon/src/components/Link/types.ts +++ b/packages/canon/src/components/Link/types.ts @@ -30,4 +30,5 @@ export interface LinkProps weight?: 'regular' | 'bold' | Partial>; className?: string; style?: CSSProperties; + render?: (props: Omit) => ReactNode; } From 4fe5b08db69de12bc690cd013047917658bf1298 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 17 Mar 2025 06:50:25 +0000 Subject: [PATCH 02/99] Create ten-dodos-lead.md Signed-off-by: Charles de Dreuille --- .changeset/ten-dodos-lead.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ten-dodos-lead.md diff --git a/.changeset/ten-dodos-lead.md b/.changeset/ten-dodos-lead.md new file mode 100644 index 0000000000..340c5170cf --- /dev/null +++ b/.changeset/ten-dodos-lead.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +We added a render prop to the Link component to make sure it can work with React Router. From 02b86027ef03e3e1f90e1d7405384c78ffa2370c Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 17 Mar 2025 15:12:25 +0000 Subject: [PATCH 03/99] Update Link render prop mechanism Signed-off-by: Charles de Dreuille --- .../src/components/Link/Link.stories.tsx | 3 +- packages/canon/src/components/Link/Link.tsx | 78 +++++++++++-------- packages/canon/src/components/Link/types.ts | 12 ++- packages/canon/src/index.ts | 1 + 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx index 38e71c310a..856fe2532d 100644 --- a/packages/canon/src/components/Link/Link.stories.tsx +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -33,7 +33,7 @@ type Story = StoryObj; export const Default: Story = { args: { - href: 'https://canon.backstage.io', + to: 'https://canon.backstage.io', children: 'Sign up for Backstage', }, }; @@ -66,6 +66,7 @@ export const AllWeights: Story = { export const Responsive: Story = { args: { + ...Default.args, variant: { xs: 'label', md: 'body', diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx index 8b88009a86..e3f636ea06 100644 --- a/packages/canon/src/components/Link/Link.tsx +++ b/packages/canon/src/components/Link/Link.tsx @@ -14,45 +14,59 @@ * limitations under the License. */ -import React, { forwardRef } from 'react'; +import React, { forwardRef, memo, ComponentType } from 'react'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; import clsx from 'clsx'; -import type { LinkProps } from './types'; +import type { LinkProps, LinkRenderProps } from './types'; /** @public */ -export const Link = forwardRef((props, ref) => { - const { - children, - variant = 'body', - weight = 'regular', - style, - className, - render, - ...restProps - } = props; - - const responsiveVariant = useResponsiveValue(variant); - const responsiveWeight = useResponsiveValue(weight); - - const linkProps = { - ref, - className: clsx( - 'canon-Link', - responsiveVariant && `canon-Link--variant-${responsiveVariant}`, - responsiveWeight && `canon-Link--weight-${responsiveWeight}`, +export const Link = memo( + forwardRef((props, ref) => { + const { + children, + variant = 'body', + weight = 'regular', + style, className, - ), - style, - children, - ...restProps, - }; + render, + to, + ...restProps + } = props; - if (render) { - return render(linkProps); - } + const responsiveVariant = useResponsiveValue(variant); + const responsiveWeight = useResponsiveValue(weight); - return ; -}); + const linkProps: LinkRenderProps = { + className: clsx( + 'canon-Link', + responsiveVariant && `canon-Link--variant-${responsiveVariant}`, + responsiveWeight && `canon-Link--weight-${responsiveWeight}`, + className, + ), + style, + children, + to, + ...restProps, + }; + + if (render) { + // If render is a component type, wrap it in memo to prevent unnecessary re-renders + if (typeof render === 'function' && !render.length) { + const MemoizedComponent = memo( + render as ComponentType, + ); + return ; + } + // If it's a render function, call it directly + const RenderComponent = render as ( + props: LinkRenderProps, + ) => React.ReactNode; + return ; + } + + return ; + }), +); Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts index 7bde893de2..134509563a 100644 --- a/packages/canon/src/components/Link/types.ts +++ b/packages/canon/src/components/Link/types.ts @@ -14,13 +14,14 @@ * limitations under the License. */ -import type { CSSProperties, ReactNode } from 'react'; +import type { CSSProperties, ReactNode, ComponentType } from 'react'; import type { Breakpoint } from '../../types'; /** @public */ export interface LinkProps - extends React.AnchorHTMLAttributes { + extends Omit, 'to'> { children: ReactNode; + to: string; variant?: | 'subtitle' | 'body' @@ -30,5 +31,10 @@ export interface LinkProps weight?: 'regular' | 'bold' | Partial>; className?: string; style?: CSSProperties; - render?: (props: Omit) => ReactNode; + render?: + | ((props: Omit) => ReactNode) + | ComponentType>; } + +/** @public */ +export type LinkRenderProps = Omit; diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 3c9a28b44d..f30b825c8c 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -42,6 +42,7 @@ export * from './components/Field'; export * from './components/Tooltip'; export * from './components/Menu'; export * from './components/ScrollArea'; +export * from './components/Link'; // Types export * from './types'; From e91f071535069188569161e4a4f36349906ea622 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 17 Mar 2025 15:21:34 +0000 Subject: [PATCH 04/99] Update report.api.md Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index e993df054b..345907e44c 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import type { ComponentType } from 'react'; import { Context } from 'react'; import type { CSSProperties } from 'react'; import { Field as Field_2 } from '@base-ui-components/react/field'; @@ -703,6 +704,39 @@ export type JustifyContent = | 'around' | 'between'; +// @public (undocumented) +export const Link: React_2.MemoExoticComponent< + React_2.ForwardRefExoticComponent< + LinkProps & React_2.RefAttributes + > +>; + +// @public (undocumented) +export interface LinkProps + extends Omit, 'to'> { + // (undocumented) + children: ReactNode; + // (undocumented) + className?: string; + // (undocumented) + render?: + | ((props: Omit) => ReactNode) + | ComponentType>; + // (undocumented) + style?: CSSProperties; + // (undocumented) + to: string; + // (undocumented) + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + // (undocumented) + weight?: 'regular' | 'bold' | Partial>; +} + // @public (undocumented) export const marginPropDefs: (spacingValues: string[]) => { m: { From 411c87960c5563e97a2a3768394dea13d0195e4f Mon Sep 17 00:00:00 2001 From: Brett Wright Date: Wed, 12 Mar 2025 08:55:36 +1100 Subject: [PATCH 05/99] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20allowin?= =?UTF-8?q?g=20branch=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for allowing branch updates when creating a GitHub repository. It introduces a new `allowUpdateBranch` property to the `createGithubRepoCreateAction` function, allowing users to specify whether updates to branches should be allowed. This enhancement provides more flexibility and control over repository management. Signed-off-by: Brett Wright --- .changeset/silent-snails-lie.md | 5 + .../report.api.md | 2 + .../src/actions/github.test.ts | 75 ++++++++--- .../src/actions/github.ts | 4 + .../actions/githubRepoCreate.examples.test.ts | 124 ++++++++++++++++++ .../src/actions/githubRepoCreate.examples.ts | 15 +++ .../src/actions/githubRepoCreate.test.ts | 77 ++++++++--- .../src/actions/githubRepoCreate.ts | 4 + .../src/actions/helpers.ts | 3 + .../src/actions/inputProperties.ts | 7 + 10 files changed, 284 insertions(+), 32 deletions(-) create mode 100644 .changeset/silent-snails-lie.md diff --git a/.changeset/silent-snails-lie.md b/.changeset/silent-snails-lie.md new file mode 100644 index 0000000000..87ae46afa6 --- /dev/null +++ b/.changeset/silent-snails-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-github': patch +--- + +Add support to github:repo:create to allow branch updates diff --git a/plugins/scaffolder-backend-module-github/report.api.md b/plugins/scaffolder-backend-module-github/report.api.md index f1b7f98a30..2d49e1a5bd 100644 --- a/plugins/scaffolder-backend-module-github/report.api.md +++ b/plugins/scaffolder-backend-module-github/report.api.md @@ -210,6 +210,7 @@ export function createGithubRepoCreateAction(options: { squashMergeCommitMessage?: 'PR_BODY' | 'COMMIT_MESSAGES' | 'BLANK'; allowMergeCommit?: boolean; allowAutoMerge?: boolean; + allowUpdateBranch?: boolean; requireCodeOwnerReviews?: boolean; bypassPullRequestAllowances?: { users?: string[]; @@ -355,6 +356,7 @@ export function createPublishGithubAction(options: { squashMergeCommitMessage?: 'PR_BODY' | 'COMMIT_MESSAGES' | 'BLANK'; allowMergeCommit?: boolean; allowAutoMerge?: boolean; + allowUpdateBranch?: boolean; sourcePath?: string; bypassPullRequestAllowances?: | { diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts index 2f629622d0..30945d92f2 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts @@ -189,6 +189,12 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'private', }); @@ -211,6 +217,12 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'public', }); @@ -234,6 +246,7 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, visibility: 'private', }); @@ -258,10 +271,13 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + customElements: undefined, visibility: 'private', - has_wiki: true, - has_projects: true, - has_issues: true, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); await action.handler({ @@ -285,10 +301,13 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, visibility: 'private', - has_wiki: false, - has_projects: false, - has_issues: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); await action.handler({ @@ -314,11 +333,13 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: 'https://example.com', visibility: 'private', - custom_properties: { - foo: 'bar', - foo2: 'bar2', - }, }); }); @@ -345,6 +366,11 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, }); await action.handler({ @@ -367,6 +393,11 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, }); await action.handler({ @@ -390,6 +421,10 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, }); await action.handler({ @@ -414,9 +449,10 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, - has_wiki: true, - has_projects: true, - has_issues: true, + allow_update_branch: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, }); await action.handler({ @@ -441,9 +477,11 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, - has_wiki: false, - has_projects: false, - has_issues: false, + allow_update_branch: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); // Custom properties on user repos should be ignored @@ -471,6 +509,11 @@ describe('publish:github', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: 'https://example.com', }); }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.ts b/plugins/scaffolder-backend-module-github/src/actions/github.ts index 02b1fbbe32..fd858f0830 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.ts @@ -65,6 +65,7 @@ export function createPublishGithubAction(options: { squashMergeCommitMessage?: 'PR_BODY' | 'COMMIT_MESSAGES' | 'BLANK'; allowMergeCommit?: boolean; allowAutoMerge?: boolean; + allowUpdateBranch?: boolean; sourcePath?: string; bypassPullRequestAllowances?: | { @@ -156,6 +157,7 @@ export function createPublishGithubAction(options: { squashMergeCommitMessage: inputProps.squashMergeCommitMessage, allowRebaseMerge: inputProps.allowRebaseMerge, allowAutoMerge: inputProps.allowAutoMerge, + allowUpdateBranch: inputProps.allowUpdateBranch, sourcePath: inputProps.sourcePath, collaborators: inputProps.collaborators, hasProjects: inputProps.hasProjects, @@ -210,6 +212,7 @@ export function createPublishGithubAction(options: { squashMergeCommitMessage = 'COMMIT_MESSAGES', allowRebaseMerge = true, allowAutoMerge = false, + allowUpdateBranch = false, collaborators, hasProjects = undefined, hasWiki = undefined, @@ -258,6 +261,7 @@ export function createPublishGithubAction(options: { squashMergeCommitMessage, allowRebaseMerge, allowAutoMerge, + allowUpdateBranch, access, collaborators, hasProjects, diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.test.ts index 1a81a54c64..8694011133 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.test.ts @@ -129,6 +129,12 @@ describe('github:repo:create examples', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'private', }); }); @@ -157,6 +163,11 @@ describe('github:repo:create examples', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'private', }); }); @@ -177,6 +188,7 @@ describe('github:repo:create examples', () => { name: 'repo', org: 'owner', private: true, + description: undefined, delete_branch_on_merge: false, allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', @@ -184,9 +196,13 @@ describe('github:repo:create examples', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, visibility: 'private', has_issues: false, // disable issues + has_projects: undefined, has_wiki: false, // disable wiki + homepage: undefined, }); }); @@ -219,6 +235,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -260,6 +278,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -301,6 +321,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -342,6 +364,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -383,6 +407,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: false, allow_rebase_merge: false, allow_auto_merge: false, @@ -424,6 +450,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'pull_request_title', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -465,6 +493,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'blank', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -506,6 +536,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: true, @@ -547,6 +579,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -588,6 +622,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -629,6 +665,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -670,6 +708,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -711,6 +751,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -752,6 +794,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -793,6 +837,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -834,6 +880,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -875,6 +923,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -916,6 +966,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -957,6 +1009,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -998,6 +1052,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1039,6 +1095,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1080,6 +1138,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: false, allow_rebase_merge: true, allow_auto_merge: false, @@ -1121,6 +1181,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1162,6 +1224,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1203,6 +1267,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1244,6 +1310,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1285,6 +1353,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1326,6 +1396,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1367,6 +1439,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1408,6 +1482,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1449,6 +1525,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1490,6 +1568,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1531,6 +1611,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1572,6 +1654,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1613,6 +1697,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1654,6 +1740,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1695,6 +1783,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1736,6 +1826,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1777,6 +1869,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'pull_request_description', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: false, allow_rebase_merge: false, allow_auto_merge: false, @@ -1818,6 +1912,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: false, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: false, allow_rebase_merge: true, allow_auto_merge: false, @@ -1859,6 +1955,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1900,6 +1998,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1941,6 +2041,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -1982,6 +2084,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2023,6 +2127,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2064,6 +2170,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2105,6 +2213,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2146,6 +2256,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: false, allow_rebase_merge: true, allow_auto_merge: true, @@ -2187,6 +2299,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2228,6 +2342,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2269,6 +2385,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2310,6 +2428,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2351,6 +2471,8 @@ describe('github:repo:create examples', () => { allow_squash_merge: true, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_update_branch: false, + custom_properties: undefined, allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, @@ -2390,6 +2512,8 @@ describe('github:repo:create examples', () => { private: true, delete_branch_on_merge: false, allow_squash_merge: true, + allow_update_branch: false, + custom_properties: undefined, squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', squash_merge_commit_message: 'COMMIT_MESSAGES', allow_merge_commit: true, diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.ts index 1c1f1ca5d2..ce39e7510d 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.examples.ts @@ -991,4 +991,19 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Allow branch updates.', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:create', + name: 'Create a new GitHub repository allowing branch updates', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + allowUpdateBranch: true, + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts index 864c5b729f..bf340a485e 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts @@ -137,6 +137,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'private', }); @@ -159,6 +164,12 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, visibility: 'public', }); @@ -182,6 +193,10 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, visibility: 'private', }); @@ -206,10 +221,13 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, visibility: 'private', - has_wiki: true, - has_projects: true, - has_issues: true, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); await action.handler({ @@ -233,10 +251,12 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, visibility: 'private', - has_wiki: false, - has_projects: false, - has_issues: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, }); await action.handler({ @@ -262,11 +282,13 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + custom_properties: undefined, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: 'https://example.com', visibility: 'private', - custom_properties: { - foo: 'bar', - foo2: 'bar2', - }, }); }); @@ -293,6 +315,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, }); await action.handler({ @@ -315,6 +342,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: undefined, }); await action.handler({ @@ -338,6 +370,10 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, }); await action.handler({ @@ -362,9 +398,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, - has_wiki: true, - has_projects: true, - has_issues: true, + allow_update_branch: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); await action.handler({ @@ -389,9 +427,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, - has_wiki: false, - has_projects: false, - has_issues: false, + allow_update_branch: false, + has_wiki: undefined, + has_projects: undefined, + has_issues: undefined, + homepage: 'https://example.com', }); // Custom properties on user repos should be ignored @@ -419,6 +459,11 @@ describe('github:repo:create', () => { allow_merge_commit: true, allow_rebase_merge: true, allow_auto_merge: false, + allow_update_branch: false, + has_issues: undefined, + has_projects: undefined, + has_wiki: undefined, + homepage: 'https://example.com', }); }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts index 91b0580da9..6899b3dff8 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts @@ -55,6 +55,7 @@ export function createGithubRepoCreateAction(options: { squashMergeCommitMessage?: 'PR_BODY' | 'COMMIT_MESSAGES' | 'BLANK'; allowMergeCommit?: boolean; allowAutoMerge?: boolean; + allowUpdateBranch?: boolean; requireCodeOwnerReviews?: boolean; bypassPullRequestAllowances?: { users?: string[]; @@ -130,6 +131,7 @@ export function createGithubRepoCreateAction(options: { squashMergeCommitMessage: inputProps.squashMergeCommitMessage, allowRebaseMerge: inputProps.allowRebaseMerge, allowAutoMerge: inputProps.allowAutoMerge, + allowUpdateBranch: inputProps.allowUpdateBranch, collaborators: inputProps.collaborators, hasProjects: inputProps.hasProjects, hasWiki: inputProps.hasWiki, @@ -167,6 +169,7 @@ export function createGithubRepoCreateAction(options: { squashMergeCommitMessage = 'COMMIT_MESSAGES', allowRebaseMerge = true, allowAutoMerge = false, + allowUpdateBranch = false, collaborators, hasProjects = undefined, hasWiki = undefined, @@ -213,6 +216,7 @@ export function createGithubRepoCreateAction(options: { squashMergeCommitMessage, allowRebaseMerge, allowAutoMerge, + allowUpdateBranch, access, collaborators, hasProjects, diff --git a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts index 8e6e521c29..db3aa03679 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts @@ -44,6 +44,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( squashMergeCommitMessage: 'PR_BODY' | 'COMMIT_MESSAGES' | 'BLANK' | undefined, allowRebaseMerge: boolean, allowAutoMerge: boolean, + allowUpdateBranch: boolean, access: string | undefined, collaborators: | ( @@ -103,6 +104,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( squash_merge_commit_message: squashMergeCommitMessage, allow_rebase_merge: allowRebaseMerge, allow_auto_merge: allowAutoMerge, + allow_update_branch: allowUpdateBranch, homepage: homepage, has_projects: hasProjects, has_wiki: hasWiki, @@ -121,6 +123,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( squash_merge_commit_message: squashMergeCommitMessage, allow_rebase_merge: allowRebaseMerge, allow_auto_merge: allowAutoMerge, + allow_update_branch: allowUpdateBranch, homepage: homepage, has_projects: hasProjects, has_wiki: hasWiki, diff --git a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts index 29e426d2b9..cac93ec7c6 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts @@ -111,6 +111,12 @@ const allowSquashMerge = { default: true, description: 'Allow squash merges. The default value is `true`', }; +const allowUpdateBranch = { + title: 'Allow Update Branch', + type: 'boolean', + default: false, + description: 'Allow branch to be updated. The default value is `false`', +}; const squashMergeCommitTitle = { title: 'Default squash merge commit title', enum: ['PR_TITLE', 'COMMIT_OR_PR_TITLE'], @@ -364,6 +370,7 @@ export { allowSquashMerge }; export { squashMergeCommitTitle }; export { squashMergeCommitMessage }; export { allowAutoMerge }; +export { allowUpdateBranch }; export { collaborators }; export { defaultBranch }; export { deleteBranchOnMerge }; From 7cc4995abfd9746124363bf16a628096dfd1a3a3 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Mar 2025 09:13:09 +1100 Subject: [PATCH 06/99] Fix for duplicate results in queryEntities when using an orderField Signed-off-by: Tyler Davis --- .changeset/famous-tips-raise.md | 5 +++++ .../catalog-backend/src/service/DefaultEntitiesCatalog.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/famous-tips-raise.md diff --git a/.changeset/famous-tips-raise.md b/.changeset/famous-tips-raise.md new file mode 100644 index 0000000000..89fc8a6563 --- /dev/null +++ b/.changeset/famous-tips-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fix for duplicate results in queryEntities when using an orderField diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index f0e0667bef..a7b8fdc6c9 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -285,6 +285,7 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { if (sortField) { inner + .distinct() .leftOuterJoin('search', qb => qb .on('search.entity_id', 'final_entities.entity_id') From b8b327dcdbcd9ce737af02e885018afb1a73dd19 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Mar 2025 11:36:45 +1100 Subject: [PATCH 07/99] changeset improvements Signed-off-by: Tyler Davis --- .changeset/famous-tips-raise.md | 2 +- .../src/service/DefaultEntitiesCatalog.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.changeset/famous-tips-raise.md b/.changeset/famous-tips-raise.md index 89fc8a6563..1a44e19608 100644 --- a/.changeset/famous-tips-raise.md +++ b/.changeset/famous-tips-raise.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend': patch --- -Fix for duplicate results in queryEntities when using an orderField +Fix for duplicate results in `queryEntities` when providing an `orderField` parameter diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index a7b8fdc6c9..8e71e61bc5 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -273,6 +273,24 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { const sortField = cursor.orderFields.at(0); + if (sortField) { + const duplicateCheck = await this.database('search') + .select('entity_id', 'key', 'value') + .count('* as count') + .where('key', '=', sortField.field) + .groupBy('entity_id', 'key', 'value') + .having(this.database.raw('count(*) > 1')) + .limit(5); + + if (duplicateCheck.length > 0) { + this.logger.warn( + `Found duplicate search entries for field ${ + sortField.field + }: ${JSON.stringify(duplicateCheck)}`, + ); + } + } + // The first part of the query builder is a subquery that applies all of the // filtering. const dbQuery = this.database.with( From 5fc743a3fd32dd5538eb459ea8c244a314539119 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Mar 2025 11:37:33 +1100 Subject: [PATCH 08/99] remove debugging code Signed-off-by: Tyler Davis --- .../src/service/DefaultEntitiesCatalog.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 8e71e61bc5..a7b8fdc6c9 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -273,24 +273,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { const sortField = cursor.orderFields.at(0); - if (sortField) { - const duplicateCheck = await this.database('search') - .select('entity_id', 'key', 'value') - .count('* as count') - .where('key', '=', sortField.field) - .groupBy('entity_id', 'key', 'value') - .having(this.database.raw('count(*) > 1')) - .limit(5); - - if (duplicateCheck.length > 0) { - this.logger.warn( - `Found duplicate search entries for field ${ - sortField.field - }: ${JSON.stringify(duplicateCheck)}`, - ); - } - } - // The first part of the query builder is a subquery that applies all of the // filtering. const dbQuery = this.database.with( From 0cf9d74a5fea9f5965814ac064c3e1290efba114 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Mar 2025 12:39:33 +1100 Subject: [PATCH 09/99] Add test to replicate the duplicate issue Signed-off-by: Tyler Davis --- .../service/DefaultEntitiesCatalog.test.ts | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 070b93ab2c..aad09d7de1 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -1970,6 +1970,87 @@ describe('DefaultEntitiesCatalog', () => { ).resolves.toEqual(['BB']); }, ); + + it.each(databases.eachSupportedId())( + 'should not return duplicate entities when using orderField, %p', + async databaseId => { + await createDatabase(databaseId); + + // Create a few test entities with different names to sort by + const entities = [ + { + apiVersion: 'a', + kind: 'k', + metadata: { + name: 'a-entity', + title: 'A Test Entity', + uid: 'uid-a', + }, + spec: {}, + }, + { + apiVersion: 'a', + kind: 'k', + metadata: { + name: 'b-entity', + title: 'B Test Entity', + uid: 'uid-b', + }, + spec: {}, + }, + { + apiVersion: 'a', + kind: 'k', + metadata: { + name: 'c-entity', + title: 'C Test Entity', + uid: 'uid-c', + }, + spec: {}, + }, + ]; + + await Promise.all(entities.map(e => addEntityToSearch(e))); + + // Manually insert duplicate search entries for the same entities + // I'm not sure exactly how this happens but I have seen it in the real world + await knex('search').insert([ + { + entity_id: 'uid-a', + key: 'metadata.title', + value: 'a test entity', + original_value: 'A Test Entity', + }, + { + entity_id: 'uid-b', + key: 'metadata.title', + value: 'b test entity', + original_value: 'B Test Entity', + }, + ]); + + const catalog = new DefaultEntitiesCatalog({ + database: knex, + logger: mockServices.logger.mock(), + stitcher, + }); + + // Query with orderField + const response = await catalog.queryEntities({ + orderFields: [{ field: 'metadata.title', order: 'asc' }], + credentials: mockCredentials.none(), + }); + + const resultEntities = entitiesResponseToObjects(response.items); + + // Ensure we get exactly 3 entities back, sorted, with no duplicates + expect(resultEntities.map(e => e!.metadata.name)).toEqual([ + 'a-entity', + 'b-entity', + 'c-entity', + ]); + }, + ); }); describe('removeEntityByUid', () => { From 8a19b9bc23c3d1d17573f8fdbea8f01a6a630603 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 11:34:18 +0100 Subject: [PATCH 10/99] Add TextField docs Signed-off-by: Charles de Dreuille --- .../src/app/(docs)/components/field/page.mdx | 99 ------------------- .../src/app/(docs)/components/field/props.ts | 69 ------------- .../components/{input => text-field}/page.mdx | 42 +++++--- .../components/{input => text-field}/props.ts | 14 ++- canon-docs/src/snippets/stories-snippets.tsx | 20 +--- .../TextField/TextField.stories.tsx | 5 +- .../src/components/TextField/TextField.tsx | 7 +- 7 files changed, 52 insertions(+), 204 deletions(-) delete mode 100644 canon-docs/src/app/(docs)/components/field/page.mdx delete mode 100644 canon-docs/src/app/(docs)/components/field/props.ts rename canon-docs/src/app/(docs)/components/{input => text-field}/page.mdx (51%) rename canon-docs/src/app/(docs)/components/{input => text-field}/props.ts (61%) diff --git a/canon-docs/src/app/(docs)/components/field/page.mdx b/canon-docs/src/app/(docs)/components/field/page.mdx deleted file mode 100644 index 1526ce8c9f..0000000000 --- a/canon-docs/src/app/(docs)/components/field/page.mdx +++ /dev/null @@ -1,99 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { FieldSnippet } from '@/snippets/stories-snippets'; -import { BaseUI } from '@/components/HeadlessBanners/BaseUI'; -import { - fieldRootPropDefs, - fieldLabelPropDefs, - fieldDescriptionPropDefs, - fieldErrorPropDefs, -} from './props'; - -# Field - -A wrapper around `Input` or `Select` component to add label, description and error messages.. - -} - code={``} -/> - - - - Usage - Theming - - - - Name - - Visible on your profile - -`} - /> - - - 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. - - - - -`} - /> - - - -## API reference - - - -### Field.Root - -Groups all parts of the field. Renders a `
` element. - - - -### Field.Label - -An accessible label that is automatically associated with the field control. Renders a `
` element. - - - -## Examples - -### With Label and Description - -Here's a simple input with a label and description. - -} - code={` - Name - Visible on your profile - - `} -/> diff --git a/canon-docs/src/app/(docs)/components/field/props.ts b/canon-docs/src/app/(docs)/components/field/props.ts deleted file mode 100644 index aaadafee50..0000000000 --- a/canon-docs/src/app/(docs)/components/field/props.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const fieldRootPropDefs: Record = { - name: { - type: 'string', - responsive: false, - }, - disabled: { - type: 'boolean', - responsive: false, - }, - invalid: { - type: 'boolean', - responsive: false, - }, - validate: { - type: 'enum', - values: ['(value) => string | string[] | null | Promise'], - responsive: false, - }, - validationMode: { - type: 'enum', - values: ['onBlur', 'onChange'], - responsive: false, - }, - validationDebounceTime: { - type: 'number', - responsive: false, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const fieldLabelPropDefs: Record = { - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const fieldDescriptionPropDefs: Record = { - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const fieldErrorPropDefs: Record = { - match: { - type: 'enum', - values: [ - 'badInput', - 'customError', - 'patternMismatch', - 'rangeOverflow', - 'rangeUnderflow', - 'stepMismatch', - 'tooLong', - 'tooShort', - 'typeMismatch', - 'valid', - 'valueMissing', - ], - responsive: false, - }, - forceShow: { - type: 'boolean', - responsive: false, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/input/page.mdx b/canon-docs/src/app/(docs)/components/text-field/page.mdx similarity index 51% rename from canon-docs/src/app/(docs)/components/input/page.mdx rename to canon-docs/src/app/(docs)/components/text-field/page.mdx index 0ecaaa5409..bd5fa31025 100644 --- a/canon-docs/src/app/(docs)/components/input/page.mdx +++ b/canon-docs/src/app/(docs)/components/text-field/page.mdx @@ -2,19 +2,19 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { Tabs } from '@/components/Tabs'; import { CodeBlock } from '@/components/CodeBlock'; -import { InputSnippet } from '@/snippets/stories-snippets'; +import { TextFieldSnippet } from '@/snippets/stories-snippets'; import { BaseUI } from '@/components/HeadlessBanners/BaseUI'; import { inputPropDefs } from './props'; -# Input +# TextField -A input component tfor your forms. +A text field component for your forms. } - code={``} + preview={} + code={``} /> @@ -24,9 +24,9 @@ A input component tfor your forms. + `} /> @@ -34,30 +34,40 @@ A input component tfor your forms. 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. `} + code={``} /> ## API reference - - ## Examples ### Sizes -Here's a simple input with a label and description. +We support two different sizes: `small`, `medium`. } - code={` - - - `} + preview={} + code={` + + + `} +/> + +### With description + +Here's a simple TextField with a description. + +} + code={``} /> diff --git a/canon-docs/src/app/(docs)/components/input/props.ts b/canon-docs/src/app/(docs)/components/text-field/props.ts similarity index 61% rename from canon-docs/src/app/(docs)/components/input/props.ts rename to canon-docs/src/app/(docs)/components/text-field/props.ts index 412ae23dd6..cafed21189 100644 --- a/canon-docs/src/app/(docs)/components/input/props.ts +++ b/canon-docs/src/app/(docs)/components/text-field/props.ts @@ -4,10 +4,20 @@ import type { PropDef } from '../../../../utils/propDefs'; export const inputPropDefs: Record = { size: { type: 'enum', - values: ['sm', 'md'], - default: 'md', + values: ['small', 'medium'], + default: 'medium', responsive: false, }, + label: { + type: 'string', + }, + description: { + type: 'string', + }, + name: { + type: 'string', + required: true, + }, ...classNamePropDefs, ...stylePropDefs, }; diff --git a/canon-docs/src/snippets/stories-snippets.tsx b/canon-docs/src/snippets/stories-snippets.tsx index 9dec825ad2..5c363d6fc7 100644 --- a/canon-docs/src/snippets/stories-snippets.tsx +++ b/canon-docs/src/snippets/stories-snippets.tsx @@ -5,12 +5,11 @@ import * as BoxStories from '../../../packages/canon/src/components/Box/Box.stor import * as ButtonStories from '../../../packages/canon/src/components/Button/Button.stories'; import * as CheckboxStories from '../../../packages/canon/src/components/Checkbox/Checkbox.stories'; import * as ContainerStories from '../../../packages/canon/src/components/Container/Container.stories'; -import * as FieldStories from '../../../packages/canon/src/components/Field/Field.stories'; import * as GridStories from '../../../packages/canon/src/components/Grid/Grid.stories'; import * as HeadingStories from '../../../packages/canon/src/components/Heading/Heading.stories'; import * as IconButtonStories from '../../../packages/canon/src/components/IconButton/IconButton.stories'; import * as IconStories from '../../../packages/canon/src/components/Icon/Icon.stories'; -import * as InputStories from '../../../packages/canon/src/components/Input/Input.stories'; +import * as TextFieldStories from '../../../packages/canon/src/components/TextField/TextField.stories'; import * as TextStories from '../../../packages/canon/src/components/Text/Text.stories'; import * as FlexStories from '../../../packages/canon/src/components/Flex/Flex.stories'; import * as SelectStories from '../../../packages/canon/src/components/Select/Select.stories'; @@ -62,12 +61,12 @@ export const FlexSnippet = ({ story }: { story: keyof typeof FlexStories }) => { return StoryComponent ? : null; }; -export const FieldSnippet = ({ +export const TextFieldSnippet = ({ story, }: { - story: keyof typeof FieldStories; + story: keyof typeof TextFieldStories; }) => { - const stories = composeStories(FieldStories); + const stories = composeStories(TextFieldStories); const StoryComponent = stories[story as keyof typeof stories]; return StoryComponent ? : null; @@ -109,17 +108,6 @@ export const IconSnippet = ({ story }: { story: keyof typeof IconStories }) => { return StoryComponent ? : null; }; -export const InputSnippet = ({ - story, -}: { - story: keyof typeof InputStories; -}) => { - const stories = composeStories(InputStories); - const StoryComponent = stories[story as keyof typeof stories]; - - return StoryComponent ? : null; -}; - export const TextSnippet = ({ story }: { story: keyof typeof TextStories }) => { const stories = composeStories(TextStories); const StoryComponent = stories[story as keyof typeof stories]; diff --git a/packages/canon/src/components/TextField/TextField.stories.tsx b/packages/canon/src/components/TextField/TextField.stories.tsx index dd5558437b..ca33c28020 100644 --- a/packages/canon/src/components/TextField/TextField.stories.tsx +++ b/packages/canon/src/components/TextField/TextField.stories.tsx @@ -33,6 +33,9 @@ export const Default: Story = { args: { name: 'url', placeholder: 'Enter a URL', + style: { + maxWidth: '300px', + }, }, }; @@ -71,7 +74,7 @@ export const Sizes: Story = { description: 'Description', }, render: args => ( - + diff --git a/packages/canon/src/components/TextField/TextField.tsx b/packages/canon/src/components/TextField/TextField.tsx index ccea9e8c65..d4c95ae8b5 100644 --- a/packages/canon/src/components/TextField/TextField.tsx +++ b/packages/canon/src/components/TextField/TextField.tsx @@ -31,6 +31,7 @@ export const TextField = forwardRef( label, description, name, + style, ...rest } = props; @@ -38,7 +39,11 @@ export const TextField = forwardRef( const responsiveSize = useResponsiveValue(size); return ( - + {label && ( {label} )} From 7ae28baa5976b80b6154aecaac45ab7bb4f53f93 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 11:40:09 +0100 Subject: [PATCH 11/99] Create rare-colts-drum.md Signed-off-by: Charles de Dreuille --- .changeset/rare-colts-drum.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rare-colts-drum.md diff --git a/.changeset/rare-colts-drum.md b/.changeset/rare-colts-drum.md new file mode 100644 index 0000000000..40bd90e0a7 --- /dev/null +++ b/.changeset/rare-colts-drum.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Move styles to the root of the TextField component. From f4e9ee2958c02f8832525f25d89fbc40d96eaef8 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:28:17 +0100 Subject: [PATCH 12/99] Update Container styles Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Container/styles.css | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/canon/src/components/Container/styles.css b/packages/canon/src/components/Container/styles.css index 7c7c323cb5..19c6f7ecc5 100644 --- a/packages/canon/src/components/Container/styles.css +++ b/packages/canon/src/components/Container/styles.css @@ -1,5 +1,17 @@ .canon-Container { - max-width: 75rem; + max-width: 120rem; padding: 0 1rem; margin: 0 auto; } + +@media (min-width: 640px) { + .canon-Container { + padding: 0 2rem; + } +} + +@media (min-width: 1024px) { + .canon-Container { + padding: 0 3rem; + } +} From 24f0e0833381e96feec9431f46499fc80aa23106 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:29:51 +0100 Subject: [PATCH 13/99] Create long-hornets-share.md Signed-off-by: Charles de Dreuille --- .changeset/long-hornets-share.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/long-hornets-share.md diff --git a/.changeset/long-hornets-share.md b/.changeset/long-hornets-share.md new file mode 100644 index 0000000000..039b9f28a6 --- /dev/null +++ b/.changeset/long-hornets-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Improved Container styles, changing our max-width to 120rem and improving padding on smaller screens. From 8ed7d709d7f748994e1576e80aa52461048fed6f Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:43:29 +0100 Subject: [PATCH 14/99] Update styles.css Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Container/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/canon/src/components/Container/styles.css b/packages/canon/src/components/Container/styles.css index 19c6f7ecc5..0e9764e0db 100644 --- a/packages/canon/src/components/Container/styles.css +++ b/packages/canon/src/components/Container/styles.css @@ -1,5 +1,5 @@ .canon-Container { - max-width: 120rem; + max-width: min(120rem, 100%); padding: 0 1rem; margin: 0 auto; } From 394ffff6f448fc843b0d75d46c49494bbc16b9aa Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:44:19 +0100 Subject: [PATCH 15/99] Build CSS Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 14 +++++++++++++- packages/canon/css/container.css | 14 +++++++++++++- packages/canon/css/styles.css | 14 +++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index bc473d2006..a4291f7823 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -103,11 +103,23 @@ } .canon-Container { - max-width: 75rem; + max-width: min(120rem, 100%); margin: 0 auto; padding: 0 1rem; } +@media (width >= 640px) { + .canon-Container { + padding: 0 2rem; + } +} + +@media (width >= 1024px) { + .canon-Container { + padding: 0 3rem; + } +} + .canon-Icon { width: 1rem; height: 1rem; diff --git a/packages/canon/css/container.css b/packages/canon/css/container.css index d9fe861f3b..bcdd296ca2 100644 --- a/packages/canon/css/container.css +++ b/packages/canon/css/container.css @@ -1,5 +1,17 @@ .canon-Container { - max-width: 75rem; + max-width: min(120rem, 100%); margin: 0 auto; padding: 0 1rem; } + +@media (width >= 640px) { + .canon-Container { + padding: 0 2rem; + } +} + +@media (width >= 1024px) { + .canon-Container { + padding: 0 3rem; + } +} diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index b5093e217c..bb56f7c309 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9309,11 +9309,23 @@ } .canon-Container { - max-width: 75rem; + max-width: min(120rem, 100%); margin: 0 auto; padding: 0 1rem; } +@media (width >= 640px) { + .canon-Container { + padding: 0 2rem; + } +} + +@media (width >= 1024px) { + .canon-Container { + padding: 0 3rem; + } +} + .canon-Icon { width: 1rem; height: 1rem; From b492707737eb5f6eb3414435a371fb63be81e09a Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:53:12 +0100 Subject: [PATCH 16/99] Update styles.css Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Container/styles.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/canon/src/components/Container/styles.css b/packages/canon/src/components/Container/styles.css index 0e9764e0db..757628aba4 100644 --- a/packages/canon/src/components/Container/styles.css +++ b/packages/canon/src/components/Container/styles.css @@ -1,17 +1,17 @@ .canon-Container { - max-width: min(120rem, 100%); - padding: 0 1rem; + max-width: 120rem; + padding: 0 var(--canon-space-4); margin: 0 auto; } @media (min-width: 640px) { .canon-Container { - padding: 0 2rem; + padding: 0 var(--canon-space-8); } } @media (min-width: 1024px) { .canon-Container { - padding: 0 3rem; + padding: 0 var(--canon-space-12); } } From bdd2eaae6074a26c35be8314622d2f00d4742c34 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 14:54:06 +0100 Subject: [PATCH 17/99] Build CSS Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 8 ++++---- packages/canon/css/container.css | 8 ++++---- packages/canon/css/styles.css | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index a4291f7823..7d22371ec3 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -103,20 +103,20 @@ } .canon-Container { - max-width: min(120rem, 100%); + max-width: 120rem; + padding: 0 var(--canon-space-4); margin: 0 auto; - padding: 0 1rem; } @media (width >= 640px) { .canon-Container { - padding: 0 2rem; + padding: 0 var(--canon-space-8); } } @media (width >= 1024px) { .canon-Container { - padding: 0 3rem; + padding: 0 var(--canon-space-12); } } diff --git a/packages/canon/css/container.css b/packages/canon/css/container.css index bcdd296ca2..86329e4c30 100644 --- a/packages/canon/css/container.css +++ b/packages/canon/css/container.css @@ -1,17 +1,17 @@ .canon-Container { - max-width: min(120rem, 100%); + max-width: 120rem; + padding: 0 var(--canon-space-4); margin: 0 auto; - padding: 0 1rem; } @media (width >= 640px) { .canon-Container { - padding: 0 2rem; + padding: 0 var(--canon-space-8); } } @media (width >= 1024px) { .canon-Container { - padding: 0 3rem; + padding: 0 var(--canon-space-12); } } diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index bb56f7c309..6017eacf0e 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9309,20 +9309,20 @@ } .canon-Container { - max-width: min(120rem, 100%); + max-width: 120rem; + padding: 0 var(--canon-space-4); margin: 0 auto; - padding: 0 1rem; } @media (width >= 640px) { .canon-Container { - padding: 0 2rem; + padding: 0 var(--canon-space-8); } } @media (width >= 1024px) { .canon-Container { - padding: 0 3rem; + padding: 0 var(--canon-space-12); } } From ff94314f04d340d36836c73e9182e7a9f152518a Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 3 Apr 2025 15:07:12 +0100 Subject: [PATCH 18/99] Update styles.css Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Container/styles.css | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/canon/src/components/Container/styles.css b/packages/canon/src/components/Container/styles.css index 757628aba4..1ebf63987f 100644 --- a/packages/canon/src/components/Container/styles.css +++ b/packages/canon/src/components/Container/styles.css @@ -2,6 +2,7 @@ max-width: 120rem; padding: 0 var(--canon-space-4); margin: 0 auto; + transition: padding 0.2s ease-in-out; } @media (min-width: 640px) { From 2de219e3571dd9dff118225c0e1993b1fd710885 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:52:36 +0000 Subject: [PATCH 19/99] fix(deps): update dependency next to v14.2.26 [security] Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- canon-docs/package.json | 2 +- canon-docs/yarn.lock | 92 ++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/canon-docs/package.json b/canon-docs/package.json index acd20cb66a..5a4affc45b 100644 --- a/canon-docs/package.json +++ b/canon-docs/package.json @@ -18,7 +18,7 @@ "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", "motion": "^12.4.1", - "next": "14.2.25", + "next": "14.2.26", "react": "^18.0.2", "react-dom": "^18.0.2", "react-frame-component": "^5.2.7", diff --git a/canon-docs/yarn.lock b/canon-docs/yarn.lock index 1a78377213..00438a8658 100644 --- a/canon-docs/yarn.lock +++ b/canon-docs/yarn.lock @@ -501,10 +501,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:14.2.25": - version: 14.2.25 - resolution: "@next/env@npm:14.2.25" - checksum: 10/ea8e72daa7f440d00f092b268053d10b924fe42b5f5f31ee9b20a532558508b71a4d7909473304e239b6ca1c578c9504369678c7385314514e1ef95f0da09c03 +"@next/env@npm:14.2.26": + version: 14.2.26 + resolution: "@next/env@npm:14.2.26" + checksum: 10/0caa679cdf9367501daf1bff0e762b720613a3ee1689e5585bfb7c792fdd07c251bc1f7488919a296047eb1f2b82ceb13955b673143468cad338a87b7d98c196 languageName: node linkType: hard @@ -534,65 +534,65 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-darwin-arm64@npm:14.2.25" +"@next/swc-darwin-arm64@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-darwin-arm64@npm:14.2.26" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-darwin-x64@npm:14.2.25" +"@next/swc-darwin-x64@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-darwin-x64@npm:14.2.26" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-linux-arm64-gnu@npm:14.2.25" +"@next/swc-linux-arm64-gnu@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.26" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-linux-arm64-musl@npm:14.2.25" +"@next/swc-linux-arm64-musl@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.26" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-linux-x64-gnu@npm:14.2.25" +"@next/swc-linux-x64-gnu@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.26" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-linux-x64-musl@npm:14.2.25" +"@next/swc-linux-x64-musl@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-linux-x64-musl@npm:14.2.26" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-win32-arm64-msvc@npm:14.2.25" +"@next/swc-win32-arm64-msvc@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.26" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-win32-ia32-msvc@npm:14.2.25" +"@next/swc-win32-ia32-msvc@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.26" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:14.2.25": - version: 14.2.25 - resolution: "@next/swc-win32-x64-msvc@npm:14.2.25" +"@next/swc-win32-x64-msvc@npm:14.2.26": + version: 14.2.26 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.26" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1502,7 +1502,7 @@ __metadata: eslint-config-next: "npm:14.2.23" lightningcss: "npm:^1.28.2" motion: "npm:^12.4.1" - next: "npm:14.2.25" + next: "npm:14.2.26" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-frame-component: "npm:^5.2.7" @@ -4176,20 +4176,20 @@ __metadata: languageName: node linkType: hard -"next@npm:14.2.25": - version: 14.2.25 - resolution: "next@npm:14.2.25" +"next@npm:14.2.26": + version: 14.2.26 + resolution: "next@npm:14.2.26" dependencies: - "@next/env": "npm:14.2.25" - "@next/swc-darwin-arm64": "npm:14.2.25" - "@next/swc-darwin-x64": "npm:14.2.25" - "@next/swc-linux-arm64-gnu": "npm:14.2.25" - "@next/swc-linux-arm64-musl": "npm:14.2.25" - "@next/swc-linux-x64-gnu": "npm:14.2.25" - "@next/swc-linux-x64-musl": "npm:14.2.25" - "@next/swc-win32-arm64-msvc": "npm:14.2.25" - "@next/swc-win32-ia32-msvc": "npm:14.2.25" - "@next/swc-win32-x64-msvc": "npm:14.2.25" + "@next/env": "npm:14.2.26" + "@next/swc-darwin-arm64": "npm:14.2.26" + "@next/swc-darwin-x64": "npm:14.2.26" + "@next/swc-linux-arm64-gnu": "npm:14.2.26" + "@next/swc-linux-arm64-musl": "npm:14.2.26" + "@next/swc-linux-x64-gnu": "npm:14.2.26" + "@next/swc-linux-x64-musl": "npm:14.2.26" + "@next/swc-win32-arm64-msvc": "npm:14.2.26" + "@next/swc-win32-ia32-msvc": "npm:14.2.26" + "@next/swc-win32-x64-msvc": "npm:14.2.26" "@swc/helpers": "npm:0.5.5" busboy: "npm:1.6.0" caniuse-lite: "npm:^1.0.30001579" @@ -4230,7 +4230,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 10/fa0cd12ba7b999c6bd8e72f29df33a235bd7a42136f076b23efe5cc36c4122636e9166594f379bc293e9b7d78d0ece6e86dff54ac65c48393d2601c3efef9432 + checksum: 10/9bd8b2ef40076b251d036c6d3a3b435095ff941bab7166ebc3c75bd832c1812eb368f8dae1e56ef4bf600a81d0ad8190d778d610aa062ffa7c4c4f126cae7f13 languageName: node linkType: hard From fadf709c553ac82f4abc4c4898bc909591aee07a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:47:58 +0000 Subject: [PATCH 20/99] chore(deps): update chromaui/action digest to 649b4fd Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_storybook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 1181273bb0..075c328359 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -54,7 +54,7 @@ jobs: - run: yarn --cwd packages/canon build-storybook - - uses: chromaui/action@c93e0bc3a63aa176e14a75b61a31847cbfdd341c # v11 + - uses: chromaui/action@649b4fd73c3f7cd7a65bd0b9f131349335ec661b # v11 with: token: ${{ secrets.GITHUB_TOKEN }} # projectToken intentionally shared to allow collaborators to run Chromatic on forks From 61d350fd1c3b1ad6a6f590f1bd76322a6f445075 Mon Sep 17 00:00:00 2001 From: Alex McKay Date: Thu, 3 Apr 2025 14:02:44 -0400 Subject: [PATCH 21/99] move catalog filter blueprint to react package Signed-off-by: Alex McKay --- .changeset/happy-spoons-sing.md | 11 +++++++++++ plugins/catalog-react/report-alpha.api.md | 14 ++++++++++++++ .../blueprints/CatalogFilterBlueprint.test.tsx | 0 .../alpha/blueprints/CatalogFilterBlueprint.ts | 0 .../catalog-react/src/alpha/blueprints/index.ts | 1 + plugins/catalog/report-alpha.api.md | 15 --------------- plugins/catalog/src/alpha/blueprints/index.ts | 16 ---------------- plugins/catalog/src/alpha/filters.tsx | 2 +- plugins/catalog/src/alpha/index.ts | 1 - plugins/catalog/src/index.ts | 1 - 10 files changed, 27 insertions(+), 34 deletions(-) create mode 100644 .changeset/happy-spoons-sing.md rename plugins/{catalog => catalog-react}/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx (100%) rename plugins/{catalog => catalog-react}/src/alpha/blueprints/CatalogFilterBlueprint.ts (100%) delete mode 100644 plugins/catalog/src/alpha/blueprints/index.ts diff --git a/.changeset/happy-spoons-sing.md b/.changeset/happy-spoons-sing.md new file mode 100644 index 0000000000..3ac0a48032 --- /dev/null +++ b/.changeset/happy-spoons-sing.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-catalog-react': minor +'@backstage/plugin-catalog': minor +--- + +**BREAKING**: `CatalogFilterBlueprint`, used in the new frontend system, is now exported under plugin-catalog-react instead of plugin-catalog. + +```diff ++ import { CatalogFilterBlueprint } from '@backstage/plugin-catalog-react/alpha'; +- import { CatalogFilterBlueprint } from '@backstage/plugin-catalog/alpha'; +``` diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 2df3371353..f3aaf6af9a 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -16,6 +16,20 @@ import { ResourcePermission } from '@backstage/plugin-permission-common'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { TranslationRef } from '@backstage/core-plugin-api/alpha'; +// @alpha +export const CatalogFilterBlueprint: ExtensionBlueprint<{ + kind: 'catalog-filter'; + name: undefined; + params: { + loader: () => Promise; + }; + output: ConfigurableExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: never; +}>; + // @alpha (undocumented) export const catalogReactTranslationRef: TranslationRef< 'catalog-react', diff --git a/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx similarity index 100% rename from plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx rename to plugins/catalog-react/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx diff --git a/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.ts b/plugins/catalog-react/src/alpha/blueprints/CatalogFilterBlueprint.ts similarity index 100% rename from plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.ts rename to plugins/catalog-react/src/alpha/blueprints/CatalogFilterBlueprint.ts diff --git a/plugins/catalog-react/src/alpha/blueprints/index.ts b/plugins/catalog-react/src/alpha/blueprints/index.ts index 10e339c44d..dc3fa7de1b 100644 --- a/plugins/catalog-react/src/alpha/blueprints/index.ts +++ b/plugins/catalog-react/src/alpha/blueprints/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { CatalogFilterBlueprint } from './CatalogFilterBlueprint'; export { EntityCardBlueprint } from './EntityCardBlueprint'; export { EntityContentBlueprint } from './EntityContentBlueprint'; export { diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index 77d8dbec50..c63f0f4fce 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -12,7 +12,6 @@ import { Entity } from '@backstage/catalog-model'; import { EntityCardType } from '@backstage/plugin-catalog-react/alpha'; import { EntityContentLayoutProps } from '@backstage/plugin-catalog-react/alpha'; import { EntityPredicate } from '@backstage/plugin-catalog-react/alpha'; -import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; @@ -25,20 +24,6 @@ import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-rea import { SearchResultListItemBlueprintParams } from '@backstage/plugin-search-react/alpha'; import { TranslationRef } from '@backstage/core-plugin-api/alpha'; -// @alpha -export const CatalogFilterBlueprint: ExtensionBlueprint<{ - kind: 'catalog-filter'; - name: undefined; - params: { - loader: () => Promise; - }; - output: ConfigurableExtensionDataRef; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: never; -}>; - // @alpha (undocumented) export const catalogTranslationRef: TranslationRef< 'catalog', diff --git a/plugins/catalog/src/alpha/blueprints/index.ts b/plugins/catalog/src/alpha/blueprints/index.ts deleted file mode 100644 index a3a7bcb33f..0000000000 --- a/plugins/catalog/src/alpha/blueprints/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { CatalogFilterBlueprint } from './CatalogFilterBlueprint'; diff --git a/plugins/catalog/src/alpha/filters.tsx b/plugins/catalog/src/alpha/filters.tsx index 3507658731..1ff2981864 100644 --- a/plugins/catalog/src/alpha/filters.tsx +++ b/plugins/catalog/src/alpha/filters.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { CatalogFilterBlueprint } from './blueprints'; +import { CatalogFilterBlueprint } from '@backstage/plugin-catalog-react/alpha'; const catalogTagCatalogFilter = CatalogFilterBlueprint.make({ name: 'tag', diff --git a/plugins/catalog/src/alpha/index.ts b/plugins/catalog/src/alpha/index.ts index 3357024fd9..c9f4c8c4b7 100644 --- a/plugins/catalog/src/alpha/index.ts +++ b/plugins/catalog/src/alpha/index.ts @@ -16,5 +16,4 @@ export { default } from './plugin'; -export * from './blueprints'; export * from './translation'; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index f3cc7d3d0b..6555b4a2a0 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -21,7 +21,6 @@ */ export * from './apis'; - export type { AboutCardProps, AboutContentProps, From 3fe0b6a8a083c4d87036963a482723bbd996b345 Mon Sep 17 00:00:00 2001 From: Alex McKay Date: Thu, 3 Apr 2025 14:06:02 -0400 Subject: [PATCH 22/99] re-add accidentally deleted space Signed-off-by: Alex McKay --- plugins/catalog/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 6555b4a2a0..f3cc7d3d0b 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -21,6 +21,7 @@ */ export * from './apis'; + export type { AboutCardProps, AboutContentProps, From 8c2a4e95491d678fea4a82955219d8cc06b9702a Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 08:58:07 +0100 Subject: [PATCH 23/99] Move to useRender from BaseUI Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 27 ++++++++ .../src/components/Link/Link.stories.tsx | 18 ++++++ packages/canon/src/components/Link/Link.tsx | 63 +++++++------------ packages/canon/src/components/Link/types.ts | 14 ++--- 4 files changed, 73 insertions(+), 49 deletions(-) diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 766b40febd..7335ee93f3 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -15,6 +15,7 @@ import { RefAttributes } from 'react'; import type { RemixiconComponentType } from '@remixicon/react'; import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area'; import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip'; +import type { useRender } from '@base-ui-components/react/use-render'; // @public (undocumented) export type AlignItems = 'stretch' | 'start' | 'center' | 'end'; @@ -696,6 +697,32 @@ export type JustifyContent = | 'around' | 'between'; +// @public (undocumented) +export const Link: React_2.ForwardRefExoticComponent< + Omit & React_2.RefAttributes +>; + +// @public (undocumented) +export interface LinkProps extends useRender.ComponentProps<'a'> { + // (undocumented) + children: ReactNode; + // (undocumented) + className?: string; + // (undocumented) + style?: CSSProperties; + // (undocumented) + to?: string; + // (undocumented) + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + // (undocumented) + weight?: 'regular' | 'bold' | Partial>; +} + // @public (undocumented) export const marginPropDefs: (spacingValues: string[]) => { m: { diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx index 856fe2532d..bdaa4006ae 100644 --- a/packages/canon/src/components/Link/Link.stories.tsx +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -19,6 +19,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Link } from './Link'; import { Flex } from '../Flex'; import { Text } from '../Text'; +import { Link as RouterLink, MemoryRouter } from 'react-router-dom'; const meta = { title: 'Components/Link', @@ -74,6 +75,23 @@ export const Responsive: Story = { }, }; +export const CustomRender: Story = { + render: () => { + return ( + + }>Go to Catalog + + ); + }, + decorators: [ + Story => ( + + + + ), + ], +}; + export const Playground: Story = { args: { ...Default.args, diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx index e3f636ea06..e9d0d6aadf 100644 --- a/packages/canon/src/components/Link/Link.tsx +++ b/packages/canon/src/components/Link/Link.tsx @@ -14,59 +14,44 @@ * limitations under the License. */ -import React, { forwardRef, memo, ComponentType } from 'react'; +import React, { forwardRef } from 'react'; +import { useRender } from '@base-ui-components/react/use-render'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; import clsx from 'clsx'; -import type { LinkProps, LinkRenderProps } from './types'; +import type { LinkProps } from './types'; /** @public */ -export const Link = memo( - forwardRef((props, ref) => { - const { - children, - variant = 'body', - weight = 'regular', - style, - className, - render, - to, - ...restProps - } = props; +export const Link = forwardRef((props, ref) => { + const { + className, + variant = 'body', + weight = 'regular', + render = , + ...restProps + } = props; - const responsiveVariant = useResponsiveValue(variant); - const responsiveWeight = useResponsiveValue(weight); + const responsiveVariant = useResponsiveValue(variant); + const responsiveWeight = useResponsiveValue(weight); + const internalRef = React.useRef(null); - const linkProps: LinkRenderProps = { + const { renderElement } = useRender({ + render, + props: { className: clsx( 'canon-Link', responsiveVariant && `canon-Link--variant-${responsiveVariant}`, responsiveWeight && `canon-Link--weight-${responsiveWeight}`, className, ), - style, - children, - to, + responsiveVariant, + responsiveWeight, ...restProps, - }; + }, + refs: [ref, internalRef], + }); - if (render) { - // If render is a component type, wrap it in memo to prevent unnecessary re-renders - if (typeof render === 'function' && !render.length) { - const MemoizedComponent = memo( - render as ComponentType, - ); - return ; - } - // If it's a render function, call it directly - const RenderComponent = render as ( - props: LinkRenderProps, - ) => React.ReactNode; - return ; - } - - return ; - }), -); + return renderElement(); +}); Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts index 134509563a..20fa4a2f31 100644 --- a/packages/canon/src/components/Link/types.ts +++ b/packages/canon/src/components/Link/types.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import type { CSSProperties, ReactNode, ComponentType } from 'react'; +import type { CSSProperties, ReactNode } from 'react'; import type { Breakpoint } from '../../types'; +import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ -export interface LinkProps - extends Omit, 'to'> { +export interface LinkProps extends useRender.ComponentProps<'a'> { children: ReactNode; - to: string; + to?: string; variant?: | 'subtitle' | 'body' @@ -31,10 +31,4 @@ export interface LinkProps weight?: 'regular' | 'bold' | Partial>; className?: string; style?: CSSProperties; - render?: - | ((props: Omit) => ReactNode) - | ComponentType>; } - -/** @public */ -export type LinkRenderProps = Omit; From 92e3a5ad3caf9f8ed8c224a41981691913061054 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 11:31:42 +0100 Subject: [PATCH 24/99] Add transition Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 1 + packages/canon/css/container.css | 1 + packages/canon/css/styles.css | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 7d22371ec3..bcd347382c 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -106,6 +106,7 @@ max-width: 120rem; padding: 0 var(--canon-space-4); margin: 0 auto; + transition: padding .2s ease-in-out; } @media (width >= 640px) { diff --git a/packages/canon/css/container.css b/packages/canon/css/container.css index 86329e4c30..4ba6b65dee 100644 --- a/packages/canon/css/container.css +++ b/packages/canon/css/container.css @@ -2,6 +2,7 @@ max-width: 120rem; padding: 0 var(--canon-space-4); margin: 0 auto; + transition: padding .2s ease-in-out; } @media (width >= 640px) { diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 6017eacf0e..37c5cb4d2d 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9312,6 +9312,7 @@ max-width: 120rem; padding: 0 var(--canon-space-4); margin: 0 auto; + transition: padding .2s ease-in-out; } @media (width >= 640px) { From eb9a514c17008ee0becda2a927f146b80120e001 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 13:07:35 +0100 Subject: [PATCH 25/99] Improve TextField Signed-off-by: Charles de Dreuille --- packages/canon/.storybook/main.ts | 5 +- packages/canon/package.json | 1 + .../TextField/TextField.stories.tsx | 90 ++++--------------- .../components/TextField/TextField.styles.css | 31 ++++--- .../src/components/TextField/TextField.tsx | 55 +++++++++--- .../canon/src/components/TextField/types.ts | 2 + packages/canon/src/stories/Form.stories.tsx | 68 ++++++++++++++ yarn.lock | 3 +- 8 files changed, 159 insertions(+), 96 deletions(-) create mode 100644 packages/canon/src/stories/Form.stories.tsx diff --git a/packages/canon/.storybook/main.ts b/packages/canon/.storybook/main.ts index bb00568cb0..8bd28db556 100644 --- a/packages/canon/.storybook/main.ts +++ b/packages/canon/.storybook/main.ts @@ -9,7 +9,10 @@ function getAbsolutePath(value: string): any { return dirname(require.resolve(join(value, 'package.json'))); } const config: StorybookConfig = { - stories: ['../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + stories: [ + '../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)', + '../src/stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', + ], staticDirs: ['../static'], addons: [ getAbsolutePath('@storybook/addon-webpack5-compiler-swc'), diff --git a/packages/canon/package.json b/packages/canon/package.json index b5d2d715f6..e0febe544d 100644 --- a/packages/canon/package.json +++ b/packages/canon/package.json @@ -66,6 +66,7 @@ "mini-css-extract-plugin": "^2.9.2", "react": "^18.0.2", "react-dom": "^18.0.2", + "react-hook-form": "^7.55.0", "react-router-dom": "^6.3.0", "storybook": "^8.6.8" }, diff --git a/packages/canon/src/components/TextField/TextField.stories.tsx b/packages/canon/src/components/TextField/TextField.stories.tsx index dd5558437b..39f6d1926f 100644 --- a/packages/canon/src/components/TextField/TextField.stories.tsx +++ b/packages/canon/src/components/TextField/TextField.stories.tsx @@ -14,11 +14,9 @@ * limitations under the License. */ -import React, { useState } from 'react'; +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { TextField } from './TextField'; -import { Form } from '@base-ui-components/react/form'; -import { Button } from '../Button'; import { Flex } from '../Flex'; const meta = { @@ -57,6 +55,13 @@ export const WithDescription: Story = { }, }; +export const Required: Story = { + args: { + ...WithLabel.args, + required: true, + }, +}; + export const Disabled: Story = { args: { ...WithLabel.args, @@ -88,76 +93,17 @@ export const Responsive: Story = { }, }; -export const ShowErrorOnSubmit: Story = { +export const withError: Story = { args: { ...WithLabel.args, - pattern: 'https?://.*', - type: 'url', - required: true, - label: 'Homepage', - name: 'url', - value: 'https://backstage-fake-site.com', + error: 'Invalid URL', + }, +}; + +export const withErrorAndDescription: Story = { + args: { + ...WithLabel.args, + error: 'Invalid URL', + description: 'Description', }, - decorators: [ - Story => { - const [errors, setErrors] = useState | undefined>( - undefined, - ); - const [loading, setLoading] = useState(false); - - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - const formData = new FormData(event.currentTarget); - - setLoading(true); - - await new Promise(resolve => { - setTimeout(resolve, 200); - }); - - try { - const url = new URL(formData.get('url') as string); - - const allowedHosts = [ - 'backstage.io', - 'beta.backstage.io', - 'www.backstage.io', - ]; - - if (!allowedHosts.includes(url.hostname)) { - setErrors({ url: 'The example domain is not allowed' }); - setLoading(false); - - return; - } - - setErrors(undefined); - setLoading(false); - - return; - } catch { - setErrors({ url: 'This is not a valid URL' }); - setLoading(false); - } - }; - - return ( -
setErrors(undefined)} - onSubmit={handleSubmit} - > - - - - ); - }, - ], }; diff --git a/packages/canon/src/components/TextField/TextField.styles.css b/packages/canon/src/components/TextField/TextField.styles.css index bd36661b23..5ffae8aa16 100644 --- a/packages/canon/src/components/TextField/TextField.styles.css +++ b/packages/canon/src/components/TextField/TextField.styles.css @@ -14,21 +14,21 @@ * limitations under the License. */ -.canon-FieldRoot { +.canon-TextField { display: flex; flex-direction: column; font-family: var(--canon-font-regular); width: 100%; } -.canon-FieldLabel { +.canon-TextField--label { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-FieldDescription { +.canon-TextField--description { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -36,7 +36,7 @@ padding-top: var(--canon-space-1_5); } -.canon-FieldError { +.canon-TextField--error { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -44,7 +44,7 @@ padding-top: var(--canon-space-1_5); } -.canon-Input { +.canon-TextField--input { border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); padding: 0 var(--canon-space-4); @@ -57,34 +57,41 @@ width: 100%; } -.canon-Input::placeholder { +.canon-TextField--input::placeholder { color: var(--canon-fg-secondary); } -.canon-Input:hover { +.canon-TextField--input:hover { border-color: var(--canon-border-hover); } -.canon-Input:focus-visible { +.canon-TextField--input:focus-visible { outline-color: var(--canon-border-pressed); outline-width: 0px; border-color: var(--canon-border-pressed); } -.canon-Input[data-invalid] { +.canon-TextField--input[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-Input[data-disabled] { +.canon-TextField--input[data-disabled] { opacity: 0.5; cursor: not-allowed; border: 1px solid var(--canon-border-disabled); } -.canon-Input--size-small { +.canon-TextField--input-size-small { height: 2rem; } -.canon-Input--size-medium { +.canon-TextField--input-size-medium { height: 2.5rem; } + +.canon-TextField--required { + color: var(--canon-fg-danger); + font-size: var(--canon-font-size-3); + font-weight: var(--canon-font-weight-regular); + margin-left: var(--canon-space-1); +} diff --git a/packages/canon/src/components/TextField/TextField.tsx b/packages/canon/src/components/TextField/TextField.tsx index ccea9e8c65..2b96bfe555 100644 --- a/packages/canon/src/components/TextField/TextField.tsx +++ b/packages/canon/src/components/TextField/TextField.tsx @@ -15,7 +15,6 @@ */ import React, { forwardRef } from 'react'; -import { Field } from '@base-ui-components/react/field'; import { Input } from '@base-ui-components/react/input'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; import clsx from 'clsx'; @@ -23,7 +22,7 @@ import clsx from 'clsx'; import type { TextFieldProps } from './types'; /** @public */ -export const TextField = forwardRef( +export const TextField = forwardRef( (props: TextFieldProps, ref) => { const { className, @@ -31,30 +30,66 @@ export const TextField = forwardRef( label, description, name, + error, + required, + disabled, ...rest } = props; // Get the responsive value for the variant const responsiveSize = useResponsiveValue(size); + // Generate unique IDs for accessibility + const inputId = `textfield-${name}`; + const descriptionId = `${inputId}-description`; + const errorId = `${inputId}-error`; + return ( - +
{label && ( - {label} + )} {description && ( - +

{description} - +

)} - - + {error && ( + + )} +
); }, ); diff --git a/packages/canon/src/components/TextField/types.ts b/packages/canon/src/components/TextField/types.ts index 100d5fcb17..fbb1c0393d 100644 --- a/packages/canon/src/components/TextField/types.ts +++ b/packages/canon/src/components/TextField/types.ts @@ -44,4 +44,6 @@ export interface TextFieldProps * The name of the text field */ name: string; + + error?: string | null; } diff --git a/packages/canon/src/stories/Form.stories.tsx b/packages/canon/src/stories/Form.stories.tsx new file mode 100644 index 0000000000..76710d30dc --- /dev/null +++ b/packages/canon/src/stories/Form.stories.tsx @@ -0,0 +1,68 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { TextField } from '../components/TextField'; +import { useForm, SubmitHandler } from 'react-hook-form'; + +const meta = { + title: 'Global/Form', +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +type Inputs = { + example: string; + exampleRequired: string; +}; + +export const Default: Story = { + render: () => { + const { + register, + handleSubmit, + watch, + formState: { errors }, + } = useForm(); + const onSubmit: SubmitHandler = data => console.log(data); + + console.log(watch('example')); // watch input value by passing the name of it + + return ( +
+ {/* register your input into the hook by invoking the "register" function */} + + + {/* include validation with required or other standard HTML validation rules */} + {/* */} + + + + {/* errors will return when field validation fails */} + {errors.exampleRequired && This field is required} + + + + ); + }, +}; diff --git a/yarn.lock b/yarn.lock index 9b199ff03b..839ccaefe9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3800,6 +3800,7 @@ __metadata: mini-css-extract-plugin: "npm:^2.9.2" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" + react-hook-form: "npm:^7.55.0" react-router-dom: "npm:^6.3.0" storybook: "npm:^8.6.8" peerDependencies: @@ -41500,7 +41501,7 @@ __metadata: languageName: node linkType: hard -"react-hook-form@npm:^7.12.2": +"react-hook-form@npm:^7.12.2, react-hook-form@npm:^7.55.0": version: 7.55.0 resolution: "react-hook-form@npm:7.55.0" peerDependencies: From 7b544e19d5f57209a962390572595a4d3fe61f27 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 13:32:46 +0100 Subject: [PATCH 26/99] Improve a11y Signed-off-by: Charles de Dreuille --- packages/canon/package.json | 1 - packages/canon/report.api.md | 3 +- .../components/TextField/TextField.styles.css | 4 +-- .../src/components/TextField/TextField.tsx | 32 +++++++------------ .../canon/src/components/TextField/types.ts | 3 ++ yarn.lock | 3 +- 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/packages/canon/package.json b/packages/canon/package.json index e0febe544d..b5d2d715f6 100644 --- a/packages/canon/package.json +++ b/packages/canon/package.json @@ -66,7 +66,6 @@ "mini-css-extract-plugin": "^2.9.2", "react": "^18.0.2", "react-dom": "^18.0.2", - "react-hook-form": "^7.55.0", "react-router-dom": "^6.3.0", "storybook": "^8.6.8" }, diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 766b40febd..c4cfbefcc2 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -1055,7 +1055,7 @@ export { Text_2 as Text }; // @public (undocumented) export const TextField: React_2.ForwardRefExoticComponent< - TextFieldProps & React_2.RefAttributes + TextFieldProps & React_2.RefAttributes >; // @public (undocumented) @@ -1063,6 +1063,7 @@ export interface TextFieldProps extends Omit, 'size'> { className?: string; description?: string; + error?: string | null; label?: string; name: string; size?: 'small' | 'medium' | Partial>; diff --git a/packages/canon/src/components/TextField/TextField.styles.css b/packages/canon/src/components/TextField/TextField.styles.css index 5ffae8aa16..899acc0034 100644 --- a/packages/canon/src/components/TextField/TextField.styles.css +++ b/packages/canon/src/components/TextField/TextField.styles.css @@ -90,8 +90,8 @@ } .canon-TextField--required { - color: var(--canon-fg-danger); - font-size: var(--canon-font-size-3); + color: var(--canon-fg-secondary); + font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); margin-left: var(--canon-space-1); } diff --git a/packages/canon/src/components/TextField/TextField.tsx b/packages/canon/src/components/TextField/TextField.tsx index 2b96bfe555..c3e1783561 100644 --- a/packages/canon/src/components/TextField/TextField.tsx +++ b/packages/canon/src/components/TextField/TextField.tsx @@ -32,7 +32,6 @@ export const TextField = forwardRef( name, error, required, - disabled, ...rest } = props; @@ -40,31 +39,18 @@ export const TextField = forwardRef( const responsiveSize = useResponsiveValue(size); // Generate unique IDs for accessibility - const inputId = `textfield-${name}`; - const descriptionId = `${inputId}-description`; - const errorId = `${inputId}-error`; + const inputId = React.useId(); + const descriptionId = React.useId(); + const errorId = React.useId(); return ( -
+
{label && ( -