From ccb1fc68b6a0c248ff0da98db78b1f96e87b55c2 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 16 May 2025 18:57:36 +0100 Subject: [PATCH 1/2] Improve the way we treat custom render on Text and Heading Signed-off-by: Charles de Dreuille --- .changeset/puny-garlics-bathe.md | 5 +++ packages/canon/css/components.css | 20 +++++++++ packages/canon/css/heading.css | 20 +++++++++ packages/canon/css/styles.css | 20 +++++++++ packages/canon/report.api.md | 29 ++++++++----- .../components/Heading/Heading.stories.tsx | 29 +++++++++---- .../canon/src/components/Heading/Heading.tsx | 41 ++++++++----------- .../canon/src/components/Heading/styles.css | 20 +++++++++ .../canon/src/components/Heading/types.ts | 18 ++++++-- .../src/components/Text/Text.stories.tsx | 7 ++++ packages/canon/src/components/Text/Text.tsx | 35 ++++++++-------- packages/canon/src/components/Text/types.ts | 7 ++-- 12 files changed, 188 insertions(+), 63 deletions(-) create mode 100644 .changeset/puny-garlics-bathe.md diff --git a/.changeset/puny-garlics-bathe.md b/.changeset/puny-garlics-bathe.md new file mode 100644 index 0000000000..97071ac2b7 --- /dev/null +++ b/.changeset/puny-garlics-bathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +We are modifying the way we treat custom render using 'useRender()' under the hood from BaseUI. diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 23133c9966..496dd1303d 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -447,6 +447,26 @@ font-weight: var(--canon-font-weight-bold); } +.canon-Heading[data-color="primary"] { + color: var(--canon-fg-primary); +} + +.canon-Heading[data-color="secondary"] { + color: var(--canon-fg-secondary); +} + +.canon-Heading[data-color="danger"] { + color: var(--canon-fg-danger); +} + +.canon-Heading[data-color="warning"] { + color: var(--canon-fg-warning); +} + +.canon-Heading[data-color="success"] { + color: var(--canon-fg-success); +} + .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/css/heading.css b/packages/canon/css/heading.css index 53e8d8f25a..50e9d3503d 100644 --- a/packages/canon/css/heading.css +++ b/packages/canon/css/heading.css @@ -36,6 +36,26 @@ font-weight: var(--canon-font-weight-bold); } +.canon-Heading[data-color="primary"] { + color: var(--canon-fg-primary); +} + +.canon-Heading[data-color="secondary"] { + color: var(--canon-fg-secondary); +} + +.canon-Heading[data-color="danger"] { + color: var(--canon-fg-danger); +} + +.canon-Heading[data-color="warning"] { + color: var(--canon-fg-warning); +} + +.canon-Heading[data-color="success"] { + color: var(--canon-fg-success); +} + .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 42d47267f8..aceb4e39db 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9671,6 +9671,26 @@ font-weight: var(--canon-font-weight-bold); } +.canon-Heading[data-color="primary"] { + color: var(--canon-fg-primary); +} + +.canon-Heading[data-color="secondary"] { + color: var(--canon-fg-secondary); +} + +.canon-Heading[data-color="danger"] { + color: var(--canon-fg-danger); +} + +.canon-Heading[data-color="warning"] { + color: var(--canon-fg-warning); +} + +.canon-Heading[data-color="success"] { + color: var(--canon-fg-success); +} + .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index ecfdfde188..0ffa6052c2 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -613,18 +613,28 @@ export interface GridProps extends SpaceProps { // @public (undocumented) export const Heading: ForwardRefExoticComponent< - HeadingProps & RefAttributes + Omit & RefAttributes >; // @public (undocumented) -export interface HeadingProps { - // (undocumented) - as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; - // (undocumented) - children: React.ReactNode; +export interface HeadingProps + extends Omit, 'color'> { // (undocumented) className?: string; // (undocumented) + color?: + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | Partial< + Record< + Breakpoint, + 'primary' | 'secondary' | 'danger' | 'warning' | 'success' + > + >; + // (undocumented) style?: React.CSSProperties; // (undocumented) truncate?: boolean; @@ -1208,7 +1218,7 @@ export interface TableCellTextProps // @public (undocumented) const Text_2: ForwardRefExoticComponent< - TextProps & RefAttributes + Omit & RefAttributes >; export { Text_2 as Text }; @@ -1231,9 +1241,8 @@ export interface TextFieldProps } // @public (undocumented) -export interface TextProps { - // (undocumented) - children: ReactNode; +export interface TextProps + extends Omit, 'color'> { // (undocumented) className?: string; // (undocumented) diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx index 59ac1aab91..aa9625ae03 100644 --- a/packages/canon/src/components/Heading/Heading.stories.tsx +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -50,6 +50,21 @@ export const AllVariants: Story = { ), }; +export const AllColors: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + + + ), +}; + export const Truncate: Story = { args: { ...Title1.args, @@ -67,13 +82,6 @@ export const Responsive: Story = { }, }; -export const CustomTag: Story = { - args: { - variant: 'title5', - as: 'h2', - }, -}; - export const WrappedInLink: Story = { args: { ...Default.args, @@ -87,6 +95,13 @@ export const WrappedInLink: Story = { ], }; +export const CustomRender: Story = { + args: { + ...Default.args, + render:

, + }, +}; + export const Playground: Story = { render: () => ( diff --git a/packages/canon/src/components/Heading/Heading.tsx b/packages/canon/src/components/Heading/Heading.tsx index b7f99cd102..6ea2fe006b 100644 --- a/packages/canon/src/components/Heading/Heading.tsx +++ b/packages/canon/src/components/Heading/Heading.tsx @@ -14,46 +14,41 @@ * limitations under the License. */ -import { forwardRef } from 'react'; +import { forwardRef, useRef } from 'react'; import clsx from 'clsx'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; - +import { useRender } from '@base-ui-components/react/use-render'; import type { HeadingProps } from './types'; /** @public */ export const Heading = forwardRef( (props, ref) => { const { - children, variant = 'title1', - as = 'h1', + color = 'primary', truncate, className, + render =

, ...restProps } = props; - // Get the responsive value for the variant const responsiveVariant = useResponsiveValue(variant); + const responsiveColor = useResponsiveValue(color); + const internalRef = useRef(null); - // Determine the component to render based on the variant - let Component = as; - if (variant === 'title2') Component = 'h2'; - if (variant === 'title3') Component = 'h3'; - if (variant === 'title4') Component = 'h4'; - if (variant === 'title5') Component = 'h5'; - if (as) Component = as; + const { renderElement } = useRender({ + render, + props: { + className: clsx('canon-Heading', className), + ['data-variant']: responsiveVariant, + ['data-color']: responsiveColor, + ['data-truncate']: truncate, + ...restProps, + }, + refs: [ref, internalRef], + }); - return ( - - {children} - - ); + return renderElement(); }, ); diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css index f0f4561400..c629c1ba48 100644 --- a/packages/canon/src/components/Heading/styles.css +++ b/packages/canon/src/components/Heading/styles.css @@ -52,6 +52,26 @@ font-weight: var(--canon-font-weight-bold); } +.canon-Heading[data-color='primary'] { + color: var(--canon-fg-primary); +} + +.canon-Heading[data-color='secondary'] { + color: var(--canon-fg-secondary); +} + +.canon-Heading[data-color='danger'] { + color: var(--canon-fg-danger); +} + +.canon-Heading[data-color='warning'] { + color: var(--canon-fg-warning); +} + +.canon-Heading[data-color='success'] { + color: var(--canon-fg-success); +} + .canon-Heading[data-truncate] { overflow: hidden; text-overflow: ellipsis; diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts index b87f16eb34..312682033d 100644 --- a/packages/canon/src/components/Heading/types.ts +++ b/packages/canon/src/components/Heading/types.ts @@ -15,10 +15,11 @@ */ import { Breakpoint } from '../../types'; +import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ -export interface HeadingProps { - children: React.ReactNode; +export interface HeadingProps + extends Omit, 'color'> { variant?: | 'display' | 'title1' @@ -32,7 +33,18 @@ export interface HeadingProps { 'display' | 'title1' | 'title2' | 'title3' | 'title4' | 'title5' > >; - as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; + color?: + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | Partial< + Record< + Breakpoint, + 'primary' | 'secondary' | 'danger' | 'warning' | 'success' + > + >; truncate?: boolean; className?: string; style?: React.CSSProperties; diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index ba1155daed..5fe0c12cb5 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -108,6 +108,13 @@ export const WrappedInLink: Story = { ], }; +export const CustomRender: Story = { + args: { + ...Default.args, + render: , + }, +}; + export const Playground: Story = { render: () => ( diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 90cc9bb2ac..7e4dc74720 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -14,8 +14,9 @@ * limitations under the License. */ -import { forwardRef } from 'react'; +import { forwardRef, useRef } from 'react'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import { useRender } from '@base-ui-components/react/use-render'; import clsx from 'clsx'; import type { TextProps } from './types'; @@ -24,13 +25,12 @@ import type { TextProps } from './types'; export const Text = forwardRef( (props, ref) => { const { - children, variant = 'body', weight = 'regular', color = 'primary', - style, className, truncate, + render =

, ...restProps } = props; @@ -38,21 +38,22 @@ export const Text = forwardRef( const responsiveVariant = useResponsiveValue(variant); const responsiveWeight = useResponsiveValue(weight); const responsiveColor = useResponsiveValue(color); + const internalRef = useRef(null); - return ( -

- {children} -

- ); + const { renderElement } = useRender({ + render, + props: { + className: clsx('canon-Text', className), + ['data-variant']: responsiveVariant, + ['data-weight']: responsiveWeight, + ['data-color']: responsiveColor, + ['data-truncate']: truncate, + ...restProps, + }, + refs: [ref, internalRef], + }); + + return renderElement(); }, ); diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts index 7adafd73d7..f9c3c0aaca 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import type { CSSProperties, ReactNode } from 'react'; +import type { CSSProperties } from 'react'; import type { Breakpoint } from '../../types'; +import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ -export interface TextProps { - children: ReactNode; +export interface TextProps + extends Omit, 'color'> { variant?: | 'subtitle' | 'body' From 4934e7abc9468b47c4bf4a8667e95c907e916474 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 16 May 2025 19:00:50 +0100 Subject: [PATCH 2/2] Improve colors for Heading Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 12 ------------ packages/canon/css/heading.css | 12 ------------ packages/canon/css/styles.css | 12 ------------ packages/canon/report.api.md | 10 +--------- .../canon/src/components/Heading/Heading.stories.tsx | 3 --- packages/canon/src/components/Heading/styles.css | 12 ------------ packages/canon/src/components/Heading/types.ts | 10 +--------- 7 files changed, 2 insertions(+), 69 deletions(-) diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 496dd1303d..6ed608a808 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -455,18 +455,6 @@ color: var(--canon-fg-secondary); } -.canon-Heading[data-color="danger"] { - color: var(--canon-fg-danger); -} - -.canon-Heading[data-color="warning"] { - color: var(--canon-fg-warning); -} - -.canon-Heading[data-color="success"] { - color: var(--canon-fg-success); -} - .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/css/heading.css b/packages/canon/css/heading.css index 50e9d3503d..96d6f13fac 100644 --- a/packages/canon/css/heading.css +++ b/packages/canon/css/heading.css @@ -44,18 +44,6 @@ color: var(--canon-fg-secondary); } -.canon-Heading[data-color="danger"] { - color: var(--canon-fg-danger); -} - -.canon-Heading[data-color="warning"] { - color: var(--canon-fg-warning); -} - -.canon-Heading[data-color="success"] { - color: var(--canon-fg-success); -} - .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index aceb4e39db..3038e919ba 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9679,18 +9679,6 @@ color: var(--canon-fg-secondary); } -.canon-Heading[data-color="danger"] { - color: var(--canon-fg-danger); -} - -.canon-Heading[data-color="warning"] { - color: var(--canon-fg-warning); -} - -.canon-Heading[data-color="success"] { - color: var(--canon-fg-success); -} - .canon-Heading[data-truncate] { text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 0ffa6052c2..43ab122f5a 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -625,15 +625,7 @@ export interface HeadingProps color?: | 'primary' | 'secondary' - | 'danger' - | 'warning' - | 'success' - | Partial< - Record< - Breakpoint, - 'primary' | 'secondary' | 'danger' | 'warning' | 'success' - > - >; + | Partial>; // (undocumented) style?: React.CSSProperties; // (undocumented) diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx index aa9625ae03..bb241b5aae 100644 --- a/packages/canon/src/components/Heading/Heading.stories.tsx +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -58,9 +58,6 @@ export const AllColors: Story = { - - - ), }; diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css index c629c1ba48..1644da09df 100644 --- a/packages/canon/src/components/Heading/styles.css +++ b/packages/canon/src/components/Heading/styles.css @@ -60,18 +60,6 @@ color: var(--canon-fg-secondary); } -.canon-Heading[data-color='danger'] { - color: var(--canon-fg-danger); -} - -.canon-Heading[data-color='warning'] { - color: var(--canon-fg-warning); -} - -.canon-Heading[data-color='success'] { - color: var(--canon-fg-success); -} - .canon-Heading[data-truncate] { overflow: hidden; text-overflow: ellipsis; diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts index 312682033d..7a94bdf2a4 100644 --- a/packages/canon/src/components/Heading/types.ts +++ b/packages/canon/src/components/Heading/types.ts @@ -36,15 +36,7 @@ export interface HeadingProps color?: | 'primary' | 'secondary' - | 'danger' - | 'warning' - | 'success' - | Partial< - Record< - Breakpoint, - 'primary' | 'secondary' | 'danger' | 'warning' | 'success' - > - >; + | Partial>; truncate?: boolean; className?: string; style?: React.CSSProperties;