diff --git a/.github/playwright/impact-map.generated.json b/.github/playwright/impact-map.generated.json index 0561c1f3ad14..7829f6ec070f 100644 --- a/.github/playwright/impact-map.generated.json +++ b/.github/playwright/impact-map.generated.json @@ -803,6 +803,7 @@ "playwright/e2e/Features/DataQuality/Profiler.spec.ts", "playwright/e2e/Features/DataQuality/ProfilerIngestionForm.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseImportExportBasic.spec.ts", @@ -1711,6 +1712,7 @@ "playwright/e2e/Features/DataQuality/ProfilerSettingsAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", "playwright/e2e/Features/DataQuality/TableLevelTests.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseCrudAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", @@ -2024,6 +2026,7 @@ "playwright/e2e/Features/DataAssetRulesEnabled.spec.ts", "playwright/e2e/Features/DataQuality/ProfilerIngestionForm.spec.ts", "playwright/e2e/Features/DataQuality/TableLevelTests.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/ExploreQuickFilters.spec.ts", "playwright/e2e/Features/IngestionListNameSorting.spec.ts", "playwright/e2e/Features/LandingPageWidgets/LandingPagePlatformHealth.spec.ts", @@ -3074,6 +3077,7 @@ "playwright/e2e/Features/DataQuality/Profiler.spec.ts", "playwright/e2e/Features/DataQuality/ProfilerIngestionForm.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseImportExportBasic.spec.ts", @@ -3296,6 +3300,7 @@ "playwright/e2e/Features/AdvancedSearch.spec.ts", "playwright/e2e/Features/ContextCenterDashboard.spec.ts", "playwright/e2e/Features/DataQuality/IncidentManagerSqlQuery.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DomainFilterQueryFilter.spec.ts", "playwright/e2e/Features/LandingPageWidgets/LandingPageContextCenter.spec.ts", "playwright/e2e/Features/LandingPageWidgets/LandingPageCriticalAnnouncements.spec.ts", @@ -3486,6 +3491,7 @@ "playwright/e2e/Features/DataQuality/ProfilerSettingsAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", "playwright/e2e/Features/DataQuality/TableLevelTests.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseCrudAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", @@ -3935,7 +3941,6 @@ "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseIncidentPermissions.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseResultPermissions.spec.ts", - "playwright/e2e/Pages/AuditLogs.spec.ts", "playwright/e2e/Pages/DataContractsSemanticRules.spec.ts", "playwright/e2e/Pages/DataInsightSettings.spec.ts", "playwright/e2e/Pages/Entity.spec.ts", @@ -4102,6 +4107,7 @@ "playwright/e2e/Features/DataQuality/DataQualityListAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/DataQualityPermissions.spec.ts", "playwright/e2e/Features/DataQuality/IncidentManagerDateFilter.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseImportExportBasic.spec.ts", @@ -4835,6 +4841,7 @@ "playwright/e2e/Features/AIMode/ConnectionServiceDetails.spec.ts", "playwright/e2e/Features/Announcements/SystemAnnouncements.spec.ts", "playwright/e2e/Features/BulkEditEntity.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/OnlineUsers.spec.ts", "playwright/e2e/Features/TestSuitePipelineRedeploy.spec.ts", "playwright/e2e/Pages/AIProfile/ProfileMembers.spec.ts", @@ -5162,6 +5169,7 @@ "playwright/e2e/Features/DataQuality/Profiler.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", "playwright/e2e/Features/DataQuality/TableLevelTests.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseCrudAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", @@ -5270,6 +5278,7 @@ "playwright/e2e/Features/DataQuality/DataQuality.spec.ts", "playwright/e2e/Features/DataQuality/IncidentManagerDateFilter.spec.ts", "playwright/e2e/Features/DataQuality/IncidentManagerSqlQuery.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/EntitySuggestions.spec.ts", "playwright/e2e/Features/FailedTestCaseSampleData.spec.ts", @@ -6783,6 +6792,14 @@ "playwright/e2e/Features/DataQuality/ProfilerSettingsAfterReindex.spec.ts" ] }, + { + "sources": [ + "openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx" + ], + "specs": [ + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts" + ] + }, { "sources": [ "openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/RunSummaryTiles/RunSummaryTiles.tsx" @@ -11963,6 +11980,7 @@ "playwright/e2e/Features/DataQuality/ProfilerSettingsAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/RunTestCase.spec.ts", "playwright/e2e/Features/DataQuality/TableLevelTests.spec.ts", + "playwright/e2e/Features/DataQuality/TableTestCaseExportFilters.spec.ts", "playwright/e2e/Features/DataQuality/TableTestCasePagination.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseCrudAfterReindex.spec.ts", "playwright/e2e/Features/DataQuality/TestCaseDetailsPage.spec.ts", diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.test.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.test.tsx new file mode 100644 index 000000000000..2edf3aaaea99 --- /dev/null +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2026 Collate. + * 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 { describe, expect, it } from 'vitest'; +import { NavItemBase, type NavItemSize } from './nav-item'; + +const Icon = () => ; + +describe('NavItemBase', () => { + it('renders an item without an href as a button, not a link', () => { + render(Logout); + + expect(screen.getByRole('button', { name: 'Logout' })).toBeInTheDocument(); + expect(screen.queryByRole('link')).not.toBeInTheDocument(); + }); + + it.each<[NavItemSize, string]>([ + ['sm', 'tw:pl-9'], + ['md', 'tw:pl-10'], + ])( + 'indents a %s collapsible child to line up with its parent label', + (size, indent) => { + render( + + Child + + ); + + expect(screen.getByRole('link', { name: 'Child' })).toHaveClass(indent); + } + ); + + it.each<[NavItemSize, number]>([ + ['md', 0], + ['lg', 1], + ])('shows a %s collapsible child icon %i time(s)', (size, count) => { + render( + + Child + + ); + + expect(screen.queryAllByTestId('nav-icon')).toHaveLength(count); + }); + + it('fills the current item with the brand colour in the solid variant', () => { + render( + <> + + Current + + + Default + + + Resting + + + ); + + expect(screen.getByRole('link', { name: 'Resting' })).toHaveClass( + 'tw:bg-transparent' + ); + expect(screen.getByText('Resting')).toHaveClass('tw:text-tertiary'); + expect(screen.getByText('Current')).toHaveClass('tw:text-primary_on-brand'); + + expect(screen.getByRole('link', { name: 'Current' })).toHaveClass( + 'tw:bg-brand-solid' + ); + expect(screen.getByRole('link', { name: 'Default' })).toHaveClass( + 'tw:bg-brand-primary_alt' + ); + }); +}); diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.tsx index 21805ae12670..588889005916 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/app-navigation/base-components/nav-item.tsx @@ -14,26 +14,64 @@ const styles = sortCx({ rootSelected: 'tw:bg-brand-primary_alt tw:hover:bg-brand-primary_alt', }); -export type NavItemSize = 'sm' | 'md'; +export type NavItemVariant = 'default' | 'solid'; + +// `solid` fills the current item with the brand colour and opens a collapsible +// group under a filled header; hover tints rest items brand instead of gray. +const solidStyles = sortCx({ + // Rest items sit on the sidebar surface instead of painting white boxes. + root: 'tw:bg-transparent tw:hover:bg-brand-primary_alt tw:dark:hover:bg-brand-primary_alt', + rootSelected: + 'tw:bg-brand-solid tw:hover:bg-brand-solid tw:dark:bg-brand-solid tw:dark:hover:bg-brand-solid', + groupSelected: + 'tw:bg-brand-solid_hover tw:hover:bg-brand-solid_hover tw:dark:bg-brand-solid_hover tw:dark:hover:bg-brand-solid_hover', + groupOpen: + 'tw:in-open:rounded-b-none tw:in-open:bg-brand-solid_hover tw:in-open:hover:bg-brand-solid_hover tw:dark:in-open:bg-brand-solid_hover', + // Rest label and icon share one mid-gray, as in the pre-migration classic sidebar. + label: 'tw:text-tertiary tw:group-hover:text-brand-secondary', + labelSelected: + 'tw:text-primary_on-brand tw:group-hover:text-primary_on-brand', + labelOpen: + 'tw:in-open:text-primary_on-brand tw:in-open:group-hover:text-primary_on-brand', + icon: 'tw:text-fg-tertiary tw:group-hover:text-fg-brand-secondary_alt', + iconSelected: 'tw:text-fg-white tw:group-hover:text-fg-white', + iconOpen: 'tw:in-open:text-fg-white tw:in-open:group-hover:text-fg-white', +}); + +export type NavItemSize = 'sm' | 'md' | 'lg'; const sizeStyles = sortCx({ sm: { - padding: 'tw:py-1', - icon: 'tw:size-4', + root: 'tw:px-3 tw:py-1', + // Lines child labels up with the parent label: px-3 + icon + mr-2. + child: 'tw:py-1 tw:pr-3 tw:pl-9', + icon: 'tw:mr-2 tw:size-4', label: 'tw:text-sm tw:font-normal', labelCurrent: 'tw:font-semibold', }, md: { - padding: 'tw:py-2', - icon: 'tw:size-5', + root: 'tw:px-3 tw:py-2', + child: 'tw:py-2 tw:pr-3 tw:pl-10', + icon: 'tw:mr-2 tw:size-5', label: 'tw:text-md tw:font-semibold', labelCurrent: '', }, + // Roomy primary navigation: 56px rows with 28px icons. Children keep their + // icon and sit flush inside the open group. + lg: { + root: 'tw:rounded-xl tw:p-3.5', + child: 'tw:rounded-none tw:px-4 tw:py-2.5', + icon: 'tw:mr-2.5 tw:size-7', + label: 'tw:text-sm tw:font-medium', + labelCurrent: 'tw:font-semibold', + }, }); interface NavItemBaseProps { - /** Text and icon size. `sm` is for dense secondary lists. Defaults to `md`. */ + /** Text and icon size. `sm` is for dense secondary lists, `lg` for roomy primary navigation. Defaults to `md`. */ size?: NavItemSize; + /** `solid` fills the current item and an open group's header with the brand colour. Defaults to `default`. */ + variant?: NavItemVariant; /** Whether the nav item shows only an icon. */ iconOnly?: boolean; /** Whether the collapsible nav item is open. */ @@ -67,20 +105,42 @@ export const NavItemBase = ({ children, truncate = true, size = 'md', + variant = 'default', dataTestId, onClick, }: NavItemBaseProps) => { - const iconElement = Icon && ( + const isSolid = variant === 'solid'; + const isGroup = type === 'collapsible'; + // Children only show their icon at `lg`; smaller sizes indent instead. + const showIcon = type !== 'collapsible-child' || size === 'lg'; + + const iconElement = Icon && showIcon && (