Skip to content

Commit c9dfc7a

Browse files
authored
[Component Audit] Divider: keep numeric labels visible and named (#7572)
1 parent 3235ec9 commit c9dfc7a

8 files changed

Lines changed: 237 additions & 108 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@astryxdesign/core': patch
3+
---
4+
5+
[fix] `Divider` now keeps numeric zero labels inside the labelled separator and omits boolean or empty-string labels without leaving empty label spacing.
6+
7+
@cixzhang

‎apps/storybook/stories/Divider.stories.tsx‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,19 +109,15 @@ export const FullBleed: Story = {
109109
<Card>
110110
<VStack gap={3}>
111111
<Text type="label">Normal divider</Text>
112-
<Text type="body">
113-
The divider respects container padding.
114-
</Text>
112+
<Text type="body">The divider respects container padding.</Text>
115113
<Divider />
116114
<Text type="body">Content below the divider.</Text>
117115
</VStack>
118116
</Card>
119117
<Card>
120118
<VStack gap={3}>
121119
<Text type="label">Full bleed divider</Text>
122-
<Text type="body">
123-
The divider extends to container edges.
124-
</Text>
120+
<Text type="body">The divider extends to container edges.</Text>
125121
<Divider isFullBleed />
126122
<Text type="body">Content below the divider.</Text>
127123
</VStack>
@@ -187,6 +183,28 @@ export const InCard: Story = {
187183
),
188184
};
189185

186+
export const NumericLabel: Story = {
187+
render: () => (
188+
<Section variant="muted">
189+
<div {...stylex.props(styles.storyWrapper)}>
190+
<Card>
191+
<VStack gap={3}>
192+
<Text type="supporting">Horizontal numeric label</Text>
193+
<Divider label={0} />
194+
</VStack>
195+
</Card>
196+
<Card height={200}>
197+
<HStack gap={4} xstyle={styles.fullHeight}>
198+
<Text type="body">Start</Text>
199+
<Divider orientation="vertical" label={0} />
200+
<Text type="body">End</Text>
201+
</HStack>
202+
</Card>
203+
</div>
204+
</Section>
205+
),
206+
};
207+
190208
export const FullBleedVertical: Story = {
191209
render: () => (
192210
<Section variant="muted">

‎packages/core/src/Chat/ChatSystemMessage.spec.md‎

Lines changed: 36 additions & 37 deletions
Large diffs are not rendered by default.

‎packages/core/src/Chat/ChatSystemMessage.test.tsx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,16 @@ describe('ChatSystemMessage', () => {
152152
expect(root).toContainElement(screen.getByRole('separator'));
153153
});
154154

155-
it('records Divider numeric-zero output for the shared FR15 advisory', () => {
155+
it('keeps the numeric-zero label visible and names the shared divider', () => {
156156
render(
157157
<ChatSystemMessage variant="divider" data-testid="divider-zero">
158158
{0}
159159
</ChatSystemMessage>,
160160
);
161161
const root = screen.getByTestId('divider-zero');
162162
const separator = screen.getByRole('separator');
163-
expect(root).toHaveTextContent('00');
164-
expect(separator).not.toHaveAccessibleName();
165-
expect(separator).not.toHaveAttribute('aria-labelledby');
163+
expect(root).toHaveTextContent(/^0$/);
164+
expect(separator).toHaveAccessibleName('0');
165+
expect(separator).toHaveAttribute('aria-labelledby');
166166
});
167167
});

‎packages/core/src/Divider/Divider.doc.mjs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export const docs = {
4040
{
4141
name: 'label',
4242
type: 'ReactNode',
43-
description: 'Optional label centered on the divider.',
43+
description:
44+
'Optional label centered on the divider. Numeric zero is preserved; null, booleans, and empty strings render no label.',
4445
},
4546
{
4647
name: 'variant',
@@ -92,7 +93,8 @@ export const docsZh = {
9293
{
9394
name: 'label',
9495
type: 'ReactNode',
95-
description: '居中显示在分隔线上的可选标签。',
96+
description:
97+
'居中显示在分隔线上的可选标签。数字 0 会保留;null、布尔值和空字符串不渲染标签。',
9698
},
9799
{
98100
name: 'variant',
@@ -143,7 +145,7 @@ export const docsDense = {
143145
},
144146
propDescriptions: {
145147
orientation: 'divider orientation',
146-
label: 'optional centered label on divider',
148+
label: 'optional centered label; preserves 0; null/boolean/"" omit it',
147149
variant: 'visual weight of divider line',
148150
isFullBleed: 'extend to container edges w/ negative margins',
149151
xstyle: 'StyleX styles for layout; must be stylex.create() value',

‎packages/core/src/Divider/Divider.spec.md‎

Lines changed: 140 additions & 54 deletions
Large diffs are not rendered by default.

‎packages/core/src/Divider/Divider.test.tsx‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,21 @@ describe('Divider', () => {
128128
expect(divider).not.toHaveAttribute('aria-labelledby');
129129
});
130130

131+
it.each([false, true, ''])('omits a label wrapper for %j', label => {
132+
render(<Divider label={label} data-testid="divider" />);
133+
const divider = screen.getByTestId('divider');
134+
expect(divider).not.toHaveAttribute('aria-labelledby');
135+
expect(divider.children).toHaveLength(1);
136+
});
137+
138+
it('preserves a numeric zero label and uses it as the accessible name', () => {
139+
render(<Divider label={0} data-testid="divider" />);
140+
const divider = screen.getByTestId('divider');
141+
expect(screen.getByText('0')).toBeInTheDocument();
142+
expect(divider).toHaveAccessibleName('0');
143+
expect(divider.children).toHaveLength(3);
144+
});
145+
131146
it('renders astryx-* class names for theme targeting', () => {
132147
render(
133148
<Divider variant="strong" orientation="vertical" data-testid="divider" />,

‎packages/core/src/Divider/Divider.tsx‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
typeScaleVars,
2626
borderVars,
2727
} from '../theme/tokens.stylex';
28-
import {mergeProps} from '../utils';
28+
import {isRenderable, mergeProps} from '../utils';
2929
import {themeProps} from '../utils/themeProps';
3030
import type {DividerVariantMap} from './index';
3131

@@ -45,6 +45,7 @@ export interface DividerProps extends BaseProps<HTMLDivElement> {
4545

4646
/**
4747
* Optional label to display centered on the divider.
48+
* Numeric zero is preserved; null, booleans, and empty strings render no label.
4849
* Rendered with small, secondary text styling.
4950
*/
5051
label?: ReactNode;
@@ -153,14 +154,15 @@ export function Divider({
153154
...props
154155
}: DividerProps) {
155156
const isHorizontal = orientation === 'horizontal';
157+
const hasLabel = isRenderable(label);
156158
const labelId = useId();
157159

158160
// A separator does not derive its accessible name from its content, so the
159161
// rendered label must be referenced explicitly via aria-labelledby to be
160162
// exposed as the separator's name (WCAG 1.3.1). An explicit aria-label or
161163
// aria-labelledby from the consumer takes precedence.
162164
const resolvedLabelledBy =
163-
ariaLabelledBy ?? (label && ariaLabel == null ? labelId : undefined);
165+
ariaLabelledBy ?? (hasLabel && ariaLabel == null ? labelId : undefined);
164166

165167
return (
166168
<div
@@ -189,7 +191,7 @@ export function Divider({
189191
lineStyles[variant],
190192
)}
191193
/>
192-
{label && (
194+
{hasLabel && (
193195
<div
194196
id={labelId}
195197
{...stylex.props(
@@ -199,7 +201,7 @@ export function Divider({
199201
{label}
200202
</div>
201203
)}
202-
{label && (
204+
{hasLabel && (
203205
<div
204206
{...stylex.props(
205207
isHorizontal ? lineStyles.horizontalLine : lineStyles.verticalLine,

0 commit comments

Comments
 (0)