diff --git a/.changeset/witty-weeks-boil.md b/.changeset/witty-weeks-boil.md
new file mode 100644
index 0000000000..0e49315962
--- /dev/null
+++ b/.changeset/witty-weeks-boil.md
@@ -0,0 +1,5 @@
+---
+'@backstage/canon': patch
+---
+
+Add new truncate prop to Text and Heading components in Canon.
diff --git a/canon-docs/src/app/(docs)/components/heading/page.mdx b/canon-docs/src/app/(docs)/components/heading/page.mdx
index 10fd60bef1..e09bae9945 100644
--- a/canon-docs/src/app/(docs)/components/heading/page.mdx
+++ b/canon-docs/src/app/(docs)/components/heading/page.mdx
@@ -62,6 +62,22 @@ appearance of the heading.
`}
/>
+### Truncate
+
+The `Heading` component has a `truncate` prop that can be used to truncate the
+heading.
+
+}
+ code={`
+ A man looks at a painting in a museum and says, “Brothers and sisters I
+ have none, but that man's father is my father's son.” Who is
+ in the painting?
+ `}
+/>
+
### Responsive
You can also use the `variant` prop to change the appearance of the text based
diff --git a/canon-docs/src/app/(docs)/components/heading/props.ts b/canon-docs/src/app/(docs)/components/heading/props.ts
index a3dd3288ff..6d62d2dee1 100644
--- a/canon-docs/src/app/(docs)/components/heading/props.ts
+++ b/canon-docs/src/app/(docs)/components/heading/props.ts
@@ -12,6 +12,10 @@ export const headingPropDefs: Record = {
values: ['ReactNode'],
responsive: false,
},
+ truncate: {
+ type: 'boolean',
+ default: 'false',
+ },
...classNamePropDefs,
...stylePropDefs,
};
diff --git a/canon-docs/src/app/(docs)/components/text/page.mdx b/canon-docs/src/app/(docs)/components/text/page.mdx
index 70d5de5504..85a783f4f3 100644
--- a/canon-docs/src/app/(docs)/components/text/page.mdx
+++ b/canon-docs/src/app/(docs)/components/text/page.mdx
@@ -102,6 +102,21 @@ appearance of the text.
`}
/>
+### Truncate
+
+The `Text` component has a `truncate` prop that can be used to truncate the
+text.
+
+}
+ code={`
+ A man looks at a painting in a museum and says, “Brothers and sisters I
+ have none, but that man's father is my father's son.” Who is
+ in the painting?
+ `}
+/>
+
### Responsive
You can also use the `variant` prop to change the appearance of the text based
diff --git a/canon-docs/src/app/(docs)/components/text/props.ts b/canon-docs/src/app/(docs)/components/text/props.ts
index 53413dc9e7..76c229af58 100644
--- a/canon-docs/src/app/(docs)/components/text/props.ts
+++ b/canon-docs/src/app/(docs)/components/text/props.ts
@@ -16,6 +16,10 @@ export const textPropDefs: Record = {
values: ['regular', 'bold'],
responsive: true,
},
+ truncate: {
+ type: 'boolean',
+ default: 'false',
+ },
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css
index 3ef8175c46..23f1b69728 100644
--- a/packages/canon/css/components.css
+++ b/packages/canon/css/components.css
@@ -403,6 +403,12 @@
color: var(--canon-fg-success);
}
+.canon-Text[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
.canon-Heading {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
@@ -441,6 +447,12 @@
font-weight: var(--canon-font-weight-bold);
}
+.canon-Heading[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
.canon-IconButton {
user-select: none;
font-family: var(--canon-font-regular);
diff --git a/packages/canon/css/heading.css b/packages/canon/css/heading.css
index f1bea873b4..53e8d8f25a 100644
--- a/packages/canon/css/heading.css
+++ b/packages/canon/css/heading.css
@@ -35,3 +35,9 @@
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
+
+.canon-Heading[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css
index 07bac6155c..c441260aaa 100644
--- a/packages/canon/css/styles.css
+++ b/packages/canon/css/styles.css
@@ -9627,6 +9627,12 @@
color: var(--canon-fg-success);
}
+.canon-Text[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
.canon-Heading {
font-family: var(--canon-font-regular);
color: var(--canon-fg-primary);
@@ -9665,6 +9671,12 @@
font-weight: var(--canon-font-weight-bold);
}
+.canon-Heading[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
.canon-IconButton {
user-select: none;
font-family: var(--canon-font-regular);
diff --git a/packages/canon/css/text.css b/packages/canon/css/text.css
index 523574f779..c4f9671816 100644
--- a/packages/canon/css/text.css
+++ b/packages/canon/css/text.css
@@ -51,3 +51,9 @@
.canon-Text[data-color="success"] {
color: var(--canon-fg-success);
}
+
+.canon-Text[data-truncate] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md
index 87d6e11f6e..ff8c36e5ec 100644
--- a/packages/canon/report.api.md
+++ b/packages/canon/report.api.md
@@ -628,6 +628,8 @@ export interface HeadingProps {
// (undocumented)
style?: React.CSSProperties;
// (undocumented)
+ truncate?: boolean;
+ // (undocumented)
variant?:
| 'display'
| 'title1'
@@ -1275,6 +1277,8 @@ export interface TextProps {
// (undocumented)
style?: CSSProperties;
// (undocumented)
+ truncate?: boolean;
+ // (undocumented)
variant?:
| 'subtitle'
| 'body'
diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx
index 1fbc882d5d..59ac1aab91 100644
--- a/packages/canon/src/components/Heading/Heading.stories.tsx
+++ b/packages/canon/src/components/Heading/Heading.stories.tsx
@@ -50,6 +50,14 @@ export const AllVariants: Story = {
),
};
+export const Truncate: Story = {
+ args: {
+ ...Title1.args,
+ truncate: true,
+ style: { maxWidth: '400px' },
+ },
+};
+
export const Responsive: Story = {
args: {
variant: {
diff --git a/packages/canon/src/components/Heading/Heading.tsx b/packages/canon/src/components/Heading/Heading.tsx
index 994b1de06e..b7f99cd102 100644
--- a/packages/canon/src/components/Heading/Heading.tsx
+++ b/packages/canon/src/components/Heading/Heading.tsx
@@ -27,6 +27,7 @@ export const Heading = forwardRef(
children,
variant = 'title1',
as = 'h1',
+ truncate,
className,
...restProps
} = props;
@@ -47,6 +48,7 @@ export const Heading = forwardRef(
ref={ref}
className={clsx('canon-Heading', className)}
data-variant={responsiveVariant}
+ data-truncate={truncate}
{...restProps}
>
{children}
diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css
index c85bbca135..f0f4561400 100644
--- a/packages/canon/src/components/Heading/styles.css
+++ b/packages/canon/src/components/Heading/styles.css
@@ -51,3 +51,9 @@
font-size: var(--canon-font-size-5);
font-weight: var(--canon-font-weight-bold);
}
+
+.canon-Heading[data-truncate] {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts
index 6250e8d2eb..b87f16eb34 100644
--- a/packages/canon/src/components/Heading/types.ts
+++ b/packages/canon/src/components/Heading/types.ts
@@ -33,6 +33,7 @@ export interface HeadingProps {
>
>;
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
+ 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 0b0ffd2712..ba1155daed 100644
--- a/packages/canon/src/components/Text/Text.stories.tsx
+++ b/packages/canon/src/components/Text/Text.stories.tsx
@@ -78,6 +78,13 @@ export const AllColors: Story = {
),
};
+export const Truncate: Story = {
+ args: {
+ ...Default.args,
+ truncate: true,
+ },
+};
+
export const Responsive: Story = {
args: {
...Default.args,
diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx
index 76f5adbb43..90cc9bb2ac 100644
--- a/packages/canon/src/components/Text/Text.tsx
+++ b/packages/canon/src/components/Text/Text.tsx
@@ -30,6 +30,7 @@ export const Text = forwardRef(
color = 'primary',
style,
className,
+ truncate,
...restProps
} = props;
@@ -45,6 +46,7 @@ export const Text = forwardRef(
data-variant={responsiveVariant}
data-weight={responsiveWeight}
data-color={responsiveColor}
+ data-truncate={truncate}
style={style}
{...restProps}
>
diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css
index 9f0ed703d9..c0cd781007 100644
--- a/packages/canon/src/components/Text/styles.css
+++ b/packages/canon/src/components/Text/styles.css
@@ -67,3 +67,9 @@
.canon-Text[data-color='success'] {
color: var(--canon-fg-success);
}
+
+.canon-Text[data-truncate] {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts
index d4f1615694..7adafd73d7 100644
--- a/packages/canon/src/components/Text/types.ts
+++ b/packages/canon/src/components/Text/types.ts
@@ -39,6 +39,7 @@ export interface TextProps {
'primary' | 'secondary' | 'danger' | 'warning' | 'success'
>
>;
+ truncate?: boolean;
className?: string;
style?: CSSProperties;
}