diff --git a/packages/varlet-ui/src/bottom-navigation/__tests__/index.spec.js b/packages/varlet-ui/src/bottom-navigation/__tests__/index.spec.js
index e950685f8cf..4408b3935ec 100644
--- a/packages/varlet-ui/src/bottom-navigation/__tests__/index.spec.js
+++ b/packages/varlet-ui/src/bottom-navigation/__tests__/index.spec.js
@@ -145,6 +145,38 @@ describe('test bottom-navigation events', () => {
wrapper.unmount()
})
+ test('bottom-navigation before-change rejects update', async () => {
+ const handleBeforeChange = vi.fn(() => [true, false])
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ data: () => ({
+ active: 0,
+ }),
+ methods: {
+ handleBeforeChange,
+ onUpdateActive,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await delay(16)
+ await trigger(wrapper.findAll('.var-bottom-navigation-item')[1], 'click')
+ await delay(16)
+ expect(handleBeforeChange).toHaveBeenCalledTimes(1)
+ expect(onUpdateActive).not.toHaveBeenCalled()
+
+ wrapper.unmount()
+ })
+
test('bottom-navigation fab-click event', async () => {
const handleFabClick = vi.fn()
const wrapper = mount({
@@ -177,6 +209,184 @@ describe('test bottom-navigation events', () => {
})
describe('test bottom-navigation component props', () => {
+ test('bottom-navigation active boundary for non-number', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+
+ `,
+ methods: {
+ onUpdateActive,
+ },
+ })
+
+ await delay(16)
+ expect(onUpdateActive).not.toHaveBeenCalled()
+ wrapper.unmount()
+ })
+
+ test('bottom-navigation active boundary clamps', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+
+ `,
+ methods: {
+ onUpdateActive,
+ },
+ })
+
+ await delay(16)
+ expect(onUpdateActive).toHaveBeenCalledWith(0)
+ wrapper.unmount()
+
+ const onUpdateActiveMax = vi.fn()
+ const wrapperMax = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+
+ `,
+ methods: {
+ onUpdateActiveMax,
+ },
+ })
+
+ await delay(16)
+ expect(onUpdateActiveMax).toHaveBeenCalledWith(1)
+ wrapperMax.unmount()
+ })
+
+ test('bottom-navigation clamps active above max', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ methods: {
+ onUpdateActive,
+ },
+ })
+
+ await delay(16)
+ expect(onUpdateActive).toHaveBeenCalledWith(0)
+ wrapper.unmount()
+ })
+
+ test('bottom-navigation ignores NaN active', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ data: () => ({
+ active: Number.NaN,
+ }),
+ methods: {
+ onUpdateActive,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await delay(16)
+ expect(onUpdateActive).not.toHaveBeenCalled()
+ wrapper.unmount()
+ })
+
+ test('bottom-navigation clamps negative active on length change', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ data: () => ({
+ active: -1,
+ showItems: false,
+ }),
+ methods: {
+ onUpdateActive,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await delay(16)
+ expect(onUpdateActive).not.toHaveBeenCalled()
+
+ await wrapper.setData({ showItems: true })
+ await delay(16)
+ expect(onUpdateActive).toHaveBeenCalledWith(0)
+
+ wrapper.unmount()
+ })
+
+ test('bottom-navigation active boundary clamps on length change', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ data: () => ({
+ showItems: false,
+ }),
+ methods: {
+ onUpdateActive,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await delay(16)
+ expect(onUpdateActive).not.toHaveBeenCalled()
+
+ await wrapper.setData({ showItems: true })
+ await delay(16)
+ expect(onUpdateActive).toHaveBeenCalledWith(1)
+
+ wrapper.unmount()
+ })
+
test('bottom-navigation fixed', async () => {
const wrapper = mount(Wrapper, {
props: {
@@ -331,4 +541,211 @@ describe('test bottom-navigation component props', () => {
expect(wrapper.html()).toMatchSnapshot()
wrapper.unmount()
})
+
+ test('bottom-navigation adds right-space class for odd items with fab', async () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+ home
+
+
+
+
+
+ `,
+ })
+
+ await delay(16)
+ const items = wrapper.findAll('.var-bottom-navigation-item')
+ expect(items[2].classes()).toContain('var-bottom-navigation-item--right-space')
+ wrapper.unmount()
+ })
+})
+
+test('bottom-navigation-item renders badge with icon', async () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ })
+
+ await new Promise((resolve) => setTimeout(resolve, 16))
+ const item = wrapper.find('.var-bottom-navigation-item')
+ expect(item.find('.var-badge').exists()).toBe(true)
+ expect(item.find('.var-badge').find('.var-icon-home').exists()).toBe(true)
+ const badge = wrapper.findComponent({ name: 'VarBadge' })
+ expect(badge.props('type')).toBe('danger')
+ expect(badge.props('dot')).toBe(true)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item badge without icon', async () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ })
+
+ await new Promise((resolve) => setTimeout(resolve, 16))
+ const item = wrapper.find('.var-bottom-navigation-item')
+ expect(item.find('.var-badge').exists()).toBe(true)
+ expect(item.find('.var-icon').exists()).toBe(false)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item renders icon without badge', () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ })
+
+ const item = wrapper.find('.var-bottom-navigation-item')
+ expect(item.find('.var-badge').exists()).toBe(false)
+ expect(item.find('.var-icon-home').exists()).toBe(true)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item supports custom icon slot', () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ icon
+
+
+
+ `,
+ })
+
+ expect(wrapper.find('.custom-icon').exists()).toBe(true)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item active by name', () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ expect(wrapper.findAll('.var-bottom-navigation-item')[0].classes()).toContain('var-bottom-navigation-item--active')
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item active by index', async () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await new Promise((resolve) => setTimeout(resolve, 16))
+ expect(wrapper.findAll('.var-bottom-navigation-item')[1].classes()).toContain('var-bottom-navigation-item--active')
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item click triggers update', async () => {
+ const onUpdateActive = vi.fn()
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ methods: {
+ onUpdateActive,
+ },
+ template: `
+
+
+
+
+ `,
+ })
+
+ await new Promise((resolve) => setTimeout(resolve, 16))
+ await wrapper.findAll('.var-bottom-navigation-item')[1].trigger('click')
+ expect(onUpdateActive).toHaveBeenCalledWith(1)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item renders empty icon when none provided', () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ })
+
+ const item = wrapper.find('.var-bottom-navigation-item')
+ expect(item.find('.var-icon').exists()).toBe(false)
+ expect(item.find('.custom-icon').exists()).toBe(false)
+ wrapper.unmount()
+})
+
+test('bottom-navigation-item badge uses custom props', () => {
+ const wrapper = mount({
+ components: {
+ [VarBottomNavigation.name]: VarBottomNavigation,
+ [VarBottomNavigationItem.name]: VarBottomNavigationItem,
+ },
+ template: `
+
+
+
+ `,
+ })
+
+ const badge = wrapper.findComponent({ name: 'VarBadge' })
+ expect(badge.props('type')).toBe('success')
+ expect(badge.props('dot')).toBe(false)
+ expect(badge.props('value')).toBe(9)
+ wrapper.unmount()
})