@astryxdesign/core 0.1.1 → 0.1.2-canary.2581eb4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/README.md +119 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
- package/dist/Banner/Banner.d.ts +7 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +9 -2
- package/dist/Button/Button.d.ts +12 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +51 -13
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +6 -1
- package/dist/Calendar/index.d.ts +1 -1
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +7 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +5 -4
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +7 -1
- package/dist/HoverCard/HoverCard.d.ts +2 -2
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +18 -6
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +7 -3
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +1 -1
- package/dist/Layer/anchorName.d.ts +20 -0
- package/dist/Layer/anchorName.d.ts.map +1 -0
- package/dist/Layer/anchorName.js +36 -0
- package/dist/Layer/useLayer.d.ts +19 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +15 -6
- package/dist/Link/Link.d.ts +1 -1
- package/dist/Link/Link.js +2 -2
- package/dist/Link/LinkProvider.d.ts +1 -0
- package/dist/Link/LinkProvider.d.ts.map +1 -1
- package/dist/Link/LinkProvider.js +1 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +13 -3
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +13 -0
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +31 -27
- package/dist/Slider/Slider.js +2 -2
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +27 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +30 -7
- package/dist/Table/TableRow.d.ts +6 -1
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +15 -7
- package/dist/Table/index.d.ts +3 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
- package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
- package/dist/Table/plugins/stickyColumns/index.js +3 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
- package/dist/Table/types.d.ts +95 -5
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +1 -1
- package/dist/Text/text.stylex.d.ts +2 -2
- package/dist/Text/text.stylex.js +2 -2
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +28 -3
- package/dist/ToggleButton/ToggleButton.d.ts +7 -9
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +34 -57
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +1 -1
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +1 -0
- package/dist/astryx.css +30 -1
- package/dist/astryx.umd.js +147 -0
- package/dist/astryx.umd.js.map +7 -0
- package/dist/theme/generateThemeRules.js +1 -1
- package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
- package/dist/theme/syntax/SyntaxTheme.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
- package/dist/theme/syntax/presets.d.ts +1 -1
- package/dist/theme/syntax/presets.d.ts.map +1 -1
- package/dist/theme/syntax/presets.js +1 -1
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.js +1 -1
- package/dist/theme/types.d.ts +1 -1
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/package.json +6 -2
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
- package/src/Banner/Banner.doc.mjs +8 -0
- package/src/Banner/Banner.test.tsx +16 -7
- package/src/Banner/Banner.tsx +9 -2
- package/src/Button/Button.doc.mjs +6 -0
- package/src/Button/Button.test.tsx +105 -11
- package/src/Button/Button.tsx +73 -5
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +21 -26
- package/src/Calendar/Calendar.tsx +21 -5
- package/src/Calendar/index.ts +1 -0
- package/src/Chat/ChatComposer.doc.mjs +3 -0
- package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
- package/src/CheckboxInput/CheckboxInput.doc.mjs +8 -0
- package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
- package/src/DateTimeInput/DateTimeInput.doc.mjs +11 -3
- package/src/DateTimeInput/DateTimeInput.test.tsx +23 -3
- package/src/DateTimeInput/DateTimeInput.tsx +13 -10
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/EmptyState/EmptyState.test.tsx +4 -2
- package/src/EmptyState/EmptyState.tsx +6 -2
- package/src/HoverCard/HoverCard.doc.mjs +3 -0
- package/src/HoverCard/HoverCard.test.tsx +189 -2
- package/src/HoverCard/HoverCard.tsx +20 -16
- package/src/HoverCard/useHoverCard.tsx +13 -10
- package/src/Icon/Icon.doc.mjs +8 -0
- package/src/Kbd/Kbd.tsx +2 -9
- package/src/Layer/anchorName.test.ts +146 -0
- package/src/Layer/anchorName.ts +46 -0
- package/src/Layer/useLayer.doc.mjs +7 -2
- package/src/Layer/useLayer.tsx +33 -9
- package/src/Layout/Layout.doc.mjs +1 -1
- package/src/Lightbox/Lightbox.doc.mjs +0 -2
- package/src/Link/Link.test.tsx +2 -2
- package/src/Link/Link.tsx +3 -3
- package/src/Link/LinkProvider.tsx +1 -0
- package/src/List/List.doc.mjs +7 -0
- package/src/Markdown/Markdown.doc.mjs +2 -0
- package/src/Markdown/Markdown.test.tsx +17 -26
- package/src/Markdown/Markdown.tsx +16 -6
- package/src/MobileNav/MobileNav.doc.mjs +8 -8
- package/src/MobileNav/MobileNav.tsx +13 -0
- package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
- package/src/OverflowList/OverflowList.doc.mjs +17 -0
- package/src/Pagination/Pagination.test.tsx +137 -13
- package/src/Pagination/Pagination.tsx +33 -28
- package/src/ProgressBar/ProgressBar.test.tsx +13 -3
- package/src/Resizable/Resizable.doc.mjs +1 -1
- package/src/Section/Section.doc.mjs +11 -0
- package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
- package/src/Selector/Selector.doc.mjs +4 -0
- package/src/Skeleton/Skeleton.doc.mjs +11 -1
- package/src/Slider/Slider.doc.mjs +4 -0
- package/src/Slider/Slider.test.tsx +3 -16
- package/src/Slider/Slider.tsx +1 -1
- package/src/TabList/TabList.doc.mjs +6 -0
- package/src/Table/BaseTable.tsx +51 -24
- package/src/Table/Table.doc.mjs +12 -0
- package/src/Table/Table.test.tsx +29 -30
- package/src/Table/Table.tsx +22 -1
- package/src/Table/TableRow.tsx +37 -7
- package/src/Table/index.ts +3 -0
- package/src/Table/plugins/stickyColumns/index.ts +4 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
- package/src/Table/types.ts +101 -4
- package/src/Table/useBaseTablePlugins.ts +1 -0
- package/src/Table/useTableColumnResize.doc.mjs +96 -0
- package/src/Table/useTableStickyColumns.doc.mjs +59 -0
- package/src/Text/Heading.doc.mjs +15 -2
- package/src/Text/Text.doc.mjs +7 -1
- package/src/Text/text.stylex.ts +2 -2
- package/src/Timestamp/Timestamp.doc.mjs +9 -0
- package/src/Timestamp/Timestamp.test.tsx +44 -18
- package/src/Timestamp/Timestamp.tsx +30 -10
- package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
- package/src/ToggleButton/ToggleButton.test.tsx +39 -18
- package/src/ToggleButton/ToggleButton.tsx +46 -83
- package/src/Tokenizer/Tokenizer.tsx +4 -1
- package/src/Toolbar/Toolbar.doc.mjs +65 -5
- package/src/Tooltip/Tooltip.test.tsx +13 -0
- package/src/Tooltip/useTooltip.tsx +2 -3
- package/src/hooks/useImageMode.test.ts +133 -0
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/generateThemeRules.test.ts +7 -8
- package/src/theme/generateThemeRules.ts +1 -1
- package/src/theme/syntax/SyntaxTheme.tsx +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
- package/src/theme/syntax/presets.ts +5 -2
- package/src/theme/syntax/tokens.ts +1 -1
- package/src/theme/types.ts +1 -1
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
|
@@ -103,8 +103,8 @@ export const docs = {
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
name: 'weekStartsOn',
|
|
106
|
-
type:
|
|
107
|
-
description: 'First day of week (0=Sunday).',
|
|
106
|
+
type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'",
|
|
107
|
+
description: 'First day of week. Accepts a number (0=Sunday) or a three-letter day name (e.g. "mon").',
|
|
108
108
|
default: '0',
|
|
109
109
|
},
|
|
110
110
|
],
|
|
@@ -147,7 +147,7 @@ export const docsZh = {
|
|
|
147
147
|
{name: 'hasOutsideDays', type: 'boolean', description: '显示相邻月份的日期。', default: 'true'},
|
|
148
148
|
{name: 'hasWeekNumbers', type: 'boolean', description: '显示 ISO 周数。', default: 'false'},
|
|
149
149
|
{name: 'hasVariableRowCount', type: 'boolean', description: '可变行数与固定 6 行网格。', default: 'false'},
|
|
150
|
-
{name: 'weekStartsOn', type:
|
|
150
|
+
{name: 'weekStartsOn', type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun'…'sat'", description: '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。', default: '0'},
|
|
151
151
|
],
|
|
152
152
|
theming: {
|
|
153
153
|
targets: [
|
|
@@ -200,6 +200,6 @@ export const docsDense = {
|
|
|
200
200
|
hasOutsideDays: 'show days from adjacent months',
|
|
201
201
|
hasWeekNumbers: 'show ISO week numbers',
|
|
202
202
|
hasVariableRowCount: 'variable vs fixed 6-row grid',
|
|
203
|
-
weekStartsOn: 'first day of week (0=Sunday)',
|
|
203
|
+
weekStartsOn: 'first day of week (0=Sunday, or name e.g. "mon")',
|
|
204
204
|
},
|
|
205
205
|
};
|
|
@@ -146,10 +146,7 @@ describe('Calendar', () => {
|
|
|
146
146
|
const handleFocusChange = vi.fn();
|
|
147
147
|
|
|
148
148
|
render(
|
|
149
|
-
<Calendar
|
|
150
|
-
focusDate="2026-01-01"
|
|
151
|
-
onFocusDateChange={handleFocusChange}
|
|
152
|
-
/>,
|
|
149
|
+
<Calendar focusDate="2026-01-01" onFocusDateChange={handleFocusChange} />,
|
|
153
150
|
);
|
|
154
151
|
|
|
155
152
|
const nextButton = screen.getByRole('button', {name: 'Next month'});
|
|
@@ -189,9 +186,7 @@ describe('Calendar', () => {
|
|
|
189
186
|
return day !== 0 && day !== 6;
|
|
190
187
|
};
|
|
191
188
|
|
|
192
|
-
render(
|
|
193
|
-
<Calendar focusDate="2026-01-01" dateConstraints={[isWeekday]} />,
|
|
194
|
-
);
|
|
189
|
+
render(<Calendar focusDate="2026-01-01" dateConstraints={[isWeekday]} />);
|
|
195
190
|
|
|
196
191
|
// January 4, 2026 is a Sunday - should be disabled
|
|
197
192
|
const sunday = getDayButton(4);
|
|
@@ -238,6 +233,21 @@ describe('Calendar', () => {
|
|
|
238
233
|
expect(dayNames[0]).toHaveTextContent('Mo');
|
|
239
234
|
});
|
|
240
235
|
|
|
236
|
+
it('accepts a three-letter day name for weekStartsOn', () => {
|
|
237
|
+
render(<Calendar weekStartsOn="mon" />);
|
|
238
|
+
|
|
239
|
+
// "mon" should behave exactly like the numeric 1 (Monday first).
|
|
240
|
+
const dayNames = screen.getAllByText(/^(Mo|Tu|We|Th|Fr|Sa|Su)$/);
|
|
241
|
+
expect(dayNames[0]).toHaveTextContent('Mo');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('treats weekStartsOn day names case-insensitively', () => {
|
|
245
|
+
render(<Calendar weekStartsOn={'WED' as 'wed'} />);
|
|
246
|
+
|
|
247
|
+
const dayNames = screen.getAllByText(/^(Mo|Tu|We|Th|Fr|Sa|Su)$/);
|
|
248
|
+
expect(dayNames[0]).toHaveTextContent('We');
|
|
249
|
+
});
|
|
250
|
+
|
|
241
251
|
// ─── Range Mode ──────────────────────────────────────────────
|
|
242
252
|
|
|
243
253
|
it('supports range selection mode', async () => {
|
|
@@ -245,11 +255,7 @@ describe('Calendar', () => {
|
|
|
245
255
|
const handleChange = vi.fn();
|
|
246
256
|
|
|
247
257
|
render(
|
|
248
|
-
<Calendar
|
|
249
|
-
mode="range"
|
|
250
|
-
onChange={handleChange}
|
|
251
|
-
focusDate="2026-01-01"
|
|
252
|
-
/>,
|
|
258
|
+
<Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
|
|
253
259
|
);
|
|
254
260
|
|
|
255
261
|
// Click start date
|
|
@@ -271,11 +277,7 @@ describe('Calendar', () => {
|
|
|
271
277
|
const handleChange = vi.fn();
|
|
272
278
|
|
|
273
279
|
render(
|
|
274
|
-
<Calendar
|
|
275
|
-
mode="range"
|
|
276
|
-
onChange={handleChange}
|
|
277
|
-
focusDate="2026-01-01"
|
|
278
|
-
/>,
|
|
280
|
+
<Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
|
|
279
281
|
);
|
|
280
282
|
|
|
281
283
|
// Click later date first
|
|
@@ -346,10 +348,7 @@ describe('Calendar', () => {
|
|
|
346
348
|
const handleFocusChange = vi.fn();
|
|
347
349
|
|
|
348
350
|
render(
|
|
349
|
-
<Calendar
|
|
350
|
-
focusDate="2026-01-01"
|
|
351
|
-
onFocusDateChange={handleFocusChange}
|
|
352
|
-
/>,
|
|
351
|
+
<Calendar focusDate="2026-01-01" onFocusDateChange={handleFocusChange} />,
|
|
353
352
|
);
|
|
354
353
|
|
|
355
354
|
// Focus Jan 28
|
|
@@ -414,11 +413,7 @@ describe('Calendar', () => {
|
|
|
414
413
|
const handleChange = vi.fn();
|
|
415
414
|
|
|
416
415
|
render(
|
|
417
|
-
<Calendar
|
|
418
|
-
mode="range"
|
|
419
|
-
onChange={handleChange}
|
|
420
|
-
focusDate="2026-01-01"
|
|
421
|
-
/>,
|
|
416
|
+
<Calendar mode="range" onChange={handleChange} focusDate="2026-01-01" />,
|
|
422
417
|
);
|
|
423
418
|
|
|
424
419
|
// Click start date to begin range selection
|
|
@@ -70,8 +70,19 @@ import {
|
|
|
70
70
|
// Types
|
|
71
71
|
// =============================================================================
|
|
72
72
|
|
|
73
|
-
export type {
|
|
74
|
-
|
|
73
|
+
export type {
|
|
74
|
+
ISODateString,
|
|
75
|
+
DayOfWeek,
|
|
76
|
+
DayOfWeekName,
|
|
77
|
+
DateRange,
|
|
78
|
+
} from '../utils/dateTypes';
|
|
79
|
+
import type {
|
|
80
|
+
ISODateString,
|
|
81
|
+
DayOfWeek,
|
|
82
|
+
DayOfWeekName,
|
|
83
|
+
DateRange,
|
|
84
|
+
} from '../utils/dateTypes';
|
|
85
|
+
import {normalizeDayOfWeek} from '../utils/dateTypes';
|
|
75
86
|
import {themeProps} from '../utils/themeProps';
|
|
76
87
|
|
|
77
88
|
/** Imperative handle for Calendar handleRef */
|
|
@@ -134,10 +145,11 @@ interface CalendarBaseProps extends Omit<
|
|
|
134
145
|
hasVariableRowCount?: boolean;
|
|
135
146
|
|
|
136
147
|
/**
|
|
137
|
-
* First day of week.
|
|
148
|
+
* First day of week. Accepts a number (0 = Sunday … 6 = Saturday) or a
|
|
149
|
+
* three-letter day name ('sun'–'sat', case-insensitive) for readability.
|
|
138
150
|
* Default: 0 (Sunday)
|
|
139
151
|
*/
|
|
140
|
-
weekStartsOn?: DayOfWeek;
|
|
152
|
+
weekStartsOn?: DayOfWeek | DayOfWeekName;
|
|
141
153
|
}
|
|
142
154
|
|
|
143
155
|
// ─── Mode-specific Props (discriminated union) ────────────────
|
|
@@ -200,13 +212,17 @@ export function Calendar({ref, ...props}: CalendarProps) {
|
|
|
200
212
|
hasOutsideDays = true,
|
|
201
213
|
hasWeekNumbers = false,
|
|
202
214
|
hasVariableRowCount = false,
|
|
203
|
-
weekStartsOn = 0,
|
|
215
|
+
weekStartsOn: weekStartsOnProp = 0,
|
|
204
216
|
xstyle,
|
|
205
217
|
className,
|
|
206
218
|
style,
|
|
207
219
|
...rest
|
|
208
220
|
} = props;
|
|
209
221
|
|
|
222
|
+
// Normalize `weekStartsOn` (number or three-letter day name) to a numeric
|
|
223
|
+
// DayOfWeek so all downstream date math keeps working with an index.
|
|
224
|
+
const weekStartsOn = normalizeDayOfWeek(weekStartsOnProp);
|
|
225
|
+
|
|
210
226
|
// Today's date (memoized)
|
|
211
227
|
const today = useMemo(() => plainDateToday(), []);
|
|
212
228
|
|
package/src/Calendar/index.ts
CHANGED
|
@@ -7,6 +7,9 @@ export const docs = {
|
|
|
7
7
|
subComponentOf: 'Chat',
|
|
8
8
|
displayName: 'Chat Composer',
|
|
9
9
|
description: 'Layout shell for a chat composer. Arranges named slots (drawer, header, input, footer, send) with page-radius container, hover/focus shadows, and concentric inner radius for child elements.',
|
|
10
|
+
playground: {
|
|
11
|
+
wrapper: {component: 'Stack', props: {width: 480}},
|
|
12
|
+
},
|
|
10
13
|
props: [
|
|
11
14
|
{
|
|
12
15
|
name: 'onSubmit',
|
|
@@ -8,6 +8,18 @@ export const docs = {
|
|
|
8
8
|
displayName: 'Chat Composer Drawer',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
10
|
description: "Collapsible drawer panel that sits above the chat input inside ChatComposer. Pass it to the composer's `drawer` slot to show attachments, context chips, or any supplementary content. When `count` is provided the drawer gains a collapse toggle: collapsed state shows a badge and label, expanded state shows all children.",
|
|
11
|
+
playground: {
|
|
12
|
+
wrapper: {component: 'Stack', props: {width: 480}},
|
|
13
|
+
defaults: {
|
|
14
|
+
count: 3,
|
|
15
|
+
label: 'Attachments',
|
|
16
|
+
children: [
|
|
17
|
+
{__element: 'Token', props: {label: 'design-spec.pdf'}},
|
|
18
|
+
{__element: 'Token', props: {label: 'api-schema.json'}},
|
|
19
|
+
{__element: 'Token', props: {label: 'screenshot.png'}},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
11
23
|
props: [
|
|
12
24
|
{
|
|
13
25
|
name: 'children',
|
|
@@ -63,6 +63,13 @@ export const docs = {
|
|
|
63
63
|
description: 'Whether the checkbox is disabled.',
|
|
64
64
|
default: 'false',
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
name: 'isReadOnly',
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
description:
|
|
70
|
+
'Whether the checkbox is read-only. Displays the current state at full opacity but prevents interaction. Unlike `isDisabled`, read-only checkboxes are not visually dimmed.',
|
|
71
|
+
default: 'false',
|
|
72
|
+
},
|
|
66
73
|
{
|
|
67
74
|
name: 'isOptional',
|
|
68
75
|
type: 'boolean',
|
|
@@ -156,6 +163,7 @@ export const docsZh = {
|
|
|
156
163
|
},
|
|
157
164
|
{name: 'isLoading', type: 'boolean', description: '复选框是否处于加载状态。显示旋转器并阻止交互。', default: 'false'},
|
|
158
165
|
{name: 'isDisabled', type: 'boolean', description: '复选框是否禁用。', default: 'false'},
|
|
166
|
+
{name: 'isReadOnly', type: 'boolean', description: '复选框是否为只读。以完整不透明度显示当前状态但阻止交互。与 isDisabled 不同,只读复选框不会变暗。', default: 'false'},
|
|
159
167
|
{name: 'isOptional', type: 'boolean', description: '字段是否可选。与 isRequired 互斥。', default: 'false'},
|
|
160
168
|
{name: 'isRequired', type: 'boolean', description: '复选框是否必填。与 isOptional 互斥。', default: 'false'},
|
|
161
169
|
{name: 'size', type: "'sm' | 'md'", description: '复选框尺寸。sm 用于紧凑布局,md 为默认。', default: "'md'"},
|
|
@@ -37,6 +37,21 @@ export const docs = {
|
|
|
37
37
|
container: true,
|
|
38
38
|
targets: [{className: 'astryx-clickable-card', visualProps: ['variant']}],
|
|
39
39
|
},
|
|
40
|
+
playground: {
|
|
41
|
+
defaults: {
|
|
42
|
+
label: 'View product details',
|
|
43
|
+
href: '#',
|
|
44
|
+
padding: 4,
|
|
45
|
+
children: {
|
|
46
|
+
__element: 'XDSVStack',
|
|
47
|
+
props: {gap: 1},
|
|
48
|
+
children: [
|
|
49
|
+
{__element: 'XDSHeading', props: {level: 3}, children: 'Wireless Headphones'},
|
|
50
|
+
{__element: 'XDSText', props: {type: 'body'}, children: 'Noise-cancelling over-ear headphones with 30-hour battery life.'},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
40
55
|
};
|
|
41
56
|
|
|
42
57
|
/** @type {import('../docs-types').TranslationDoc} */
|
|
@@ -108,9 +108,15 @@ export const docs = {
|
|
|
108
108
|
{
|
|
109
109
|
name: 'placeholder',
|
|
110
110
|
type: 'string',
|
|
111
|
-
description: 'Placeholder text shown when no
|
|
111
|
+
description: 'Placeholder text shown in the date portion when no date is selected.',
|
|
112
112
|
default: "'Select a date'",
|
|
113
113
|
},
|
|
114
|
+
{
|
|
115
|
+
name: 'timePlaceholder',
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Placeholder text shown in the time portion when no time is selected.',
|
|
118
|
+
default: "'Select a time'",
|
|
119
|
+
},
|
|
114
120
|
{
|
|
115
121
|
name: 'size',
|
|
116
122
|
type: "'sm' | 'md' | 'lg'",
|
|
@@ -202,7 +208,8 @@ export const docsZh = {
|
|
|
202
208
|
{name: 'hourFormat', type: "'12h' | '24h'", description: "控制显示格式。'12h' 显示 AM/PM;'24h' 使用 24 小时制。", default: "'12h'"},
|
|
203
209
|
{name: 'timeIncrement', type: 'number', description: '在时间输入中按箭头键时增减的分钟数。', default: '1'},
|
|
204
210
|
{name: 'hasClear', type: 'boolean', description: '当有值时显示清除按钮。', default: 'false'},
|
|
205
|
-
{name: 'placeholder', type: 'string', description: '
|
|
211
|
+
{name: 'placeholder', type: 'string', description: '日期部分未选择日期时显示的占位符文本。', default: "'Select a date'"},
|
|
212
|
+
{name: 'timePlaceholder', type: 'string', description: '时间部分未选择时间时显示的占位符文本。', default: "'Select a time'"},
|
|
206
213
|
{name: 'size', type: "'sm' | 'md' | 'lg'", description: '输入控件的尺寸。', default: "'md'"},
|
|
207
214
|
{name: 'status', type: 'InputStatus', description: '错误、警告或成功状态的状态指示对象,附带消息。'},
|
|
208
215
|
{name: 'labelTooltip', type: 'string', description: '通过标签末尾的信息图标显示的提示文本。'},
|
|
@@ -249,7 +256,8 @@ export const docsDense = {
|
|
|
249
256
|
hourFormat: "display format. '12h' shows AM/PM; '24h' uses 24-hour",
|
|
250
257
|
timeIncrement: 'minutes to add/subtract on arrow keys in time input',
|
|
251
258
|
hasClear: 'Shows clear button when datetime is set',
|
|
252
|
-
placeholder: 'placeholder
|
|
259
|
+
placeholder: 'date-portion placeholder when empty',
|
|
260
|
+
timePlaceholder: 'time-portion placeholder when empty',
|
|
253
261
|
size: 'input control size',
|
|
254
262
|
status: 'error/warning/success status w/ message',
|
|
255
263
|
labelTooltip: 'tooltip text via info icon at label end',
|
|
@@ -31,6 +31,28 @@ describe('DateTimeInput', () => {
|
|
|
31
31
|
expect(screen.getByPlaceholderText('Pick a date')).toBeInTheDocument();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it('defaults the time portion placeholder to "Select a time"', () => {
|
|
35
|
+
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
36
|
+
expect(screen.getByPlaceholderText('Select a time')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('applies a custom timePlaceholder to the time portion', () => {
|
|
40
|
+
render(
|
|
41
|
+
<DateTimeInput
|
|
42
|
+
label="Meeting"
|
|
43
|
+
onChange={() => {}}
|
|
44
|
+
placeholder="Pick a date"
|
|
45
|
+
timePlaceholder="Pick a time"
|
|
46
|
+
/>,
|
|
47
|
+
);
|
|
48
|
+
// Time portion uses the override; date portion keeps its own placeholder.
|
|
49
|
+
expect(screen.getByPlaceholderText('Pick a time')).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByPlaceholderText('Pick a date')).toBeInTheDocument();
|
|
51
|
+
expect(
|
|
52
|
+
screen.queryByPlaceholderText('Select a time'),
|
|
53
|
+
).not.toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
34
56
|
it('renders both date and time inputs', () => {
|
|
35
57
|
render(<DateTimeInput label="Meeting" onChange={() => {}} />);
|
|
36
58
|
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
@@ -90,9 +112,7 @@ describe('DateTimeInput', () => {
|
|
|
90
112
|
});
|
|
91
113
|
|
|
92
114
|
it('visually hides label when isLabelHidden is true', () => {
|
|
93
|
-
render(
|
|
94
|
-
<DateTimeInput label="Meeting" isLabelHidden onChange={() => {}} />,
|
|
95
|
-
);
|
|
115
|
+
render(<DateTimeInput label="Meeting" isLabelHidden onChange={() => {}} />);
|
|
96
116
|
const label = screen.getByText('Meeting');
|
|
97
117
|
expect(label).toBeInTheDocument();
|
|
98
118
|
expect(screen.getByLabelText('Meeting')).toBeInTheDocument();
|
|
@@ -48,11 +48,7 @@ import {
|
|
|
48
48
|
} from '../Field';
|
|
49
49
|
import {Icon} from '../Icon';
|
|
50
50
|
import {Spinner} from '../Spinner';
|
|
51
|
-
import {
|
|
52
|
-
Calendar,
|
|
53
|
-
type ISODateString,
|
|
54
|
-
type CalendarHandle,
|
|
55
|
-
} from '../Calendar';
|
|
51
|
+
import {Calendar, type ISODateString, type CalendarHandle} from '../Calendar';
|
|
56
52
|
import {useCalendarConstraints} from '../Calendar/hooks';
|
|
57
53
|
import {usePopover} from '../Popover';
|
|
58
54
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
@@ -278,11 +274,17 @@ export interface DateTimeInputProps extends Omit<
|
|
|
278
274
|
hasClear?: boolean;
|
|
279
275
|
|
|
280
276
|
/**
|
|
281
|
-
* Placeholder text shown when no date is selected.
|
|
277
|
+
* Placeholder text shown in the date portion when no date is selected.
|
|
282
278
|
* @default "Select a date"
|
|
283
279
|
*/
|
|
284
280
|
placeholder?: string;
|
|
285
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Placeholder text shown in the time portion when no time is selected.
|
|
284
|
+
* @default "Select a time"
|
|
285
|
+
*/
|
|
286
|
+
timePlaceholder?: string;
|
|
287
|
+
|
|
286
288
|
/**
|
|
287
289
|
* The size of the inputs.
|
|
288
290
|
* @default 'md'
|
|
@@ -385,6 +387,7 @@ export function DateTimeInput({
|
|
|
385
387
|
timeIncrement = 1,
|
|
386
388
|
hasClear = false,
|
|
387
389
|
placeholder = 'Select a date',
|
|
390
|
+
timePlaceholder = 'Select a time',
|
|
388
391
|
size: sizeProp,
|
|
389
392
|
status,
|
|
390
393
|
labelTooltip,
|
|
@@ -520,12 +523,12 @@ export function DateTimeInput({
|
|
|
520
523
|
return isTimeInRange(parsed, timeMin, timeMax);
|
|
521
524
|
}, [timePendingInput, hasSeconds, timeMin, timeMax]);
|
|
522
525
|
|
|
523
|
-
const
|
|
526
|
+
const resolvedTimePlaceholder = useMemo(() => {
|
|
524
527
|
if (isTimeFocused && !timeDisplayValue) {
|
|
525
528
|
return hourFormat === '12h' ? 'e.g., 2:30 PM' : 'e.g., 14:30';
|
|
526
529
|
}
|
|
527
|
-
return
|
|
528
|
-
}, [isTimeFocused, timeDisplayValue, hourFormat]);
|
|
530
|
+
return timePlaceholder;
|
|
531
|
+
}, [isTimeFocused, timeDisplayValue, hourFormat, timePlaceholder]);
|
|
529
532
|
|
|
530
533
|
// --- Unified change handler ---
|
|
531
534
|
const fireChange = useCallback(
|
|
@@ -893,7 +896,7 @@ export function DateTimeInput({
|
|
|
893
896
|
onFocus={handleTimeFocus}
|
|
894
897
|
onBlur={handleTimeBlur}
|
|
895
898
|
onKeyDown={handleTimeKeyDown}
|
|
896
|
-
placeholder={
|
|
899
|
+
placeholder={resolvedTimePlaceholder}
|
|
897
900
|
disabled={isEffectivelyDisabled}
|
|
898
901
|
aria-label="Time"
|
|
899
902
|
aria-required={isRequired === true ? 'true' : undefined}
|
|
@@ -40,13 +40,13 @@ export const docs = {
|
|
|
40
40
|
{
|
|
41
41
|
name: 'isOpen',
|
|
42
42
|
type: 'boolean',
|
|
43
|
-
description: 'Whether the dialog is open
|
|
43
|
+
description: 'Whether the dialog is open.',
|
|
44
44
|
required: true,
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: 'onOpenChange',
|
|
48
48
|
type: '(isOpen: boolean) => unknown',
|
|
49
|
-
description: 'Callback when dialog visibility changes
|
|
49
|
+
description: 'Callback when dialog visibility changes.',
|
|
50
50
|
required: true,
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -45,11 +45,13 @@ describe('EmptyState', () => {
|
|
|
45
45
|
/>,
|
|
46
46
|
);
|
|
47
47
|
expect(screen.getByText('Try adjusting your search.')).toBeInTheDocument();
|
|
48
|
+
// Description renders as <div> (never <p>) so block content composes safely.
|
|
49
|
+
expect(screen.getByText('Try adjusting your search.').tagName).toBe('DIV');
|
|
48
50
|
});
|
|
49
51
|
|
|
50
52
|
it('does not render description when not provided', () => {
|
|
51
|
-
|
|
52
|
-
expect(
|
|
53
|
+
render(<EmptyState title="No results" />);
|
|
54
|
+
expect(screen.queryByText('Try adjusting your search.')).toBeNull();
|
|
53
55
|
});
|
|
54
56
|
|
|
55
57
|
it('renders with icon', () => {
|
|
@@ -174,13 +174,17 @@ export function EmptyState({
|
|
|
174
174
|
title,
|
|
175
175
|
)}
|
|
176
176
|
{description != null && (
|
|
177
|
-
<p
|
|
177
|
+
// Rendered as <div> (not <p>): description accepts ReactNode and a
|
|
178
|
+
// <p> cannot legally contain block children, which causes hydration
|
|
179
|
+
// mismatches. The StyleX style sets margin: 0, so appearance is
|
|
180
|
+
// unchanged.
|
|
181
|
+
<div
|
|
178
182
|
{...stylex.props(
|
|
179
183
|
styles.description,
|
|
180
184
|
isCompact && styles.descriptionCompact,
|
|
181
185
|
)}>
|
|
182
186
|
{description}
|
|
183
|
-
</
|
|
187
|
+
</div>
|
|
184
188
|
)}
|
|
185
189
|
</div>
|
|
186
190
|
{actions != null && (
|
|
@@ -108,6 +108,7 @@ export const docs = {
|
|
|
108
108
|
{ guidance: false, description: 'Place critical actions or required information inside a hover card; users may miss content that only appears on hover.' },
|
|
109
109
|
{ guidance: false, description: 'Use a hover card when a simple Tooltip or Popover would suffice.' },
|
|
110
110
|
{ guidance: false, description: 'Use a HoverCard for content the user must interact with; it disappears when the cursor leaves.' },
|
|
111
|
+
{ guidance: false, description: 'Nest a HoverCard whose content has block elements directly inside phrasing-only contexts such as a <p>, <label>, or heading. The card renders inline, so block content there is invalid HTML the browser reparents. Wrap the surrounding text in a block element (e.g. a <div>) instead.' },
|
|
111
112
|
],
|
|
112
113
|
anatomy: [
|
|
113
114
|
{name: 'Trigger', required: true, description: 'The element that opens the hover card on hover or focus: a button, link, or inline text.'},
|
|
@@ -216,6 +217,7 @@ export const docsZh = {
|
|
|
216
217
|
{ guidance: false, description: 'Place critical actions or required information inside a hover card; users may miss content that only appears on hover.' },
|
|
217
218
|
{ guidance: false, description: 'Use a hover card when a simple Tooltip or Popover would suffice.' },
|
|
218
219
|
{ guidance: false, description: 'Use a HoverCard for content the user must interact with; it disappears when the cursor leaves.' },
|
|
220
|
+
{ guidance: false, description: 'Nest a HoverCard whose content has block elements directly inside phrasing-only contexts such as a <p>, <label>, or heading. The card renders inline, so block content there is invalid HTML the browser reparents. Wrap the surrounding text in a block element (e.g. a <div>) instead.' },
|
|
219
221
|
],
|
|
220
222
|
},
|
|
221
223
|
};
|
|
@@ -233,6 +235,7 @@ export const docsDense = {
|
|
|
233
235
|
{ guidance: false, description: 'Place critical actions or required information inside a hover card; users may miss content that only appears on hover.' },
|
|
234
236
|
{ guidance: false, description: 'Use a hover card when a simple Tooltip or Popover would suffice.' },
|
|
235
237
|
{ guidance: false, description: 'Use a HoverCard for content the user must interact with; it disappears when the cursor leaves.' },
|
|
238
|
+
{ guidance: false, description: 'Nest a block-content HoverCard directly inside phrasing-only contexts (<p>, <label>, heading); it renders inline so block content is invalid HTML there. Wrap surrounding text in a block element instead.' },
|
|
236
239
|
],
|
|
237
240
|
},
|
|
238
241
|
components: [
|