Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions packages/core/src/components/divider/_divider.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
@import "../../common/variables";

$divider-margin: $pt-spacing !default;
$divider-text-spacing: $pt-spacing !default;
$divider-min-segment-width: $pt-spacing * 2 !default;

.#{$ns}-divider {
border-bottom: 1px solid $pt-divider-black;
Expand All @@ -19,4 +21,57 @@ $divider-margin: $pt-spacing !default;
&.#{$ns}-compact {
margin: 0;
}

// Divider with embedded text
&-with-text {
align-items: center;
border: none;
display: flex;
text-align: center;
white-space: nowrap;

&::before,
&::after {
border-bottom: 1px solid $pt-divider-black;
content: "";
flex: 1;

.#{$ns}-dark & {
border-color: $pt-dark-divider-white;
}
}

&::before {
margin-right: $divider-text-spacing;
}

&::after {
margin-left: $divider-text-spacing;
}
}

// Text alignment variants
&-text-left {
&::before {
flex: 0 0 $divider-min-segment-width;
}
}

&-text-right {
&::after {
flex: 0 0 $divider-min-segment-width;
}
}

// Text content wrapper
&-text-content {
align-items: center;
color: $pt-text-color-muted;
display: inline-flex;
font-size: $pt-font-size-small;

.#{$ns}-dark & {
color: $pt-dark-text-color-muted;
}
}
}
8 changes: 8 additions & 0 deletions packages/core/src/components/divider/divider.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ Use **Divider** to separate blocks of content within a page or container. By def

@reactCodeExample DividerBasicExample

@## With text

You can embed text or other content within a divider by passing children. This is useful for section headers or labels.

The text alignment can be controlled with the `textAlignment` prop, which accepts `"left"`, `"center"` (default), or `"right"`.

@reactCodeExample DividerWithTextExample

@## Compact

The `compact` prop removes the margin from the divider, making it flush with adjacent content.
Expand Down
46 changes: 44 additions & 2 deletions packages/core/src/components/divider/divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import { Classes } from "../../common";
import { DISPLAYNAME_PREFIX, type Props } from "../../common/props";

export interface DividerProps extends Props, React.HTMLAttributes<HTMLElement> {
/**
* Content to embed within the divider, typically text or an icon.
* When provided, the divider will be split with the content in between.
*/
children?: React.ReactNode;

/**
* If true, makes the Divider flush with adjacent content.
*
Expand All @@ -34,6 +40,14 @@ export interface DividerProps extends Props, React.HTMLAttributes<HTMLElement> {
* @default "div"
*/
tagName?: keyof React.JSX.IntrinsicElements;

/**
* Alignment of content within the divider.
* Only applies when `children` is provided.
*
* @default "center"
*/
textAlignment?: "left" | "center" | "right";
}

// this component is simple enough that tests would be purely tautological.
Expand All @@ -44,8 +58,36 @@ export interface DividerProps extends Props, React.HTMLAttributes<HTMLElement> {
*
* @see https://blueprintjs.com/docs/#core/components/divider
*/
export const Divider: React.FC<DividerProps> = ({ className, compact = false, tagName = "div", ...htmlProps }) => {
const classes = classNames(Classes.DIVIDER, { [Classes.COMPACT]: compact }, className);
export const Divider: React.FC<DividerProps> = ({
children,
className,
compact = false,
tagName = "div",
textAlignment = "center",
...htmlProps
}) => {
const classes = classNames(
Classes.DIVIDER,
{
[Classes.COMPACT]: compact,
[`${Classes.DIVIDER}-with-text`]: children != null,
[`${Classes.DIVIDER}-text-${textAlignment}`]: children != null && textAlignment !== "center",
},
className,
);

if (children != null) {
return createElement(
tagName,
{
...htmlProps,
className: classes,
role: "separator",
},
<span className={`${Classes.DIVIDER}-text-content`}>{children}</span>,
);
}

return createElement(tagName, {
...htmlProps,
className: classes,
Expand Down
120 changes: 120 additions & 0 deletions packages/core/test/divider/dividerTests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
*
* 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 { render, screen } from "@testing-library/react";
import { expect } from "chai";

import { Classes, Divider } from "../../src";
import { hasClass } from "../utils";

describe("<Divider>", () => {
it("should render with default className", () => {
const { container } = render(<Divider />);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(divider).to.exist;
});

it("should support custom className", () => {
const { container } = render(<Divider className="custom-class" />);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, "custom-class")).to.be.true;
});

it("should support compact prop", () => {
const { container } = render(<Divider compact={true} />);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, Classes.COMPACT)).to.be.true;
});

it("should render with custom tagName", () => {
const { container } = render(<Divider tagName="hr" />);
const divider = container.querySelector("hr");

expect(divider).to.exist;
expect(hasClass(divider!, Classes.DIVIDER)).to.be.true;
});

describe("with text", () => {
it("should render text content", () => {
render(<Divider>Section Title</Divider>);
const textContent = screen.getByText("Section Title");

expect(textContent).to.exist;
});

it("should apply text-related classes", () => {
const { container } = render(<Divider>Section Title</Divider>);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, `${Classes.DIVIDER}-with-text`)).to.be.true;
});

it("should not add unnecessary class for default center alignment", () => {
const { container } = render(<Divider>Section Title</Divider>);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, `${Classes.DIVIDER}-text-center`)).to.be.false;
});

it("should support textAlignment prop", () => {
const { container } = render(<Divider textAlignment="left">Section Title</Divider>);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, `${Classes.DIVIDER}-text-left`)).to.be.true;
});

it("should support textAlignment=right", () => {
const { container } = render(<Divider textAlignment="right">Section Title</Divider>);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, `${Classes.DIVIDER}-text-right`)).to.be.true;
});

it("should have role=separator when text is present", () => {
const { container } = render(<Divider>Section Title</Divider>);
const divider = container.querySelector('[role="separator"]');

expect(divider).to.exist;
});

it("should wrap text content in text-content span", () => {
const { container } = render(<Divider>Section Title</Divider>);
const textWrapper = container.querySelector(`.${Classes.DIVIDER}-text-content`);

expect(textWrapper).to.exist;
expect(textWrapper?.textContent).to.equal("Section Title");
});
});

describe("without text", () => {
it("should not have text-related classes", () => {
const { container } = render(<Divider />);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(hasClass(divider!, `${Classes.DIVIDER}-with-text`)).to.be.false;
});

it("should not have role attribute", () => {
const { container } = render(<Divider />);
const divider = container.querySelector(`.${Classes.DIVIDER}`);

expect(divider?.hasAttribute("role")).to.be.false;
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Divider } from "@blueprintjs/core";

export default function DividerWithText() {
return (
<div>
<p>Content above the divider</p>
<Divider textAlignment="left">Left aligned</Divider>
<p>Middle content</p>
<Divider>Center aligned (default)</Divider>
<p>More content</p>
<Divider textAlignment="right">Right aligned</Divider>
<p>Content below the divider</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<p>Content above the divider</p>
<Divider textAlignment="left">Left aligned</Divider>
<p>Middle content</p>
<Divider>Center aligned (default)</Divider>
<p>More content</p>
<Divider textAlignment="right">Right aligned</Divider>
<p>Content below the divider</p>
15 changes: 15 additions & 0 deletions packages/docs-app/src/examples/core-examples/dividerExamples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import dividerBasicCode from "./divider/DividerBasic.tsx?raw";
import DividerVertical from "./divider/DividerVertical";
import dividerVerticalPreview from "./divider/DividerVertical.tsx.preview?raw";
import dividerVerticalCode from "./divider/DividerVertical.tsx?raw";
import DividerWithText from "./divider/DividerWithText";
import dividerWithTextPreview from "./divider/DividerWithText.tsx.preview?raw";
import dividerWithTextCode from "./divider/DividerWithText.tsx?raw";

export const DividerBasicExample: React.FC<ExampleProps> = props => {
return (
Expand All @@ -30,3 +33,15 @@ export const DividerVerticalExample: React.FC<ExampleProps> = props => {
</CodeExample>
);
};

export const DividerWithTextExample: React.FC<ExampleProps> = props => {
return (
<CodeExample
previewCode={dividerWithTextPreview}
sourceCode={dividerWithTextCode}
{...props}
>
<DividerWithText />
</CodeExample>
);
};