diff --git a/apps/www/src/components/dataview-demo.tsx b/apps/www/src/components/dataview-demo.tsx index c70134c19..a3d40c80a 100644 --- a/apps/www/src/components/dataview-demo.tsx +++ b/apps/www/src/components/dataview-demo.tsx @@ -492,7 +492,7 @@ export function DataViewVirtualizedGroupingDemo() { // --------------------------------------------------------------------------- export function DataViewLoadingDemo() { - const [isLoading, setIsLoading] = useState(true); + const [loading, setLoading] = useState(true); return ( @@ -500,20 +500,20 @@ export function DataViewLoadingDemo() { size='small' variant='outline' color='neutral' - onClick={() => setIsLoading(v => !v)} + onClick={() => setLoading(v => !v)} > - {isLoading ? 'Stop loading' : 'Show skeletons'} + {loading ? 'Stop loading' : 'Show skeletons'} - Skeleton rows render while `isLoading` is true. + Skeleton rows render while `loading` is true.
diff --git a/apps/www/src/components/demo/demo-playground.tsx b/apps/www/src/components/demo/demo-playground.tsx index d501e6329..e26d6fac6 100644 --- a/apps/www/src/components/demo/demo-playground.tsx +++ b/apps/www/src/components/demo/demo-playground.tsx @@ -109,8 +109,8 @@ export default function DemoPlayground({ diff --git a/apps/www/src/content/docs/(overview)/upgrading.mdx b/apps/www/src/content/docs/(overview)/upgrading.mdx index 50864fe5e..5f67a346e 100644 --- a/apps/www/src/content/docs/(overview)/upgrading.mdx +++ b/apps/www/src/content/docs/(overview)/upgrading.mdx @@ -7,6 +7,47 @@ One section per release, newest first, with only the changes that need action from you. The full record of every release, features and fixes included, is on [GitHub releases](https://github.com/raystack/apsara/releases). +## Unreleased: boolean props use one naming rule + +A boolean prop that removes a part which shows by default is `hide`. A +prop that turns off a behavior which is on by default is `disable`. +Each flipped prop defaults to `false`, so a component looks and behaves the +same when you do not pass it. TypeScript reports each old name. + +| Component | Before | After | +|---|---|---| +| `Dialog.Content`, `Drawer.Content` | `showCloseButton={false}` | `hideCloseButton` | +| `Dialog.Content`, `AlertDialog.Content` | `showNestedAnimation={false}` | `disableNestedAnimation` | +| `Editor` toolbar controls | `tooltip={false}` | `hideTooltip` | +| `DatePicker`, `RangePicker` | `showCalendarIcon={false}` | `hideCalendarIcon` | +| `CalendarPreview.Body` | `showIcon` | `showCalendarIcon` | +| `DataView.List`, `DataView.Timeline` | `showGroupHeaders={false}` | `hideGroupHeaders` | +| `DataView.Timeline` | `showGridlines={false}` | `hideGridlines` | +| `DataView.Timeline` | `showCursorLine={false}` | `hideCursorLine` | +| `Skeleton` | `enableAnimation={false}` | `disableAnimation` | +| `DataView` | `isLoading` | `loading` | +| `Calendar` | `loadingData` | `loading` | +| `CopyButton` | `resetIcon={false}` | `resetTimeout={Infinity}` | + +```tsx +// Before + + + +// After + + +``` + +`useDataView().isLoading` keeps its name. + +### A loading `Button` is disabled + +`Button` with `loading` ignores clicks and sets `aria-disabled="true"`, as +`IconButton` does. The button stays focusable, and it keeps its loading style. +If your code let a user click a loading button, for example to cancel, render +a separate cancel button. + ## Unreleased: Sidebar and Drawer use one `side` `Sidebar` names its edge with `side`, as other components do. `Drawer` takes diff --git a/apps/www/src/content/docs/components/alert-dialog/props.ts b/apps/www/src/content/docs/components/alert-dialog/props.ts index 9c81028dc..bb2573dc5 100644 --- a/apps/www/src/content/docs/components/alert-dialog/props.ts +++ b/apps/www/src/content/docs/components/alert-dialog/props.ts @@ -8,10 +8,10 @@ export interface AlertDialogProps { export interface AlertDialogContentProps { /** - * Toggle nested dialog animation (scaling and translation) - * @default true + * Turns off the scale and offset of a dialog that has a nested dialog open. + * @default false */ - showNestedAnimation?: boolean; + disableNestedAnimation?: boolean; /** Overlay configuration including blur, className, and style */ overlay?: { diff --git a/apps/www/src/content/docs/components/button/index.mdx b/apps/www/src/content/docs/components/button/index.mdx index 9728d7560..3d96e7f52 100644 --- a/apps/www/src/content/docs/components/button/index.mdx +++ b/apps/www/src/content/docs/components/button/index.mdx @@ -98,8 +98,9 @@ Every rendered part carries a stable `data-slot` attribute for [styling and test - Uses the native ` + ); + const button = screen.getByRole('button'); + expect(button).not.toBeDisabled(); + expect(button).toHaveAttribute('aria-disabled', 'true'); + button.focus(); + expect(button).toHaveFocus(); + fireEvent.click(button); + expect(handleClick).not.toHaveBeenCalled(); + }); + it('handles focus events', () => { const handleFocus = vi.fn(); render(); diff --git a/packages/react/components/button/button.tsx b/packages/react/components/button/button.tsx index 778fb8be2..cc09e3d96 100644 --- a/packages/react/components/button/button.tsx +++ b/packages/react/components/button/button.tsx @@ -157,7 +157,7 @@ export const Button = ({ button({ variant, size, color, radius, disabled, loading, className }), isLoaderOnly && getLoaderOnlyClass(size) )} - disabled={disabled} + disabled={disabled || loading} render={render} nativeButton={!render} focusableWhenDisabled={loading} diff --git a/packages/react/components/calendar-preview/calendar-preview-body.tsx b/packages/react/components/calendar-preview/calendar-preview-body.tsx index 64454b373..610b0f8e6 100644 --- a/packages/react/components/calendar-preview/calendar-preview-body.tsx +++ b/packages/react/components/calendar-preview/calendar-preview-body.tsx @@ -13,13 +13,16 @@ import { CalendarPreviewSeparator } from './calendar-preview-separator'; export interface CalendarPreviewBodyProps extends useRender.ComponentProps<'div'> { label?: ReactNode; - /** @defaultValue false */ - showIcon?: boolean; + /** + * Shows the calendar icon in the date input. + * @default false + */ + showCalendarIcon?: boolean; } export function CalendarPreviewBody({ label, - showIcon = false, + showCalendarIcon = false, className, children, render, @@ -45,7 +48,9 @@ export function CalendarPreviewBody({ children: children ?? ( <> {label} - + {scale !== 'day' && } diff --git a/packages/react/components/calendar/__tests__/calendar.test.tsx b/packages/react/components/calendar/__tests__/calendar.test.tsx index f2c57f42e..bdb55c2ae 100644 --- a/packages/react/components/calendar/__tests__/calendar.test.tsx +++ b/packages/react/components/calendar/__tests__/calendar.test.tsx @@ -30,7 +30,7 @@ describe('Calendar', () => { expect(screen.getByLabelText('Next month')).toBeInTheDocument(); }); it('disables navigation when loading', () => { - render(); + render(); const prevButton = screen.getByLabelText('Previous month'); const nextButton = screen.getByLabelText('Next month'); diff --git a/packages/react/components/calendar/__tests__/date-picker.test.tsx b/packages/react/components/calendar/__tests__/date-picker.test.tsx index 4e3c74912..29c134c5e 100644 --- a/packages/react/components/calendar/__tests__/date-picker.test.tsx +++ b/packages/react/components/calendar/__tests__/date-picker.test.tsx @@ -40,7 +40,7 @@ describe('DatePicker', () => { * Two regressions on the picker's `calendarProps` type: * - Original type required `mode`/`selected`/`onSelect`, which the picker * overrides after the spread, so consumer values were silently ignored. - * - CalendarPropsExtended fields (tooltipMessages, dateInfo, loadingData, + * - CalendarPropsExtended fields (tooltipMessages, dateInfo, loading, * showTooltip) were unreachable because the type didn't include them. */ it('accepts CalendarPropsExtended fields via calendarProps without type cast', () => { @@ -53,7 +53,7 @@ describe('DatePicker', () => { endMonth: new Date(2030, 0, 1), tooltipMessages: { '15-06-2025': 'Sample tooltip' }, dateInfo: { '15-06-2025': 'i' }, - loadingData: false, + loading: false, showTooltip: true }} /> diff --git a/packages/react/components/calendar/__tests__/range-picker.test.tsx b/packages/react/components/calendar/__tests__/range-picker.test.tsx index 2c4884867..d1657a457 100644 --- a/packages/react/components/calendar/__tests__/range-picker.test.tsx +++ b/packages/react/components/calendar/__tests__/range-picker.test.tsx @@ -96,7 +96,7 @@ describe('RangePicker', () => { endMonth: new Date(2030, 0, 1), tooltipMessages: { '15-06-2025': 'Sample tooltip' }, dateInfo: { '15-06-2025': 'i' }, - loadingData: false, + loading: false, showTooltip: true }} /> diff --git a/packages/react/components/calendar/calendar.tsx b/packages/react/components/calendar/calendar.tsx index ad3817e9c..ca8c741db 100644 --- a/packages/react/components/calendar/calendar.tsx +++ b/packages/react/components/calendar/calendar.tsx @@ -30,7 +30,7 @@ export interface CalendarPropsExtended { * (consumer must apply `timeZone` themselves to stay consistent). */ dateInfo?: Record | ((date: Date) => ReactNode | null); - loadingData?: boolean; + loading?: boolean; timeZone?: string; } @@ -118,7 +118,7 @@ export const Calendar = function ({ showTooltip = false, tooltipMessages = {}, dateInfo = {}, - loadingData = false, + loading = false, timeZone, ...props }: CalendarProps) { @@ -130,8 +130,8 @@ export const Calendar = function ({ PreviousMonthButton: ({ children, ...props }) => ( ( ), DayButton: props => { @@ -177,12 +177,12 @@ export const Calendar = function ({ const hasDateInfo = Boolean(dateComponent); return ( - +