diff --git a/web/packages/common/src/components/DataView/StudioDataView.test.tsx b/web/packages/common/src/components/DataView/StudioDataView.test.tsx index add894b9fc..92840efe97 100644 --- a/web/packages/common/src/components/DataView/StudioDataView.test.tsx +++ b/web/packages/common/src/components/DataView/StudioDataView.test.tsx @@ -80,7 +80,9 @@ function renderCellContent(col: Record, flatRow: FlatRow): Reac } vi.mock('@nemo/common/src/components/DataView/internal', () => ({ - useInnerDataViewContext: () => ({ table: { getAllLeafColumns: () => [] } }), + useInnerDataViewContext: () => ({ + table: { getAllLeafColumns: () => [], getSelectedRowModel: () => ({ flatRows: [] }) }, + }), Toolbar: ({ children, slotBulkActions, diff --git a/web/packages/common/src/components/DataView/StudioDataViewToolbar.tsx b/web/packages/common/src/components/DataView/StudioDataViewToolbar.tsx index 57dc84ba4d..518d413b2f 100644 --- a/web/packages/common/src/components/DataView/StudioDataViewToolbar.tsx +++ b/web/packages/common/src/components/DataView/StudioDataViewToolbar.tsx @@ -40,8 +40,10 @@ export function StudioDataViewToolbar({ }: StudioDataViewToolbarProps) { const { table } = useInnerDataViewContext(); const hasFilterableColumns = table.getAllLeafColumns().some((col) => col.getCanFilter()); + const hasSelectedRows = table.getSelectedRowModel().flatRows.length > 0; + const hostsBulkActions = Boolean(renderBulkActions) && hasSelectedRows; - if (!searchField && !hasFilterableColumns) return null; + if (!searchField && !hasFilterableColumns && !hostsBulkActions) return null; return ( <> diff --git a/web/packages/common/src/components/RadioCard/RadioCard.stories.tsx b/web/packages/common/src/components/RadioCard/RadioCard.stories.tsx index f53d084892..75c9fa0b5c 100644 --- a/web/packages/common/src/components/RadioCard/RadioCard.stories.tsx +++ b/web/packages/common/src/components/RadioCard/RadioCard.stories.tsx @@ -10,7 +10,7 @@ * its affiliates is strictly prohibited. */ import { RadioCard } from '@nemo/common/src/components/RadioCard/index'; -import { RadioGroupRoot, Stack } from '@nvidia/foundations-react-core'; +import { Flex, RadioGroupRoot, Stack } from '@nvidia/foundations-react-core'; import type { Meta, StoryObj } from '@storybook/react'; import { Boxes } from 'lucide-react'; import { useState } from 'react'; @@ -108,6 +108,36 @@ export const LabelSideLeft: Story = { }, }; +export const HiddenIndicator: Story = { + render: function HiddenIndicatorStory() { + const [value, setValue] = useState('dataset'); + return ( + + + + + + + ); + }, +}; + export const RichDescription: Story = { render: function RichDescriptionStory() { const [value, setValue] = useState('option-1'); diff --git a/web/packages/common/src/components/RadioCard/index.test.tsx b/web/packages/common/src/components/RadioCard/index.test.tsx new file mode 100644 index 0000000000..5e9d696b8a --- /dev/null +++ b/web/packages/common/src/components/RadioCard/index.test.tsx @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { RadioCard } from '@nemo/common/src/components/RadioCard/index'; +import { RadioGroupRoot } from '@nvidia/foundations-react-core'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +const renderGroup = (onValueChange: () => void, showIndicator?: boolean) => { + render( + + + + + ); +}; + +describe('RadioCard', () => { + it('Renders a radio per card with its label and description by default', () => { + renderGroup(vi.fn()); + + expect(screen.getAllByRole('radio')).toHaveLength(2); + expect(screen.getByRole('radio', { name: 'Option A' })).toBeChecked(); + expect(screen.getByText('First')).toBeInTheDocument(); + }); + + it('Keeps the radio input queryable and operable when showIndicator is false', async () => { + const user = userEvent.setup(); + const onValueChange = vi.fn(); + renderGroup(onValueChange, false); + + const optionB = screen.getByRole('radio', { name: 'Option B' }); + expect(optionB).toBeInTheDocument(); + expect(optionB).not.toBeChecked(); + + await user.click(screen.getByText('Option B')); + + expect(onValueChange).toHaveBeenCalledWith('b'); + }); +}); diff --git a/web/packages/common/src/components/RadioCard/index.tsx b/web/packages/common/src/components/RadioCard/index.tsx index 096362cb8c..342745247d 100644 --- a/web/packages/common/src/components/RadioCard/index.tsx +++ b/web/packages/common/src/components/RadioCard/index.tsx @@ -27,6 +27,8 @@ export interface RadioCardProps extends Omit