Merge pull request #29989 from backstage/canon-text-userender

Canon - Improve the way we treat custom render on Text and Heading
This commit is contained in:
Patrik Oldsberg
2025-05-19 20:01:08 +02:00
committed by GitHub
12 changed files with 121 additions and 63 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': minor
---
We are modifying the way we treat custom render using 'useRender()' under the hood from BaseUI.
+8
View File
@@ -524,6 +524,14 @@
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-truncate] {
text-overflow: ellipsis;
white-space: nowrap;
+8
View File
@@ -36,6 +36,14 @@
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-truncate] {
text-overflow: ellipsis;
white-space: nowrap;
+8
View File
@@ -9748,6 +9748,14 @@
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-truncate] {
text-overflow: ellipsis;
white-space: nowrap;
+11 -10
View File
@@ -615,18 +615,20 @@ export interface GridProps extends SpaceProps {
// @public (undocumented)
export const Heading: ForwardRefExoticComponent<
HeadingProps & RefAttributes<HTMLHeadingElement>
Omit<HeadingProps, 'ref'> & RefAttributes<HTMLHeadingElement>
>;
// @public (undocumented)
export interface HeadingProps {
// (undocumented)
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
// (undocumented)
children: React.ReactNode;
export interface HeadingProps
extends Omit<useRender.ComponentProps<'h1'>, 'color'> {
// (undocumented)
className?: string;
// (undocumented)
color?:
| 'primary'
| 'secondary'
| Partial<Record<Breakpoint, 'primary' | 'secondary'>>;
// (undocumented)
style?: React.CSSProperties;
// (undocumented)
truncate?: boolean;
@@ -1260,7 +1262,7 @@ export interface TabsRootWithoutOrientation
// @public (undocumented)
const Text_2: ForwardRefExoticComponent<
TextProps & RefAttributes<HTMLParagraphElement>
Omit<TextProps, 'ref'> & RefAttributes<HTMLParagraphElement>
>;
export { Text_2 as Text };
@@ -1283,9 +1285,8 @@ export interface TextFieldProps
}
// @public (undocumented)
export interface TextProps {
// (undocumented)
children: ReactNode;
export interface TextProps
extends Omit<useRender.ComponentProps<'p'>, 'color'> {
// (undocumented)
className?: string;
// (undocumented)
@@ -50,6 +50,18 @@ export const AllVariants: Story = {
),
};
export const AllColors: Story = {
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Heading color="primary" {...args} />
<Heading color="secondary" {...args} />
</Flex>
),
};
export const Truncate: Story = {
args: {
...Title1.args,
@@ -67,13 +79,6 @@ export const Responsive: Story = {
},
};
export const CustomTag: Story = {
args: {
variant: 'title5',
as: 'h2',
},
};
export const WrappedInLink: Story = {
args: {
...Default.args,
@@ -87,6 +92,13 @@ export const WrappedInLink: Story = {
],
};
export const CustomRender: Story = {
args: {
...Default.args,
render: <h4 />,
},
};
export const Playground: Story = {
render: () => (
<Flex direction="column" gap="4">
@@ -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<HTMLHeadingElement, HeadingProps>(
(props, ref) => {
const {
children,
variant = 'title1',
as = 'h1',
color = 'primary',
truncate,
className,
render = <h1 />,
...restProps
} = props;
// Get the responsive value for the variant
const responsiveVariant = useResponsiveValue(variant);
const responsiveColor = useResponsiveValue(color);
const internalRef = useRef<HTMLElement | null>(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 (
<Component
ref={ref}
className={clsx('canon-Heading', className)}
data-variant={responsiveVariant}
data-truncate={truncate}
{...restProps}
>
{children}
</Component>
);
return renderElement();
},
);
@@ -52,6 +52,14 @@
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-truncate] {
overflow: hidden;
text-overflow: ellipsis;
@@ -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<useRender.ComponentProps<'h1'>, 'color'> {
variant?:
| 'display'
| 'title1'
@@ -32,7 +33,10 @@ export interface HeadingProps {
'display' | 'title1' | 'title2' | 'title3' | 'title4' | 'title5'
>
>;
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
color?:
| 'primary'
| 'secondary'
| Partial<Record<Breakpoint, 'primary' | 'secondary'>>;
truncate?: boolean;
className?: string;
style?: React.CSSProperties;
@@ -108,6 +108,13 @@ export const WrappedInLink: Story = {
],
};
export const CustomRender: Story = {
args: {
...Default.args,
render: <span />,
},
};
export const Playground: Story = {
render: () => (
<Flex gap="4" direction="column">
+18 -17
View File
@@ -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<HTMLParagraphElement, TextProps>(
(props, ref) => {
const {
children,
variant = 'body',
weight = 'regular',
color = 'primary',
style,
className,
truncate,
render = <p />,
...restProps
} = props;
@@ -38,21 +38,22 @@ export const Text = forwardRef<HTMLParagraphElement, TextProps>(
const responsiveVariant = useResponsiveValue(variant);
const responsiveWeight = useResponsiveValue(weight);
const responsiveColor = useResponsiveValue(color);
const internalRef = useRef<HTMLElement | null>(null);
return (
<p
ref={ref}
className={clsx('canon-Text', className)}
data-variant={responsiveVariant}
data-weight={responsiveWeight}
data-color={responsiveColor}
data-truncate={truncate}
style={style}
{...restProps}
>
{children}
</p>
);
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();
},
);
+4 -3
View File
@@ -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<useRender.ComponentProps<'p'>, 'color'> {
variant?:
| 'subtitle'
| 'body'