From cc18146018be217ef67571440b6b907657699a0f Mon Sep 17 00:00:00 2001 From: Yonatan Kra Date: Mon, 8 May 2023 14:18:44 +0300 Subject: [PATCH] feat(pagination): create the pagination component (VIV-964) (#1146) * Create the pagination component * Add readme to components.json * Total and selectedIndex logics * Selected index attribute * Reset selected index * Items 1 to 5 show now * Creates the dots * Clicking scenarios * Need to fix RC between events in tests (clicked behaves wrongly) * Test events complete * Cleanup * Completed the use cases * Add documentation and navigation logic switch * Add navIcons API * Small change that ruins things :) * Center dots * pagination style :) * Refactor the template * Handle aria-pressed * Tab works (need to test edge cases) * Tab navigation complete * Linting stuff * Stylelint * Added size API * Hardened size API * Add snapshots * Update readme chore(enums): fix size enums * update snapshots * Constant button width * dots sizes & ui pngs * remove only from test * Remove obsolete branches --------- Co-authored-by: RTannenbaum --- apps/docs/_data/components.json | 8 + libs/components/src/lib/enums.ts | 16 +- libs/components/src/lib/pagination/README.md | 82 +++ .../src/lib/pagination/definition.ts | 26 + libs/components/src/lib/pagination/index.ts | 3 + .../src/lib/pagination/pagination.scss | 34 ++ .../src/lib/pagination/pagination.spec.ts | 465 ++++++++++++++++++ .../src/lib/pagination/pagination.template.ts | 91 ++++ .../src/lib/pagination/pagination.ts | 99 ++++ libs/components/src/lib/pagination/ui.test.ts | 37 ++ ...hots-pagination-Desktop-Chromium-linux.png | Bin 0 -> 24410 bytes ...shots-pagination-Desktop-Firefox-linux.png | Bin 0 -> 24807 bytes ...pshots-pagination-Desktop-Safari-linux.png | Bin 0 -> 21332 bytes 13 files changed, 853 insertions(+), 8 deletions(-) create mode 100644 libs/components/src/lib/pagination/README.md create mode 100644 libs/components/src/lib/pagination/definition.ts create mode 100644 libs/components/src/lib/pagination/index.ts create mode 100644 libs/components/src/lib/pagination/pagination.scss create mode 100644 libs/components/src/lib/pagination/pagination.spec.ts create mode 100644 libs/components/src/lib/pagination/pagination.template.ts create mode 100644 libs/components/src/lib/pagination/pagination.ts create mode 100644 libs/components/src/lib/pagination/ui.test.ts create mode 100644 libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Chromium-linux.png create mode 100644 libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Firefox-linux.png create mode 100644 libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Safari-linux.png diff --git a/apps/docs/_data/components.json b/apps/docs/_data/components.json index 77c496e286..c7ffa9fc7c 100644 --- a/apps/docs/_data/components.json +++ b/apps/docs/_data/components.json @@ -426,5 +426,13 @@ "/assets/modules/components/tag-group/index.js", "/assets/modules/components/tag/index.js" ] + }, + { + "title": "pagination", + "status": "alpha", + "markdown": "./libs/components/src/lib/pagination/README.md", + "modules": [ + "/assets/modules/components/pagination/index.js" + ] } ] diff --git a/libs/components/src/lib/enums.ts b/libs/components/src/lib/enums.ts index 876e5f3f4f..8fc0458c1d 100644 --- a/libs/components/src/lib/enums.ts +++ b/libs/components/src/lib/enums.ts @@ -33,17 +33,17 @@ export enum Appearance { } export enum Size { - UltraCondensed = 'ultraCondensed', - SuperCondensed = 'superCondensed', - ExtraCondensed = 'extraCondensed', - SemiCondensed = 'semiCondensed', + UltraCondensed = 'ultra-condensed', + SuperCondensed = 'super-condensed', + ExtraCondensed = 'extra-condensed', + SemiCondensed = 'semi-condensed', Condensed = 'condensed', Normal = 'normal', - SemiExpanded = 'semiExpanded', + SemiExpanded = 'semi-expanded', Expanded = 'expanded', - ExtraExpanded = 'extraExpanded', - SuperExpanded = 'superExpanded', - UltraExpanded = 'ultraExpanded', + ExtraExpanded = 'extra-expanded', + SuperExpanded = 'super-expanded', + UltraExpanded = 'ultra-expanded', } export enum LayoutSize { diff --git a/libs/components/src/lib/pagination/README.md b/libs/components/src/lib/pagination/README.md new file mode 100644 index 0000000000..ad04a57897 --- /dev/null +++ b/libs/components/src/lib/pagination/README.md @@ -0,0 +1,82 @@ +# pagination + +Represents a pagination custom element. + +```js + +``` + +```html preview + +``` + +## Members + +### Total + +Set the `total` attribute to change the pagination's view total pages. + +- Type: `number` +- Default: `0` + +```html preview + +``` + +### Size + +Set the `size` attribute to change the pagination's buttons sizes. + +- Type: `super-condensed` | `condensed` | `normal` +- Default: `super-condensed` + +```html preview blocks + + + +``` + +### Selected Index +Set the `selectedIndex` attribute to change the pagination's currently selected index. Note that this is a Zero Index counter. It is set to -1 if total pages is 0. + +- Type: `number` +- Default: `0` + +```html preview + + +``` + +### Nav Icons + +Set the `navIcons` attribute to change the pagination's navigation buttons type. + +- Type: `Boolean` +- Default: `false` + +```html preview blocks + + +``` + +### Pages List +`pagesList` is an immutable array that represents the current pagination's state. + +## Events + +### vwc-pagination-change +Listen to the `vwc-pagination-change` event to get notified when the pagination's state changes. + +```html preview + +
+ +``` diff --git a/libs/components/src/lib/pagination/definition.ts b/libs/components/src/lib/pagination/definition.ts new file mode 100644 index 0000000000..1869f63b90 --- /dev/null +++ b/libs/components/src/lib/pagination/definition.ts @@ -0,0 +1,26 @@ +import type { FoundationElementDefinition } from '@microsoft/fast-foundation'; +import {buttonRegistries} from '../button/definition'; +import { registerFactory } from '../../shared/design-system'; +import styles from './pagination.scss'; + +import { Pagination } from './pagination'; +import { PaginationTemplate as template } from './pagination.template'; + +export const paginationDefinition = + Pagination.compose({ + baseName: 'pagination', + template: template as any, + styles, + }); + +/** + * @internal + */ +export const paginationRegistries = [paginationDefinition(), buttonRegistries]; + +/** + * Registers the pagination element with the design system. + * + * @param prefix - the prefix to use for the component name + */ +export const registerPagination = registerFactory(paginationRegistries); diff --git a/libs/components/src/lib/pagination/index.ts b/libs/components/src/lib/pagination/index.ts new file mode 100644 index 0000000000..c065ecc47c --- /dev/null +++ b/libs/components/src/lib/pagination/index.ts @@ -0,0 +1,3 @@ +import { registerPagination } from './definition'; + +registerPagination(); diff --git a/libs/components/src/lib/pagination/pagination.scss b/libs/components/src/lib/pagination/pagination.scss new file mode 100644 index 0000000000..ee49effaeb --- /dev/null +++ b/libs/components/src/lib/pagination/pagination.scss @@ -0,0 +1,34 @@ +@use "../../../../../dist/libs/tokens/scss/tokens.constants" as constants; +@use "../../../../../dist/libs/tokens/scss/size.variables" as size; + + +.control{ + display: inline-flex; + justify-content: space-between; +} + +.buttons-wrapper { + display: flex; + column-gap: 4px; +} + +.dots { + align-self: center; + text-align: center; + + &:not(.size-super-condensed) { + font: var(#{constants.$vvd-typography-base-bold}); + } + &.size-super-condensed { + font: var(#{constants.$vvd-typography-base-condensed-bold}); + inline-size: #{size.$vvd-size-super-condensed}; + } + + &.size-condensed { + inline-size: #{size.$vvd-size-condensed}; + } + + &.size-normal { + inline-size: #{size.$vvd-size-normal}; + } +} diff --git a/libs/components/src/lib/pagination/pagination.spec.ts b/libs/components/src/lib/pagination/pagination.spec.ts new file mode 100644 index 0000000000..e1bae66510 --- /dev/null +++ b/libs/components/src/lib/pagination/pagination.spec.ts @@ -0,0 +1,465 @@ +import {elementUpdated, fixture, getControlElement} from '@vivid-nx/shared'; +import {Size} from '@vonage/vivid'; +import type {Button} from '../button/button'; +import {Pagination, PaginationSize} from './pagination'; +import '.'; + +const COMPONENT_TAG = 'vwc-pagination'; + +function getButtonText(button: any) { + const firstButtonText = button.label || button.textContent.trim(); + return firstButtonText; +} + +describe('vwc-pagination', () => { + let element: Pagination; + + beforeEach(async () => { + element = (await fixture( + `<${COMPONENT_TAG}>` + )) as Pagination; + }); + + describe('basic', () => { + it('should be initialized as a vwc-pagination', async () => { + expect(element).toBeInstanceOf(Pagination); + }); + }); + + describe('total', () => { + it('should init as 0', async () => { + expect(element.total).toEqual(0); + }); + + it('should set total as zero of set as negative', async () => { + element.total = -10; + expect(element.total).toEqual(0); + }); + + it('should reflect total attribute', async function () { + element.setAttribute('total', '10'); + await elementUpdated(element); + expect(element.total).toEqual(10); + }); + + it('should add a button when total is set to 1', async () => { + element.total = 1; + await elementUpdated(element); + const buttons = element.shadowRoot?.querySelectorAll('.vwc-pagination-button'); + const firstButtonText = getButtonText(buttons?.item(0)); + expect(element.pagesList.length).toEqual(1); + expect(buttons?.length).toEqual(1); + expect(firstButtonText).toEqual('1'); + }); + + it.each([2, 3, 4, 5, 6, 7])('should add %i buttons when total is set to %i', async (total) => { + element.total = total; + await elementUpdated(element); + const buttons = getButtons(element); + expect(element.pagesList.length).toEqual(total); + expect(buttons?.length).toEqual(total); + buttons?.forEach((button: any, index: number) => { + const buttonText = getButtonText(button); + expect(buttonText).toEqual(`${index + 1}`); + }); + }); + + it('should set the numbers around selectedIndex to minus 1 and plus one', async function () { + element.total = 20; + element.selectedIndex = 10; + await elementUpdated(element); + const buttonsAndDots = getControlElement(element).querySelector('#buttons-wrapper')?.children; + expect(getButtonText(buttonsAndDots?.item(2))).toEqual('10'); + expect(getButtonText(buttonsAndDots?.item(3))).toEqual('11'); + expect(getButtonText(buttonsAndDots?.item(4))).toEqual('12'); + }); + + it('should add 1,2,3 buttons, 3 dots and the last page when over 7 total', async () => { + element.total = 20; + await elementUpdated(element); + const buttonsAndDots = getControlElement(element).querySelector('#buttons-wrapper')?.children; + const dots = buttonsAndDots?.item(5); + const lastItem = buttonsAndDots?.item(6); + expect(buttonsAndDots?.length).toEqual(7); + expect(getButtonText(dots)).toEqual('...'); + expect(getButtonText(lastItem)).toEqual(element.total.toString()); + }); + + it('should set 1, "..." and the last 4 numbers when in the last four', function () { + element.total = 20; + element.selectedIndex = 17; + expect(element.pagesList).toEqual([1, '...', 16, 17, 18, 19, 20]); + }); + + it(`should set the second and one before last as "..." + when selectedIndex between 4 and (total-5)`, async function () { + element.total = 20; + element.selectedIndex = 4; + await elementUpdated(element); + const buttonsAndDots = getControlElement(element).querySelector('#buttons-wrapper')?.children; + expect(getButtonText(buttonsAndDots?.item(1))).toEqual('...'); + expect(getButtonText(buttonsAndDots?.item(5))).toEqual('...'); + }); + + it('should set only the first as "..." when total over 7 and selected index is one before last', async function () { + element.total = 20; + element.selectedIndex = 18; + await elementUpdated(element); + const buttonsAndDots = getControlElement(element).querySelector('#buttons-wrapper')?.children; + expect(getButtonText(buttonsAndDots?.item(1))).toEqual('...'); + expect(getButtonText(buttonsAndDots?.item(buttonsAndDots?.length - 2))).toEqual('19'); + }); + }); + + describe('selectedIndex', function () { + it('should set selectedIndex as zero after changing total', async () => { + element.total = 2; + const selectedIndexAfterFirstTotalSet = element.selectedIndex; + + element.selectedIndex = 1; + element.total = 3; + const selectedIndexAfterSecondTotalSet = element.selectedIndex; + + expect(selectedIndexAfterFirstTotalSet).toEqual(0); + expect(selectedIndexAfterSecondTotalSet).toEqual(0); + }); + + it('should reflect selectedIndex attribute', async function () { + element.setAttribute('selected-index', '10'); + await elementUpdated(element); + expect(element.selectedIndex).toEqual(10); + }); + + it('should set appearance "filled" of selectedIndex', async function () { + element.total = 20; + element.selectedIndex = 3; + await elementUpdated(element); + const button = element.shadowRoot?.querySelectorAll('.vwc-pagination-button').item(3); + expect(button?.getAttribute('appearance')).toEqual('filled'); + }); + + it('should set appearance "filled" of selectedIndex when navigating beyond boundary', async function () { + element.total = 20; + element.selectedIndex = 4; + await elementUpdated(element); + const button = element.shadowRoot?.querySelectorAll('.vwc-pagination-button').item(2); + expect(button?.getAttribute('appearance')).toEqual('filled'); + }); + + it('should set appearance "ghost" to any unselected buttons', async function () { + element.total = 20; + element.selectedIndex = 3; + await elementUpdated(element); + const buttons = getButtons(element); + buttons.forEach((button: Button, index: number) => { + if (index !== 3) { + expect(button.getAttribute('appearance')).toEqual('ghost'); + } + }); + }); + }); + + describe('navIcons', function() { + let prevButton: Button | undefined | null; + let nextButton: Button | undefined | null; + + beforeEach(async function () { + element.total = 20; + await elementUpdated(element); + prevButton = element.shadowRoot?.querySelector('.prev-button'); + nextButton = element.shadowRoot?.querySelector('.next-button'); + }); + + it('should default to true', function () { + expect(element.navIcons).toEqual(false); + }); + + it('should set icons if set to true', async function () { + element.navIcons = true; + await elementUpdated(element); + expect(prevButton?.hasAttribute('label')).toEqual(false); + expect(nextButton?.hasAttribute('label')).toEqual(false); + expect(prevButton?.getAttribute('icon')).toEqual('chevron-left-line'); + expect(nextButton?.getAttribute('icon')).toEqual('chevron-right-line'); + }); + + it('should use text buttons if set to false', async function () { + element.navIcons = false; + await elementUpdated(element); + expect(prevButton?.getAttribute('label')).toEqual('Previous'); + expect(nextButton?.getAttribute('label')).toEqual('Next'); + expect(prevButton?.hasAttribute('icon')).toEqual(false); + expect(nextButton?.hasAttribute('icon')).toEqual(false); + }); + }); + + describe('prev/next buttons', function () { + let prevButton: Button | undefined | null; + let nextButton: Button | undefined | null; + + beforeEach(async function () { + element.total = 20; + await elementUpdated(element); + prevButton = element.shadowRoot?.querySelector('.prev-button'); + nextButton = element.shadowRoot?.querySelector('.next-button'); + }); + + it('should set prevButton to enabled if selectedIndex is not 0', async function () { + element.selectedIndex = 3; + await elementUpdated(element); + expect(prevButton?.disabled).toEqual(false); + }); + + it('should set nextButton to enabled if selectedIndex is not the last', async function () { + element.selectedIndex = 19; + await elementUpdated(element); + element.selectedIndex = 3; + await elementUpdated(element); + expect(nextButton?.hasAttribute('disabled')).toEqual(false); + }); + + it('should set prevButton to disabled if selectedIndex is 0', async function () { + element.selectedIndex = 0; + await elementUpdated(element); + expect(prevButton?.hasAttribute('disabled')).toEqual(true); + }); + + it('should set nextButton to disabled if selectedIndex is the last', async function () { + element.selectedIndex = 19; + await elementUpdated(element); + expect(nextButton?.hasAttribute('disabled')).toEqual(true); + }); + + it('should increase selectedIndex when nextButton is clicked', async function () { + element.selectedIndex = 3; + await elementUpdated(element); + nextButton?.click(); + expect(element.selectedIndex).toEqual(4); + }); + + it('should decrease selectedIndex when prevButton is clicked', async function () { + element.selectedIndex = 3; + await elementUpdated(element); + prevButton?.click(); + expect(element.selectedIndex).toEqual(2); + }); + + it('should disabled buttons when no pages are shown', async function () { + element.total = 0; + await elementUpdated(element); + expect(prevButton?.hasAttribute('disabled')).toEqual(true); + expect(nextButton?.hasAttribute('disabled')).toEqual(true); + }); + }); + + describe('pagesList', function () { + it('should be immutable', function () { + const pagesList = element.pagesList; + expect(element.pagesList === pagesList).toEqual(false); + }); + }); + + describe('click events', function () { + let buttons: NodeListOf | undefined; + + function setEventListeners(status: { clicked: boolean; event?: Event;}) { + element.addEventListener('vwc-pagination-change', (e) => { + status.clicked = true; + status.event = e; + }); + } + beforeEach(async function () { + element.selectedIndex = 1; + + element.total = 20; + await elementUpdated(element); + buttons = element.shadowRoot?.querySelectorAll('.vwc-pagination-button'); + }); + + it('should change selectedIndex when clicking a valid button', async function () { + const status = {clicked: false}; + setEventListeners(status); + const button = buttons?.item(2); + button?.dispatchEvent(new MouseEvent('click')); + expect(element.selectedIndex).toEqual(2); + }); + + it('should leave selectedIndex as is when clicking the "..." button', async function () { + const status = {clicked: false}; + setEventListeners(status); + element.selectedIndex = 2; + const dots = getControlElement(element).querySelector('.dots'); + dots?.dispatchEvent(new MouseEvent('click')); + expect(element.selectedIndex).toEqual(2); + }); + + it('should fire the "change" event with the selectedIndex, total and oldIndex', async function () { + const status = {clicked: false, event: new Event('test')}; + setEventListeners(status); + const button = buttons?.item(2); + button?.dispatchEvent(new MouseEvent('click')); + expect(status.clicked).toEqual(true); + expect((status.event as MouseEvent).detail).toEqual({selectedIndex: 2, total: 20, oldIndex: 0}); + }); + + it('should prevent "change" event when selected button is clicked', async function () { + const status = {clicked: false, event: new Event('test')}; + element.selectedIndex = 1; + await elementUpdated(element); + setEventListeners(status); + const button = buttons?.item(1); + button?.dispatchEvent(new MouseEvent('click')); + expect(status.clicked).toEqual(false); + }); + + it('should prevent change event when "..." is clicked', async () => { + const status = {clicked: false, event: new Event('test')}; + setEventListeners(status); + const dots = getControlElement(element).querySelector('.dots'); + dots?.dispatchEvent(new MouseEvent('click')); + expect(status.clicked).toEqual(false); + }); + + it('should prevent change event on init', async () => { + const status = {clicked: false, event: new Event('test')}; + setEventListeners(status); + (element as any).selectedIndexChanged(undefined); + expect(status.clicked).toEqual(false); + }); + }); + + function getButtons(element: Pagination) { + return Array.from(element.shadowRoot?.querySelectorAll('.vwc-pagination-button') as unknown as Button[]); + } + + describe('keyboard events', function () { + + beforeEach(async function () { + element.total = 30; + await elementUpdated(element); + }); + + it('should select tag on spacebar', async function () { + const button = getButtons(element)[3]; + const event = new KeyboardEvent('keydown', {key: ' ', bubbles: true, composed: true}); + button?.dispatchEvent(event); + expect(element.selectedIndex).toEqual(3); + }); + + it('should select tag on Enter', async function () { + const button = getButtons(element)[1]; + const event = new KeyboardEvent('keydown', {key: 'Enter', bubbles: true, composed: true}); + button?.dispatchEvent(event); + expect(element.selectedIndex).toEqual(1); + }); + + it('should focus on next button on Tab press on a button', async function () { + const buttons = getButtons(element); + const button = buttons[1]; + button.focus(); + const event = new KeyboardEvent('keydown', {key: 'Tab', bubbles: true, composed: true}); + button.dispatchEvent(event); + expect((element.shadowRoot?.activeElement as any).label).toEqual(buttons[2].label); + }); + + it('should focus on previous button when tab+shift are pressed on a button', async function () { + const buttons = getButtons(element); + const button = buttons[1]; + button.focus(); + const event = new KeyboardEvent('keydown', {key: 'Tab', shiftKey: true, bubbles: true, composed: true}); + button.dispatchEvent(event); + expect((element.shadowRoot?.activeElement as any).label).toEqual(buttons[0].label); + }); + + it('should focus on prev button if focused on the first element', async function () { + element.selectedIndex = 5; + await elementUpdated(element); + const prevButton = element.shadowRoot?.querySelector('.prev-button') as Button; + const buttons = getButtons(element); + const button = buttons[0]; + button.focus(); + const event = new KeyboardEvent('keydown', {key: 'Tab', shiftKey: true, bubbles: true, composed: true}); + button.dispatchEvent(event); + expect((element.shadowRoot?.activeElement as any).label).toEqual(prevButton?.label); + }); + + it('should focus on prev button if focused on the first element', async function () { + const nextButton = element.shadowRoot?.querySelector('.next-button') as Button; + const buttons = getButtons(element); + const button = buttons[5]; + button.focus(); + const event = new KeyboardEvent('keydown', {key: 'Tab', bubbles: true, composed: true}); + button.dispatchEvent(event); + expect((element.shadowRoot?.activeElement as any).label).toEqual(nextButton?.label); + }); + }); + + describe('aria', function () { + it('should set aria-pressed false by default', async function () { + element.total = 20; + await elementUpdated(element); + const buttons = Array.from(element.shadowRoot?.querySelectorAll('.vwc-pagination-button') as unknown as Button[]); + const allButtonsAriaPressedFalse = buttons?.reduce((correct, button, index) => { + if (element.selectedIndex === index) return correct; + return correct && button.getAttribute('aria-pressed') === 'false'; + }, true); + expect(allButtonsAriaPressedFalse).toEqual(true); + }); + + it('should set aria-pressed on the selected button', async function () { + element.total = 20; + element.selectedIndex = 3; + await elementUpdated(element); + const button = element.shadowRoot?.querySelectorAll('.vwc-pagination-button').item(3); + expect(button?.getAttribute('aria-pressed')).toEqual('true'); + }); + }); + + describe('tabindex', function () { + it('should set tabindex of buttons to 0 by default', async function () { + element.total = 20; + await elementUpdated(element); + const buttons = Array.from(element.shadowRoot?.querySelectorAll('.vwc-pagination-button') as unknown as Button[]); + const allButtonsAriaPressedFalse = buttons?.reduce((correct, button) => { + return correct && button.getAttribute('tabindex') === '0'; + }, true); + expect(allButtonsAriaPressedFalse).toEqual(true); + }); + }); + + describe('size', function () { + it('should set size super-condensed of all buttons by default', async function () { + element.total = 20; + await elementUpdated(element); + const allButtons = Array.from(element.shadowRoot?.querySelectorAll('vwc-button') as unknown as Button[]); + const allButtonsCondensed = allButtons?.reduce((correct, button) => { + return correct && button.size === Size.SuperCondensed; + }, true); + expect(allButtonsCondensed).toEqual(true); + }); + + it('should change all buttons sizes', async function () { + element.total = 20; + await elementUpdated(element); + element.size = Size.Normal; + await elementUpdated(element); + const allButtons = Array.from(element.shadowRoot?.querySelectorAll('vwc-button') as unknown as Button[]); + const allButtonsCondensed = allButtons?.reduce((correct, button) => { + return correct && button.size === Size.Normal; + }, true); + expect(allButtonsCondensed).toEqual(true); + }); + + it('should revert to super-condensed if set to invalid size', async function () { + element.total = 20; + await elementUpdated(element); + element.size = 'invalid-size' as PaginationSize; + await elementUpdated(element); + const allButtons = Array.from(element.shadowRoot?.querySelectorAll('vwc-button') as unknown as Button[]); + const allButtonsCondensed = allButtons?.reduce((correct, button) => { + return correct && button.size === Size.SuperCondensed; + }, true); + expect(allButtonsCondensed).toEqual(true); + }); + }); +}); diff --git a/libs/components/src/lib/pagination/pagination.template.ts b/libs/components/src/lib/pagination/pagination.template.ts new file mode 100644 index 0000000000..539f173a8c --- /dev/null +++ b/libs/components/src/lib/pagination/pagination.template.ts @@ -0,0 +1,91 @@ +import {children, elements, html, ref, repeat, when} from '@microsoft/fast-element'; +import type { ViewTemplate } from '@microsoft/fast-element'; +import type { + ElementDefinitionContext, + FoundationElementDefinition, +} from '@microsoft/fast-foundation'; +import { classNames } from '@microsoft/fast-web-utilities'; +import { Size } from '../enums'; +import {Button} from '../button/button'; +import type {Pagination} from './pagination'; + +const ALLOWED_SIZES = [Size.SuperCondensed, Size.Condensed, Size.Normal]; + +const handleSelection = (value: string | number, {parent: x}: {parent: Pagination}) => { + return x.selectedIndex = (Number(value) - 1); +}; + +const handleKeyDown = (value: string | number, {event, parent}: {event: KeyboardEvent, parent: Pagination}) => { + if (event.key === ' ' || event.key === 'Enter') { + handleSelection(value, {parent}); + } + if (event.key === 'Tab') { + event.target!.dispatchEvent( + new CustomEvent('tabpressed', {detail: {value, shiftKey: event.shiftKey}, bubbles: true, composed: true})); + } +}; + +const getClasses = (_: Pagination) => classNames('control'); + +function getButtonAppearance(value: string | number, {parent}: {parent: Pagination}) { + return (parent.selectedIndex === Number(value) - 1) ? 'filled' : 'ghost'; +} + +const paginationButtonRenderer = (buttonTag: string) => html` + ${when(value => value !== '...', + html` + <${buttonTag} class="vwc-pagination-button" + label="${(value) => value}" + appearance="${getButtonAppearance}" + size="${(_, {parent: x}) => getPaginationSize(x)}" + tabindex="0" + aria-pressed="${(value, {parent}) => parent.selectedIndex === Number(value) - 1}" + @click="${handleSelection}" + @keydown="${handleKeyDown}" + + `)} + ${when(value => value === '...', html`
...
`)}`; + +const getPaginationSize = (x: Pagination) => { + if (!x.size || !ALLOWED_SIZES.includes(x.size)) { + return Size.SuperCondensed; + } + return x.size; +}; + + +/** + * The template for the {@link @microsoft/fast-foundation#Pagination} component. + * + * @param context + * @public + */ + +export const PaginationTemplate: ( + context: ElementDefinitionContext, + definition: FoundationElementDefinition +) => ViewTemplate = (context) => { + const buttonTag = context.tagFor(Button); + const paginationButtonTemplate = paginationButtonRenderer(buttonTag); + return html` +
+ <${buttonTag} class="prev-button" ${ref('prevButton')} + label="${x => !x.navIcons ? 'Previous' : null}" + icon="${x => x.navIcons ? 'chevron-left-line' : null}" + size="${getPaginationSize}" + ?disabled="${x => x.total === 0 || x.selectedIndex === 0}" + @click="${x => (x.selectedIndex !== undefined) && x.selectedIndex--}" + > +
+ ${repeat(x => x.pagesList, paginationButtonTemplate, { positioning: true })} +
+ <${buttonTag} class="next-button" ${ref('nextButton')} + label="${x => !x.navIcons ? 'Next' : null}" + icon="${x => x.navIcons ? 'chevron-right-line' : null}" + size="${getPaginationSize}" + ?disabled="${x => x.total === 0 || x.selectedIndex === (x.total - 1)}" + @click="${x => (x.selectedIndex !== undefined) && x.selectedIndex++}" + > +
`; +}; + diff --git a/libs/components/src/lib/pagination/pagination.ts b/libs/components/src/lib/pagination/pagination.ts new file mode 100644 index 0000000000..294f04d40f --- /dev/null +++ b/libs/components/src/lib/pagination/pagination.ts @@ -0,0 +1,99 @@ +import { FoundationElement } from '@microsoft/fast-foundation'; +import {attr, observable, ValueConverter, volatile} from '@microsoft/fast-element'; +import type { Size } from '../enums'; +import type {Button} from '../button/button'; + +export type PaginationSize = Extract; + +const MAX_DIGITS_AND_PLACEHOLDERS = 7; +const totalConverter: ValueConverter = { + fromView: (value: string) => parseInt(value, 10), + toView: (value: number) => value.toString() +}; + +/** + * Base class for pagination + * + * @public + */ +export class Pagination extends FoundationElement { + @attr size?: PaginationSize; + + @observable + paginationButtons?: Button[]; + + @observable + prevButton?: Button; + + @observable + nextButton?: Button; + + @attr({attribute: 'nav-icons', mode: 'boolean'}) navIcons = false; + + @volatile + get pagesList() { + return new Array(this.total < MAX_DIGITS_AND_PLACEHOLDERS ? this.total : + MAX_DIGITS_AND_PLACEHOLDERS).fill(0).map((_, i, arr) => { + + if (i === 0) return 1; + if (i === arr.length - 1) return this.total; + + if (this.selectedIndex !== undefined && this.total > MAX_DIGITS_AND_PLACEHOLDERS) { + if (this.selectedIndex < 4) { + if (i === MAX_DIGITS_AND_PLACEHOLDERS - 2) return '...'; + } + if (this.selectedIndex >= 4 && this.selectedIndex <= this.total - 5) { + if (i > 1 && i < MAX_DIGITS_AND_PLACEHOLDERS - 2) return this.selectedIndex + (i - 2); + if (i === 1 || i === MAX_DIGITS_AND_PLACEHOLDERS - 2) return '...'; + } + if (this.selectedIndex > this.total - 5) { + if (i > 1) return this.total - (6 - i); + if (i === 1) return '...'; + } + } + return i + 1; + }); + } + + @attr({mode: 'reflect', converter: totalConverter}) total: number; + @attr({mode: 'reflect', converter: totalConverter, attribute: 'selected-index'}) selectedIndex: number | undefined; + + constructor() { + super(); + this.total = 0; + this.selectedIndex = 0; + this.addEventListener('tabpressed', (e: Event) => { + const {value: currentLabel, shiftKey} = (e as CustomEvent).detail; + const index = this.paginationButtons!.findIndex(button => Number(button.label) === currentLabel) as number; + const focusDirection = shiftKey ? -1 : 1; + const newIndex = index + focusDirection; + if (newIndex < 0) { + return this.prevButton!.focus(); + } + if (newIndex > this.paginationButtons!.length - 1) { + return this.nextButton!.focus(); + } + this.paginationButtons && this.paginationButtons[index + focusDirection].focus(); + }); + } + + totalChanged(_: number, newValue: number) { + if (newValue < 0) { + this.total = 0; + } + + this.selectedIndex = 0; + } + + selectedIndexChanged(oldValue: number, newValue: number) { + if (oldValue === undefined) return; + this.$emit('vwc-pagination-change', {selectedIndex: newValue, total: this.total, oldIndex: oldValue}); + } + + paginationButtonsChanged(_: Button[] | undefined, newValue: Button[]) { + newValue.forEach(button => { + button.shadowRoot!.querySelector('button')!.classList.add('icon-only'); + }); + } +} + diff --git a/libs/components/src/lib/pagination/ui.test.ts b/libs/components/src/lib/pagination/ui.test.ts new file mode 100644 index 0000000000..005c2bc534 --- /dev/null +++ b/libs/components/src/lib/pagination/ui.test.ts @@ -0,0 +1,37 @@ +import * as path from 'path'; +import { expect, test } from '@playwright/test'; +import type { Page } from '@playwright/test'; +import { + extractHTMLBlocksFromReadme, + loadComponents, + loadTemplate, +} from '../../visual-tests/visual-tests-utils.js'; + +const components = ['pagination']; + +test('should show the component', async ({ page }: { page: Page }) => { + const template = extractHTMLBlocksFromReadme( + path.join(new URL('.', import.meta.url).pathname, 'README.md') + ).reduce( + (htmlString: string, block: string) => + `${htmlString}
${block}
`, + '' + ); + + await loadComponents({ + page, + components, + }); + await loadTemplate({ + page, + template, + }); + + const testWrapper = await page.$('#wrapper'); + + await page.waitForLoadState('networkidle'); + + expect(await testWrapper?.screenshot()).toMatchSnapshot( + './snapshots/pagination.png' + ); +}); diff --git a/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Chromium-linux.png b/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Chromium-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..b1bfeeca7a44323d23d8cf3542713f2e147c6fca GIT binary patch literal 24410 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 hd!6c^k8 literal 0 HcmV?d00001 diff --git a/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Firefox-linux.png b/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..cee0809396ef25a90ae35516df9761c0e8f3d751 GIT binary patch literal 24807 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 b{>0(!tU(Vh&;J3=YL&$n8&aXEN92D2Z}dLE literal 0 HcmV?d00001 diff --git a/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Safari-linux.png b/libs/components/src/lib/pagination/ui.test.ts-snapshots/snapshots-pagination-Desktop-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc66e3ccbd252e5bedcf42f8882188bd7c969b9 GIT binary patch literal 21332 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 literal 0 HcmV?d00001