diff --git a/design-library/src/components/BccPagination/BccPagination.css b/design-library/src/components/BccPagination/BccPagination.css new file mode 100644 index 00000000..e995d4b5 --- /dev/null +++ b/design-library/src/components/BccPagination/BccPagination.css @@ -0,0 +1,61 @@ +@layer components { + + .bcc-pagination { + @apply flex flex-row items-center justify-between + } + + .bcc-pagination-left { + @apply flex-row-reverse + } + + .bcc-pagination-center { + @apply flex-row justify-evenly + } + + .bcc-pagination-rowsperpage-container { + @apply inline-flex flex-row items-center gap-2 text-secondary + } + + .bcc-pagination-rowsperpage { + @apply w-[72px] + } + + .bcc-pagination-button-container { + @apply inline-flex items-center + } + + .bcc-pagination-button { + @apply select-none inline-flex items-center tracking-wide border border-s-[0.5px] border-on-primary justify-center active:shadow-inner focus-visible:outline focus-visible:outline-2 focus-visible:outline-emphasis focus-visible:outline-offset-2; + @apply cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none; + + /* Default base size */ + @apply text-sm gap-x-2; + + @apply px-3 py-1.5 w-[33px] h-[33px] bg-primary text-secondary hover:bg-secondary-hover; + + } + + .bcc-pagination-button-selected { + @apply bg-secondary font-semibold text-interactive; + } + + .bcc-pagination-elipsis { + @apply select-none inline-flex items-center tracking-wide border border-s-[0.5px] border-on-primary justify-center; + @apply text-base leading-5 gap-x-2; + @apply px-3 py-1.5 w-[33px] h-[33px] bg-primary text-secondary; + + } + + .bcc-pagination-arrow-left { + @apply w-10 border rounded-l-md text-interactive; + } + + .bcc-pagination-arrow-right { + @apply w-10 border-s-[0.5px] rounded-r-md text-interactive; + } + + .bcc-pagination-button-icon { /* base size */ + @apply w-4 h-4; + } + +} diff --git a/design-library/src/components/BccPagination/BccPagination.spec.ts b/design-library/src/components/BccPagination/BccPagination.spec.ts new file mode 100644 index 00000000..f5616722 --- /dev/null +++ b/design-library/src/components/BccPagination/BccPagination.spec.ts @@ -0,0 +1,61 @@ +import { describe, it, expect } from "vitest"; + +import { mount } from "@vue/test-utils"; +import BccPagination from "./BccPagination.vue"; + +describe("BccPagination", () => { + it("renders the right amount of page numbers", async () => { + const wrapper = mount(BccPagination, { + props: { + total: 12, + rowsPerPage: 2, + }, + }); + + expect(wrapper.emitted("update:totalPages")![0]).toEqual([6]); + }); + it("renders an ellipsis on both sides", async () => { + const wrapper = mount(BccPagination, { + props: { + total: 16, + rowsPerPage: 1, + displayLeftEllipsis: true, + displayRightEllipsis: true, + }, + }); + + await wrapper.find(".bcc-pagination-arrow-right").trigger("click"); + await wrapper.find(".bcc-pagination-arrow-right").trigger("click"); + await wrapper.find(".bcc-pagination-arrow-right").trigger("click"); + + expect(wrapper.findAll(".bcc-pagination-elipsis").at(0)).toBeTruthy(); + expect(wrapper.findAll(".bcc-pagination-elipsis").at(1)).toBeTruthy(); + }); + it("updating the rowsPerPage updates the rendered page numbers", async () => { + const wrapper = mount(BccPagination, { + props: { + total: 16, + rowsPerPage: 1, + rowsPerPageOptions: [1, 3, 5, 7, 9], + }, + }); + + expect(wrapper.findAll(".bcc-pagination-button").at(4)?.html()).contains("16"); + + await wrapper.find(".bcc-select").setValue("3"); + + expect(wrapper.findAll(".bcc-pagination-button").at(4)?.html()).contains("6"); + + await wrapper.find(".bcc-select").setValue("5"); + + expect(wrapper.findAll(".bcc-pagination-button").at(4)?.html()).contains("4"); + + await wrapper.find(".bcc-select").setValue("7"); + + expect(wrapper.findAll(".bcc-pagination-button").at(3)?.html()).contains("3"); + + await wrapper.find(".bcc-select").setValue("9"); + + expect(wrapper.findAll(".bcc-pagination-button").at(2)?.html()).contains("2"); + }); +}); diff --git a/design-library/src/components/BccPagination/BccPagination.stories.ts b/design-library/src/components/BccPagination/BccPagination.stories.ts new file mode 100644 index 00000000..a836ca71 --- /dev/null +++ b/design-library/src/components/BccPagination/BccPagination.stories.ts @@ -0,0 +1,257 @@ +import BccPagination from "./BccPagination.vue"; +import BccTable from "../BccTable/BccTable.vue"; +import BccButton from "../BccButton/BccButton.vue"; +import BccBadge from "../BccBadge/BccBadge.vue"; +import { ChevronRightIcon } from "@bcc-code/icons-vue"; + +import type { Meta, StoryFn } from "@storybook/vue3"; +import { computed, ref } from "vue"; + +export default { + title: "Other/BccPagination", + component: BccPagination, + argTypes: { + align: { + options: ["left", "center", "right"], + control: { type: "radio" }, + }, + }, +} as Meta; + +const Template: StoryFn = (args) => ({ + components: { BccPagination }, + setup() { + return { args }; + }, + template: ` + + `, +}); + +export const Example = Template.bind({}); +Example.args = { + total: 100, + rowsPerPageOptions: [5, 10, 25, 50], + rowsPerPage: 5, + align: "right", + maxButtonsDisplayed: 3, + displayLeftEllipsis: true, + displayRightEllipsis: true, + displayRowsPerPage: true, +}; + +const TempTable: StoryFn = () => ({ + components: { BccPagination, BccTable, BccButton, BccBadge }, + setup() { + const columns = ref([ + { + text: "Year group", + key: "year_group", + sortable: false, + }, + { + text: "Name", + key: "name", + sortable: false, + }, + { + text: "Status", + key: "status", + sortable: false, + }, + { + text: "Progress", + key: "progress.amount", + sortable: false, + }, + { + text: "Actions", + key: "actions", + sortable: false, + }, + ]); + + const items = ref([ + { + id: 1, + year_group: "23/24", + name: "Ola Nordmann", + status: { text: "On Track", context: "success" }, + progress: { + amount: 2, + }, + }, + { + id: 2, + year_group: "22/23", + name: "Johan Oscar", + status: { text: "Finished", context: "info" }, + progress: { + amount: 25, + }, + }, + { + id: 3, + year_group: "22/23", + name: "Ada Lovelace", + status: { text: "Behind Schedule", context: "warning" }, + progress: { + amount: 15, + }, + }, + { + id: 4, + year_group: "23/24", + name: "Firmus Piett", + status: { text: "On Track", context: "success" }, + progress: { + amount: 15, + }, + }, + { + id: 5, + year_group: "22/23", + name: "John Pork", + status: { text: "On Track", context: "warning" }, + progress: { + amount: 45, + }, + }, + { + id: 6, + year_group: "23/24", + name: "John Doe", + status: { text: "Behind Schedule", context: "info" }, + progress: { + amount: 80, + }, + }, + { + id: 7, + year_group: "22/23", + name: "G Man", + status: { text: "Finished", context: "sucess" }, + progress: { + amount: 100, + }, + }, + { + id: 8, + year_group: "23/24", + name: "Jane Doe", + status: { text: "Finished", context: "info" }, + progress: { + amount: 37, + }, + }, + { + id: 9, + year_group: "23/24", + name: "Daphne Bennet", + status: { text: "Behind Schedule", context: "warning" }, + progress: { + amount: 45, + }, + }, + { + id: 10, + year_group: "22/24", + name: "Lorem Ipsum", + status: { text: "On Track", context: "warning" }, + progress: { + amount: 10, + }, + }, + { + id: 11, + year_group: "22/23", + name: "Nathaniel B", + status: { text: "On Track", context: "sucess" }, + progress: { + amount: 95, + }, + }, + { + id: 12, + year_group: "22/23", + name: "Nathaniel T", + status: { text: "On Track", context: "sucess" }, + progress: { + amount: 20, + }, + }, + ]); + + const page = ref(1); + const rowsPerPage = ref(4); + const start = computed(() => { + return (page.value - 1) * rowsPerPage.value; + }); + const end = computed(() => { + return start.value + rowsPerPage.value; + }); + const paginatedItems = computed(() => items.value.slice(start.value, end.value)); + + return { paginatedItems, page, columns, rowsPerPage, items, ChevronRightIcon }; + }, + template: ` + + + + + + `, +}); +export const Table = TempTable.bind({}); + +const TempAlign: StoryFn = (args) => ({ + components: { BccPagination }, + setup() { + return { args }; + }, + template: ` +
+ + + +
+ `, +}); + +export const Align = TempAlign.bind({}); + +const TempElipsis: StoryFn = (args) => ({ + components: { BccPagination }, + setup() { + return { args }; + }, + template: ` +
+ + + +
+ `, +}); + +export const Elipsis = TempElipsis.bind({}); + +const TempMaxButtons: StoryFn = (args) => ({ + components: { BccPagination }, + setup() { + return { args }; + }, + template: ` +
+ + + +
+ `, +}); + +export const MaxButtons = TempMaxButtons.bind({}); diff --git a/design-library/src/components/BccPagination/BccPagination.vue b/design-library/src/components/BccPagination/BccPagination.vue new file mode 100644 index 00000000..0223e053 --- /dev/null +++ b/design-library/src/components/BccPagination/BccPagination.vue @@ -0,0 +1,154 @@ + + + diff --git a/design-library/src/css/index.css b/design-library/src/css/index.css index 989cad87..429d60eb 100644 --- a/design-library/src/css/index.css +++ b/design-library/src/css/index.css @@ -33,3 +33,4 @@ @import "../components/BccStepper/BccStepper.css"; @import "../components/BccTooltip/BccTooltip.css"; @import "../components/BccAccordion/BccAccordion.css"; +@import "../components/BccPagination/BccPagination.css"; diff --git a/design-library/src/index.ts b/design-library/src/index.ts index 76b65124..a7d22e6d 100644 --- a/design-library/src/index.ts +++ b/design-library/src/index.ts @@ -40,3 +40,4 @@ export { default as BccLinkItem } from "./components/BccLinkItem/BccLinkItem.vue export { default as BccStepper } from "./components/BccStepper/BccStepper.vue"; export { default as BccTooltip } from "./components/BccTooltip/BccTooltip.vue"; export { default as BccAccordion } from "./components/BccAccordion/BccAccordion.vue"; +export { default as BccPagination } from "./components/BccPagination/BccPagination.vue";