diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2802f490b976cc..2f46c6076e59cd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,13 +13,11 @@ updates: - 'minor' - 'patch' - # npm dependencies - weekly updates for devDependencies + # npm dependencies - weekly minor and patch updates grouped by dependency type - package-ecosystem: 'npm' directory: '/' schedule: interval: 'weekly' - allow: - - dependency-type: 'development' open-pull-requests-limit: 6 versioning-strategy: increase ignore: @@ -27,31 +25,19 @@ updates: - dependency-name: '*' update-types: ['version-update:semver-major'] groups: + npm-security-updates: + applies-to: security-updates + patterns: + - '*' npm-dev-minor-patch: + dependency-type: 'development' patterns: - '*' update-types: - 'minor' - 'patch' - # Note: Security updates for known vulnerabilities are handled automatically by GitHub - # and will create PRs regardless of the ignore rules above - - # Additional configuration for production dependencies security updates - # This ensures production dependencies get security updates more frequently - - package-ecosystem: 'npm' - directory: '/' - schedule: - interval: 'daily' - allow: - - dependency-type: 'production' - open-pull-requests-limit: 3 - versioning-strategy: increase - # Only create PRs for security updates and minor/patch versions - ignore: - - dependency-name: '*' - update-types: ['version-update:semver-major'] - groups: npm-prod-minor-patch: + dependency-type: 'production' patterns: - '*' update-types: diff --git a/.github/instructions/dependabot-security-fixes.instructions.md b/.github/instructions/dependabot-security-fixes.instructions.md index ccf52390b36345..80e9b8cd89ebe1 100644 --- a/.github/instructions/dependabot-security-fixes.instructions.md +++ b/.github/instructions/dependabot-security-fixes.instructions.md @@ -10,23 +10,24 @@ This instruction guide explains how Dependabot automation works for security fix Dependabot is configured to automatically create pull requests for: -1. **Security updates** - Daily scanning for vulnerable production dependencies -2. **Development dependencies** - Weekly updates for devDependencies +1. **Security updates** - Advisory-driven updates grouped into consolidated npm pull requests +2. **npm dependencies** - Weekly minor and patch updates grouped by dependency type 3. **GitHub Actions** - Weekly updates for workflow dependencies ## Configuration The Dependabot configuration is defined in `.github/dependabot.yml`: -- **Production dependencies**: Daily security updates with higher priority -- **Development dependencies**: Weekly updates, excluding major version bumps +- **Production dependencies**: Weekly minor and patch version updates +- **Development dependencies**: Weekly minor and patch version updates - **GitHub Actions**: Weekly updates +- **Security updates**: Grouped separately and not limited by the version update schedule ## Security Vulnerability Resolution ### Automatic Security Updates -GitHub's automatic security updates work independently of the Dependabot configuration and will create PRs for known vulnerabilities even if they require major version bumps. +GitHub triggers automatic security updates independently of the configured version update schedule. The Dependabot configuration groups eligible npm security updates into consolidated pull requests, including fixes that require major version bumps. ### Manual Resolution via Yarn Resolutions diff --git a/change/@fluentui-react-menu-ac7e6b5e-1976-4e85-b31e-1f769b78ff93.json b/change/@fluentui-react-menu-ac7e6b5e-1976-4e85-b31e-1f769b78ff93.json new file mode 100644 index 00000000000000..245c209b716e4d --- /dev/null +++ b/change/@fluentui-react-menu-ac7e6b5e-1976-4e85-b31e-1f769b78ff93.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "test: add regression tests for split-group submenu trigger alignment icons", + "packageName": "@fluentui/react-menu", + "email": "paulmardling@microsoft.com", + "dependentChangeType": "none" +} diff --git a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.test.tsx b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.test.tsx index 8503963a2976cc..03d09c30c9eaa5 100644 --- a/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.test.tsx +++ b/packages/react-components/react-menu/library/src/components/MenuItem/useMenuItem.test.tsx @@ -5,6 +5,7 @@ import { useMenuItem_unstable } from './useMenuItem'; import { MenuListProvider } from '../../contexts/menuListContext'; import { MenuProvider } from '../../contexts/menuContext'; import { MenuTriggerContextProvider } from '../../contexts/menuTriggerContext'; +import { MenuSplitGroupContextProvider } from '../../contexts/menuSplitGroupContext'; import type { MenuListContextValue } from '../../contexts/menuListContext'; import type { MenuContextValue } from '../../contexts/menuContext'; import type { ARIAButtonElement } from '@fluentui/react-aria'; @@ -51,15 +52,28 @@ function makeWrapper( menuList?: Partial; menu?: Partial; isSubmenuTrigger?: boolean; + inMenuSplitGroup?: boolean; } = {}, ) { const menuListValue: MenuListContextValue = { ...defaultMenuListContextValue, ...options.menuList }; const menuValue: MenuContextValue = { ...defaultMenuContextValue, ...options.menu }; + const splitGroupContextValue = { + setMultiline: () => null, + }; + return ({ children }: { children: React.ReactNode }) => ( - {children} + {options.inMenuSplitGroup ? ( + + + {children} + + + ) : ( + {children} + )} ); @@ -254,6 +268,17 @@ describe('useMenuItem_unstable', () => { expect(result.current.icon).toBeDefined(); }); + it('icon is undefined for a split-group submenu trigger even when hasIcons=true', () => { + const ref = React.createRef>(); + + const { result } = renderHook(() => useMenuItem_unstable({}, ref), { + wrapper: makeWrapper({ menuList: { hasIcons: true }, isSubmenuTrigger: true, inMenuSplitGroup: true }), + }); + + expect(result.current.hasSubmenu).toBe(true); + expect(result.current.icon).toBeUndefined(); + }); + it('checkmark is undefined when hasCheckmarks=false and no checkmark prop', () => { const ref = React.createRef>(); @@ -273,5 +298,16 @@ describe('useMenuItem_unstable', () => { expect(result.current.checkmark).toBeDefined(); }); + + it('checkmark is undefined for a split-group submenu trigger even when hasCheckmarks=true', () => { + const ref = React.createRef>(); + + const { result } = renderHook(() => useMenuItem_unstable({}, ref), { + wrapper: makeWrapper({ menuList: { hasCheckmarks: true }, isSubmenuTrigger: true, inMenuSplitGroup: true }), + }); + + expect(result.current.hasSubmenu).toBe(true); + expect(result.current.checkmark).toBeUndefined(); + }); }); }); diff --git a/tools/react-integration-tester/src/__tests__/setup.test.ts b/tools/react-integration-tester/src/__tests__/setup.test.ts index 5485a322a4d84d..c2e5952760de1e 100644 --- a/tools/react-integration-tester/src/__tests__/setup.test.ts +++ b/tools/react-integration-tester/src/__tests__/setup.test.ts @@ -152,6 +152,7 @@ describe('setup()', () => { join(fs.tempDir, 'tsconfig.lib.json'), JSON.stringify({ include: ['src/index.ts'], compilerOptions: { target: 'ES2020', lib: ['ES2020', 'DOM'] } }), ); + writeFileSync(join(fs.tempDir, 'tsconfig.base.json'), JSON.stringify({ compilerOptions: { paths: {} } })); writeFileSync(join(fs.tempDir, 'jest.config.js'), 'module.exports = {};'); writeFileSync(join(fs.tempDir, 'cypress.config.ts'), 'export default {};'); @@ -191,6 +192,7 @@ describe('setup()', () => { '.swcrc', 'cypress.config.ts', 'tsconfig.cy.json', + 'tsconfig.base.json', 'package.json', ]; for (const f of expectedFiles) { diff --git a/tools/react-integration-tester/src/setup.ts b/tools/react-integration-tester/src/setup.ts index aa4e4542e89cae..f1f91556bfd99e 100644 --- a/tools/react-integration-tester/src/setup.ts +++ b/tools/react-integration-tester/src/setup.ts @@ -501,6 +501,14 @@ export async function setup( metadata.tmpl, join(projectPath, 'tsconfig.cy.json'), ); + + const workspaceTsConfigBasePath = join(workspaceRoot, 'tsconfig.base.json'); + if (existsSync(workspaceTsConfigBasePath)) { + // Provide a local tsconfig.base.json so relative TsconfigPathsPlugin lookups keep working in RIT context. + writeJsonFile(join(projectPath, 'tsconfig.base.json'), { + extends: relative(projectPath, workspaceTsConfigBasePath).replace(/\\/g, '/'), + }); + } } // 4) Create package.json for test project including npm scripts