diff --git a/packages/api-generator/src/locale/en/VPagination.json b/packages/api-generator/src/locale/en/VPagination.json index 2c222a10404..dc5a54b0caa 100644 --- a/packages/api-generator/src/locale/en/VPagination.json +++ b/packages/api-generator/src/locale/en/VPagination.json @@ -14,7 +14,7 @@ "pageAriaLabel": "Label for each page button.", "prevIcon": "The icon to use for the prev button.", "previousAriaLabel": "Label for the previous button.", - "showFirstLastPage": "Show buttons for going to first and last page.", + "showFirstLastPage": "Show buttons for going to first and last page. Since v4.1.0 it accepts `'only-first'`, to only the first page button.", "start": "Specify the starting page.", "totalVisible": "Specify the total visible pagination numbers." }, diff --git a/packages/docs/src/data/new-in.json b/packages/docs/src/data/new-in.json index b539df23b0f..1a2812e97cd 100644 --- a/packages/docs/src/data/new-in.json +++ b/packages/docs/src/data/new-in.json @@ -133,6 +133,7 @@ "headerProps": "3.5.0", "initialSortOrder": "3.11.0", "pageBy": "3.12.0", + "showFirstLastPage": "4.1.0", "sortIcon": "3.12.0" }, "slots": { @@ -147,6 +148,7 @@ "groupExpandIcon": "3.10.0", "initialSortOrder": "3.11.0", "pageBy": "3.12.0", + "showFirstLastPage": "4.1.0", "sortIcon": "3.12.0" }, "slots": { diff --git a/packages/vuetify/src/components/VDataTable/VDataTableFooter.tsx b/packages/vuetify/src/components/VDataTable/VDataTableFooter.tsx index b67d27ec0c4..f2edb09ec94 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableFooter.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableFooter.tsx @@ -12,10 +12,11 @@ import { useLocale } from '@/composables/locale' // Utilities import { computed } from 'vue' -import { genericComponent, omit, propsFactory, useRender } from '@/util' +import { genericComponent, omit, pick, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' +import { makeVPaginationProps } from '../VPagination/VPagination' export const makeVDataTableFooterProps = propsFactory({ color: String, @@ -70,6 +71,10 @@ export const makeVDataTableFooterProps = propsFactory({ ]), }, showCurrentPage: Boolean, + + ...pick(makeVPaginationProps({ + showFirstLastPage: true, + }), ['showFirstLastPage']), }, 'VDataTableFooter') export const VDataTableFooter = genericComponent<{ prepend: never }>()({ diff --git a/packages/vuetify/src/components/VPagination/VPagination.tsx b/packages/vuetify/src/components/VPagination/VPagination.tsx index 5ca07fbfc5a..0ccb39885e5 100644 --- a/packages/vuetify/src/components/VPagination/VPagination.tsx +++ b/packages/vuetify/src/components/VPagination/VPagination.tsx @@ -27,7 +27,7 @@ import { computed, nextTick, shallowRef, toRef } from 'vue' import { createRange, genericComponent, keyValues, propsFactory, useRender } from '@/util' // Types -import type { ComponentPublicInstance } from 'vue' +import type { ComponentPublicInstance, PropType } from 'vue' type ItemSlot = { isActive: boolean @@ -117,7 +117,10 @@ export const makeVPaginationProps = propsFactory({ type: String, default: '...', }, - showFirstLastPage: Boolean, + showFirstLastPage: { + type: [Boolean, String] as PropType, + default: false, + }, ...makeBorderProps(), ...makeComponentProps(), @@ -278,7 +281,7 @@ export const VPagination = genericComponent()({ const nextDisabled = !!props.disabled || page.value >= start.value + length.value - 1 return { - first: props.showFirstLastPage ? { + first: [true, 'only-first'].includes(props.showFirstLastPage) ? { icon: isRtl.value ? props.lastIcon : props.firstIcon, onClick: (e: Event) => setValue(e, start.value, 'first'), disabled: prevDisabled, @@ -299,7 +302,7 @@ export const VPagination = genericComponent()({ 'aria-label': t(props.nextAriaLabel), 'aria-disabled': nextDisabled, }, - last: props.showFirstLastPage ? { + last: props.showFirstLastPage === true ? { icon: isRtl.value ? props.firstIcon : props.lastIcon, onClick: (e: Event) => setValue(e, start.value + length.value - 1, 'last'), disabled: nextDisabled, @@ -339,7 +342,7 @@ export const VPagination = genericComponent()({ data-test="v-pagination-root" >
    - { props.showFirstLastPage && ( + {[true, 'only-first'].includes(props.showFirstLastPage) && (
  • { slots.first ? slots.first(controls.value.first!) : ( @@ -380,12 +383,8 @@ export const VPagination = genericComponent()({ )}
  • - { props.showFirstLastPage && ( -
  • + { props.showFirstLastPage === true && ( +
  • { slots.last ? slots.last(controls.value.last!) : ( )} diff --git a/packages/vuetify/src/components/VPagination/__tests__/VPagination.spec.browser.tsx b/packages/vuetify/src/components/VPagination/__tests__/VPagination.spec.browser.tsx index 09d176d2be5..f2f4cf24cba 100644 --- a/packages/vuetify/src/components/VPagination/__tests__/VPagination.spec.browser.tsx +++ b/packages/vuetify/src/components/VPagination/__tests__/VPagination.spec.browser.tsx @@ -23,6 +23,24 @@ describe('VPagination', () => { expect(screen.getAllByCSS('.v-pagination__item')).toHaveLength(3) }) + it('should render without first and last page buttons', () => { + render(() => ( + + )) + + expect(screen.queryByCSS('[data-test="v-pagination-first"]')).not.toBeInTheDocument() + expect(screen.queryByCSS('[data-test="v-pagination-last"]')).not.toBeInTheDocument() + }) + + it('should render without last page button', () => { + render(() => ( + + )) + + expect(screen.queryByCSS('[data-test="v-pagination-first"]')).toBeInTheDocument() + expect(screen.queryByCSS('[data-test="v-pagination-last"]')).not.toBeInTheDocument() + }) + it('should react to mouse navigation', async () => { render(() => (