import type { Meta, StoryObj } from '@storybook/vue3'
import SyServerTable from './SyServerTable.vue'
import { StateEnum } from '../common/constants/StateEnum'
import type { DataOptions, FilterType } from '../common/types'
import { computed, defineComponent, ref, watch } from 'vue'
import type { VDataTable } from 'vuetify/components'
import dayjs from 'dayjs'
import { fn } from '@storybook/test'
import { mdiChevronDown, mdiChevronUp } from '@mdi/js'
interface User {
[key: string]: string
firstname: string
lastname: string
email: string
}
interface DataObj {
items: User[]
total: number
}
const meta = {
title: 'Composants/Tableaux/SyServerTable',
component: SyServerTable,
decorators: [
() => ({
template: '
',
}),
],
parameters: {
layout: 'fullscreen',
},
argTypes: {
'headers': {
description: 'Liste des colonnes du tableau (voir : https://vuetifyjs.com/en/api/v-data-table/#props-headers)',
control: { type: 'object' },
table: {
category: 'props',
},
},
'items': {
description: 'Liste des éléments à afficher dans le tableau',
control: { type: 'object' },
table: {
category: 'props',
defaultValue: {
summary: 'undefined',
},
},
},
'density': {
description: 'Définit la densité du tableau',
control: { type: 'select' },
options: ['default', 'comfortable', 'compact'],
table: {
category: 'props',
type: { summary: 'string', detail: `'default' | 'comfortable' | 'compact'` },
},
},
'striped': {
description: 'Affiche les lignes du tableau avec un fond rayé',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
},
},
'options': {
description: 'Options de configuration du tableau',
name: 'v-model:options',
control: { type: 'object' },
table: {
category: 'props',
type: { summary: 'DataOptions', detail: '{ page: number, itemsPerPage: number, sortBy: SortOptions[], groupBy?: SortOptions[], multiSort?: boolean, mustSort?: boolean, filters?: FilterOption[] }' },
},
},
'itemsPerPageOptions': {
description: 'Limite les options disponibles dans le sélecteur "itemsPerPage"',
control: { type: 'object' },
table: {
category: 'props',
type: { summary: 'number[]' },
defaultValue: { summary: 'undefined' },
},
},
'serverItemsLength': {
description: 'Nombre total d\'éléments à afficher',
control: { type: 'number' },
},
'saveState': {
description: 'Permet d\'activer ou non la sauvegarde des options (pagination, tris, ordre des colonnes) du tableau dans le localStorage. Par défaut, cette fonctionnalité est activée.',
control: { type: 'boolean' },
},
'suffix': {
description: 'Suffixe permettant de gérer individuellement le stockage des options d\'un tableau d\'une page à l\'autre. Ce prop est obligatoire pour garantir un stockage unique pour chaque tableau.',
control: { type: 'text' },
table: {
category: 'props',
type: { summary: 'string' },
},
required: true,
},
'caption': {
description: 'Texte de la légende du tableau',
control: { type: 'text' },
},
'showExpand': {
description: 'Affiche une colonne permettant d\'étendre les lignes pour afficher du contenu supplémentaire',
control: { type: 'boolean' },
table: {
category: 'props',
},
},
'resizableColumns': {
description: 'Permet de redimensionner les colonnes du tableau',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
},
},
'multiSort': {
description: 'Permet de trier sur plusieurs colonnes simultanément. Lorsque activé, des indicateurs numériques apparaissent à côté des icônes de tri pour montrer l\'ordre de priorité.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: {
summary: 'false',
},
},
},
'mustSort': {
description: 'Force au moins une colonne à être toujours triée. Si désactivé, toutes les colonnes peuvent être non triées.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: {
summary: 'false',
},
},
},
'enableColumnControls': {
description: 'Allow the users to re-organize the columns',
table: {
defaultValue: {
summary: 'false',
},
type: { summary: 'boolean' },
category: 'props',
},
control: { type: 'boolean' },
},
'showSelect': {
description: 'Affiche des cases à cocher pour sélectionner des lignes',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
},
},
'showSelectSingle': {
description: 'Affiche des cases à cocher pour sélectionner une seule ligne à la fois',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
},
},
'stickySelect': {
description: 'Rend la colonne de sélection (cases à cocher) sticky à gauche quand showSelect ou showSelectSingle est activé.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: {
summary: 'false',
},
},
},
'pinnedColumns': {
description: 'Liste des colonnes à épingler (sticky). Chaque entrée peut être une clé de colonne (string) ou un objet `{ key: string, side?: \'left\' | \'right\' }`. Par défaut, les colonnes sont épinglées à gauche.',
control: { type: 'object' },
table: {
category: 'props',
type: { summary: 'Array' },
defaultValue: { summary: 'undefined' },
},
},
'pinnedColumnKey': {
description: 'Raccourci pour épingler une seule colonne à gauche. Équivalent à `pinnedColumns: [key]`. Ignoré si `pinnedColumns` est défini.',
control: { type: 'text' },
table: {
category: 'props',
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
},
},
'selectionKey': {
description: 'Clé utilisée pour identifier chaque ligne lors de la sélection. Par défaut, utilise "id" si présent, sinon l\'objet complet.',
control: { type: 'text' },
table: {
category: 'props',
type: { summary: 'string' },
defaultValue: { summary: 'undefined (fallback: id | objet complet)' },
},
},
'clickableRow': {
description: 'Rend chaque ligne cliquable. Quand cette prop est activée, la ligne devient focusable au clavier et émet `row-click` sur clic, `Entrée` ou `Espace`, sans interférer avec les éléments interactifs imbriqués.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
'pageInput': {
description: 'Affiche un champ de saisie numérique dans la pagination permettant de naviguer directement vers une page en la saisissant au clavier. La navigation est déclenchée à la validation (`Entrée`) ou à la perte de focus. La valeur est automatiquement clampée entre 1 et le nombre total de pages.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
'hideDefaultFooter': {
description: 'Masque le footer par défaut du tableau (pagination et contrôles de page). Utile lorsque l\'on souhaite gérer la pagination manuellement ou ne pas en afficher.',
control: { type: 'boolean' },
table: {
category: 'props',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - 'cookie-description-${cookieName}' storybook can't infer dynamic slot name
'header.': {
description: 'Slot permettant de personnaliser le rendu de l\'en-tête d\'une colonne spécifique. Remplacer `` par la clé de la colonne souhaitée.',
control: undefined,
table: {
category: 'slots',
type: {
summary: 'slot',
detail: `{
column: HeaderColumn,
headers: HeaderColumn[][],
columns: HeaderColumn[],
locales: Record string)>,
sortBy: DataOptions['sortBy'],
someSelected: boolean,
allSelected: boolean
}`,
},
},
},
'onRow-click': {
description: 'Émis lorsqu\'une ligne est activée alors que `clickableRow` est à `true`. Reçoit l\'objet de la ligne en paramètre. Les interactions avec des éléments déjà interactifs dans la ligne ne déclenchent pas cet événement.',
table: {
category: 'events',
type: { summary: '(item: Record) => void' },
},
},
},
} satisfies Meta
export default meta
type Story = StoryObj
export const Default: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ServerSortBy: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'desc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-sort',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ServerMultiSort: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Cet exemple montre le tri multiple côté serveur avec des indicateurs d'ordre de priorité.
Les chiffres à côté des icônes de tri indiquent l'ordre de priorité du tri.
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
multiSort: true,
sortBy: [
{
key: 'lastname',
order: 'desc',
},
{
key: 'firstname',
order: 'asc',
},
],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'suffix': 'server-sort',
'density': 'default',
'striped': false,
'multiSort': true,
'serverItemsLength': 7,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items.sort((a, b) => {
for (const sort of sortBy) {
const key = sort.key
const r = String(a[key]).localeCompare(String(b[key]))
const order = sort.order === 'asc' ? 1 : -1
if (r !== 0) {
return r * order
}
}
return 0
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{
firstname: 'Virginie',
lastname: 'Beauchesne',
email: 'virginie.beauchesne@example.com',
},
{
firstname: 'Simone',
lastname: 'Bellefeuille',
email: 'simone.bellefeuille@example.com',
},
{
firstname: 'Étienne',
lastname: 'Salois',
email: 'etienne.salois@example.com',
},
{
firstname: 'Thierry',
lastname: 'Bobu',
email: 'thierry.bobu@example.com',
},
{
firstname: 'Bernadette',
lastname: 'Langelier',
email: 'bernadette.langelier@exemple.com',
},
{
firstname: 'Agate',
lastname: 'Roy',
email: 'agate.roy@exemple.com',
},
{
firstname: 'Agate',
lastname: 'Beauchesne',
email: 'agate.beauchesne@exemple.com',
},
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
Cet exemple montre le tri multiple côté serveur avec des indicateurs d'ordre de priorité.
Les chiffres à côté des icônes de tri indiquent l'ordre de priorité du tri.
`,
}
},
}
export const ServerFilterByText: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'serverItemsLength': 15,
'headers': [
{
title: 'Prénom',
key: 'firstname',
filterable: true,
filterType: 'text',
},
{
title: 'Nom',
key: 'lastname',
filterable: true,
filterType: 'text',
},
{
title: 'Email',
key: 'email',
filterable: true,
filterType: 'text',
},
],
'caption': '',
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'showFilters': true,
'suffix': 'server-filter-text',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalFilteredUsers = ref(0)
const filteredUsers = ref[]>([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Get all users
let items = [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
{ firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
{ firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
{ firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
{ firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
]
// Apply filters on server side
if (options.value?.filters && options.value.filters.length > 0) {
options.value.filters.forEach((filter) => {
const { key, value } = filter
items = items.filter((item) => {
const itemValue = item[key]
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
})
})
}
const total = items.length
// Apply pagination
const { page = 1, itemsPerPage = 10 } = options.value || {}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
filteredUsers.value = items as Record[]
totalFilteredUsers.value = total
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
state,
options,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterByNumber: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text',
},
{
title: 'Âge',
key: 'age',
filterable: true,
filterType: 'number',
},
{
title: 'Salaire',
key: 'salary',
filterable: true,
filterType: 'number',
},
],
'caption': '',
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'serverItemsLength': 15,
'showFilters': true,
'suffix': 'server-filter-number',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalFilteredUsers = ref(0)
const filteredUsers = ref[]>([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Get all users
let items = [
{ name: 'Jean Dupont', age: 32, salary: 45000 },
{ name: 'Marie Martin', age: 28, salary: 52000 },
{ name: 'Pierre Durand', age: 45, salary: 65000 },
{ name: 'Sophie Petit', age: 36, salary: 48000 },
{ name: 'Thomas Leroy', age: 41, salary: 58000 },
{ name: 'Julie Bernard', age: 29, salary: 47000 },
{ name: 'Nicolas Moreau', age: 38, salary: 61000 },
{ name: 'Camille Dubois', age: 33, salary: 49000 },
{ name: 'Alexandre Lefebvre', age: 44, salary: 67000 },
{ name: 'Émilie Girard', age: 31, salary: 51000 },
{ name: 'Lucas Roux', age: 39, salary: 59000 },
{ name: 'Chloé Lambert', age: 27, salary: 46000 },
{ name: 'Maxime Simon', age: 42, salary: 63000 },
{ name: 'Laura Fournier', age: 35, salary: 54000 },
{ name: 'Antoine Mercier', age: 40, salary: 60000 },
]
// Apply filters on server side
if (options.value?.filters && options.value.filters.length > 0) {
options.value.filters.forEach((filter) => {
const { key, value, type } = filter
items = items.filter((item) => {
const itemValue = item[key]
if (type === 'number') {
return Number(itemValue) === Number(value)
}
else {
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
}
})
})
}
const total = items.length
// Apply pagination
const { page = 1, itemsPerPage = 10 } = options.value || {}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
filteredUsers.value = items as Record[]
totalFilteredUsers.value = total
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterBySelect: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text',
},
{
title: 'Département',
key: 'department',
filterable: true,
filterType: 'select',
multiple: false,
chips: false,
hideMessages: true,
filterOptions: [
{ text: 'RH', value: 'RH' },
{ text: 'IT', value: 'IT' },
{ text: 'Finance', value: 'Finance' },
{ text: 'Marketing', value: 'Marketing' },
],
},
{
title: 'Statut',
key: 'status',
filterable: true,
filterType: 'select',
multiple: false,
chips: false,
hideMessages: true,
filterOptions: [
{ text: 'Actif', value: 'Actif' },
{ text: 'En congé', value: 'En congé' },
{ text: 'Inactif', value: 'Inactif' },
],
},
],
'caption': '',
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'serverItemsLength': 15,
'showFilters': true,
'suffix': 'server-filter-select',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalFilteredUsers = ref(0)
const filteredUsers = ref[]>([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Get all users
let items = [
{ name: 'Jean Dupont', department: 'RH', status: 'Actif' },
{ name: 'Marie Martin', department: 'IT', status: 'En congé' },
{ name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
{ name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
{ name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
{ name: 'Julie Bernard', department: 'RH', status: 'Actif' },
{ name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
{ name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
{ name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
{ name: 'Émilie Girard', department: 'RH', status: 'En congé' },
{ name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
{ name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
{ name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
{ name: 'Laura Fournier', department: 'RH', status: 'Actif' },
{ name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
]
// Apply filters on server side
if (options.value?.filters && options.value.filters.length > 0) {
options.value.filters.forEach((filter) => {
const { key, value, type } = filter
items = items.filter((item) => {
const itemValue = item[key]
if (type === 'select') {
return itemValue === value
}
else {
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
}
})
})
}
const total = items.length
// Apply pagination
const { page = 1, itemsPerPage = 10 } = options.value || {}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
filteredUsers.value = items as Record[]
totalFilteredUsers.value = total
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterBySelectMultiple: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text',
},
{
title: 'Département',
key: 'department',
filterable: true,
filterType: 'select',
multiple: true,
chips: true,
hideMessages: true,
filterOptions: [
{ text: 'RH', value: 'RH' },
{ text: 'IT', value: 'IT' },
{ text: 'Finance', value: 'Finance' },
{ text: 'Marketing', value: 'Marketing' },
],
},
{
title: 'Statut',
key: 'status',
filterable: true,
filterType: 'select',
multiple: true,
chips: true,
hideMessages: true,
filterOptions: [
{ text: 'Actif', value: 'Actif' },
{ text: 'En congé', value: 'En congé' },
{ text: 'Inactif', value: 'Inactif' },
],
},
],
'caption': '',
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'serverItemsLength': 15,
'showFilters': true,
'suffix': 'server-filter-select',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalFilteredUsers = ref(0)
const filteredUsers = ref[]>([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Get all users
let items = [
{ name: 'Jean Dupont', department: 'RH', status: 'Actif' },
{ name: 'Marie Martin', department: 'IT', status: 'En congé' },
{ name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
{ name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
{ name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
{ name: 'Julie Bernard', department: 'RH', status: 'Actif' },
{ name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
{ name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
{ name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
{ name: 'Émilie Girard', department: 'RH', status: 'En congé' },
{ name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
{ name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
{ name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
{ name: 'Laura Fournier', department: 'RH', status: 'Actif' },
{ name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
]
// Apply filters on server side
if (options.value?.filters && options.value.filters.length > 0) {
options.value.filters.forEach((filter) => {
const { key, value, type } = filter
items = items.filter((item) => {
const itemValue = item[key]
if (type === 'select') {
if (Array.isArray(value)) {
// Empty array means no filter applied
if (value.length === 0) return true
// Check if item value is in the selected values
return value.includes(itemValue)
}
else {
return itemValue === value
}
}
else {
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
}
})
})
}
const total = items.length
// Apply pagination
const { page = 1, itemsPerPage = 10 } = options.value || {}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
filteredUsers.value = items as Record[]
totalFilteredUsers.value = total
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterByAutocomplete: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text' as FilterType,
},
{
title: 'Département',
key: 'department',
filterable: true,
filterType: 'autocomplete' as FilterType,
filterOptions: [
{ text: 'RH', value: 'RH' },
{ text: 'IT', value: 'IT' },
{ text: 'Finance', value: 'Finance' },
{ text: 'Marketing', value: 'Marketing' },
],
},
{
title: 'Statut',
key: 'status',
filterable: true,
filterType: 'autocomplete' as FilterType,
multiple: true,
chips: true,
filterOptions: [
{ text: 'Actif', value: 'Actif' },
{ text: 'En congé', value: 'En congé' },
{ text: 'Inactif', value: 'Inactif' },
],
},
],
'serverItemsLength': 0,
'suffix': 'server-filter-autocomplete',
'showFilters': true,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const options = ref({
itemsPerPage: 5,
page: 1,
sortBy: [],
filters: [],
})
const filteredUsers = ref[]>([])
const totalFilteredUsers = ref(0)
const state = ref(StateEnum.IDLE)
const getUsers = () => [
{ name: 'Jean Dupont', department: 'RH', status: 'Actif' },
{ name: 'Marie Martin', department: 'IT', status: 'En congé' },
{ name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
{ name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
{ name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
]
const fetchData = async () => {
state.value = StateEnum.PENDING
await new Promise(resolve => setTimeout(resolve, 500))
let items = getUsers()
if (options.value.filters?.length) {
options.value.filters.forEach((filter) => {
const { key, value, type } = filter
items = items.filter((item) => {
const itemValue = item[key as keyof typeof item]
if (type === 'autocomplete') {
if (Array.isArray(value)) {
return value.length === 0 || value.includes(itemValue)
}
return itemValue === value
}
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
})
})
}
totalFilteredUsers.value = items.length
const { page = 1, itemsPerPage = 5 } = options.value
filteredUsers.value = itemsPerPage > 0
? items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
: items
state.value = StateEnum.RESOLVED
}
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterByExacteDate: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'serverItemsLength': 5,
'showFilters': true,
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text',
},
{
title: 'Date d\'embauche',
key: 'hireDate',
filterable: true,
filterType: 'date',
dateFormat: 'DD/MM/YYYY',
},
],
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'caption': '',
'suffix': 'server-filter-date',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
// Original data that never changes
const originalUsers = [
{
name: 'Jean-Pierre Dubois',
hireDate: dayjs('2025-05-15').format('DD/MM/YYYY'),
},
{
name: 'Marie-Claire Lefèvre',
hireDate: dayjs('2025-03-10').format('DD/MM/YYYY'),
},
{
name: 'François Moreau',
hireDate: dayjs('2025-11-22').format('DD/MM/YYYY'),
},
{
name: 'Céline Rousseau',
hireDate: dayjs('2025-01-08').format('DD/MM/YYYY'),
},
{
name: 'Thierry Bertrand',
hireDate: dayjs('2025-07-30').format('DD/MM/YYYY'),
},
]
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalUsers = ref(originalUsers.length)
const users = ref([...originalUsers])
const state = ref(StateEnum.IDLE)
const fetchData = async () => {
state.value = StateEnum.PENDING
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000))
// Start with original data
let filteredData = [...originalUsers]
// Apply filters if any
if (options.value.filters && options.value.filters.length > 0) {
filteredData = filteredData.filter((user) => {
return options.value.filters!.every((filter) => {
const { key, value, type } = filter
const itemValue = user[key as keyof typeof user]
if (!value) return true
if (type === 'date') {
// Simple date comparison for demo purposes
return itemValue === value
}
else if (type === 'period') {
const filter = value as { from: string, to: string }
const start = new Date(filter.from)
const end = new Date(filter.to)
const itemDate = new Date(itemValue)
if (itemDate) {
if (end && itemDate < end) {
return false
}
if (start && itemDate > start) {
return false
}
}
return true
}
else {
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
}
})
})
}
// Update the displayed data
users.value = filteredData
totalUsers.value = filteredData.length
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
users,
totalUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ServerFilterByPeriod: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'serverItemsLength': 5,
'showFilters': true,
'headers': [
{
title: 'Nom',
key: 'name',
filterable: true,
filterType: 'text',
},
{
title: 'Date d\'embauche',
key: 'hireDate',
filterable: true,
filterType: 'period',
dateFormat: 'DD/MM/YYYY',
},
],
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'caption': '',
'suffix': 'server-filter-date',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
// Original data that never changes
const originalUsers = [
{
name: 'Jean-Pierre Dubois',
hireDate: dayjs('2025-05-15').format('DD/MM/YYYY'),
},
{
name: 'Marie-Claire Lefèvre',
hireDate: dayjs('2025-03-10').format('DD/MM/YYYY'),
},
{
name: 'François Moreau',
hireDate: dayjs('2025-11-22').format('DD/MM/YYYY'),
},
{
name: 'Céline Rousseau',
hireDate: dayjs('2025-01-08').format('DD/MM/YYYY'),
},
{
name: 'Thierry Bertrand',
hireDate: dayjs('2025-07-30').format('DD/MM/YYYY'),
},
]
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalUsers = ref(originalUsers.length)
const users = ref([...originalUsers])
const state = ref(StateEnum.IDLE)
const fetchData = async () => {
state.value = StateEnum.PENDING
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000))
// Start with original data
let filteredData = [...originalUsers]
// Apply filters if any
if (options.value.filters && options.value.filters.length > 0) {
filteredData = filteredData.filter((user) => {
return options.value.filters!.every((filter) => {
const { key, value, type } = filter
const itemValue = user[key as keyof typeof user]
if (!value) return true
else if (type === 'period') {
const formatDate = (date: string): Date | null => {
if (!date) return null
const parsedDate = dayjs(date, 'DD/MM/YYYY')
return parsedDate.isValid() ? parsedDate.toDate() : null
}
const filter = value as { from: string, to: string }
const start = formatDate(filter.from)
const end = formatDate(filter.to)
const itemDate = formatDate(itemValue)
if (itemDate) {
if (end && itemDate > end) {
return false
}
if (start && itemDate < start) {
return false
}
}
return true
}
else {
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
}
})
})
}
// Update the displayed data
users.value = filteredData
totalUsers.value = filteredData.length
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
users,
totalUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const CustomFilterSlot: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Filtre personnalisé :
{
// Utiliser la fonction updateFilter fournie par le slot
updateFilter(val)
}"
/>
`,
},
{
name: 'Script',
code: `
`,
},
{
name: 'Style',
code: `
`,
},
],
},
args: {
'serverItemsLength': 6,
'headers': [
{
title: 'Nom',
key: 'lastname',
filterable: true,
filterType: 'text',
},
{
title: 'Prénom',
key: 'firstname',
filterable: true,
filterType: 'text',
},
{
title: 'Statut',
key: 'status',
filterable: true,
filterType: 'custom' as FilterType,
},
],
'items': [
{
firstname: 'Virginie',
lastname: 'Beauchesne',
status: 'Actif',
},
{
firstname: 'Simone',
lastname: 'Bellefeuille',
status: 'Inactif',
},
{
firstname: 'Étienne',
lastname: 'Salois',
status: 'En attente',
},
{
firstname: 'Thierry',
lastname: 'Bobu',
status: 'Actif',
},
{
firstname: 'Bernadette',
lastname: 'Langelier',
status: 'Inactif',
},
{
firstname: 'Agate',
lastname: 'Roy',
status: 'En attente',
},
],
'caption': '',
'options': {
itemsPerPage: 4,
page: 1,
filters: [],
},
'showFilters': true,
'suffix': 'server-custom-filter-slot',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
// Create reactive references
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const items = ref(args.items)
const customFilterValue = ref('')
const statusOptions = ['Actif', 'Inactif', 'En attente']
const loading = ref(false)
const serverItemsLength = ref(args.serverItemsLength)
// Fonction pour simuler une requête API avec filtrage côté serveur
const fetchData = async () => {
loading.value = true
// Simuler un délai réseau
await new Promise(resolve => setTimeout(resolve, 300))
// Récupérer les filtres
const filters = options.value?.filters || []
// Filtrer les éléments côté "serveur"
let filteredItems = [...(args.items || [])]
for (const filter of filters) {
if (filter.type === 'text') {
filteredItems = filteredItems.filter(item =>
String(item[filter.key]).toLowerCase().includes(String(filter.value).toLowerCase()),
)
}
else if (filter.type === 'select' || filter.type === 'custom') {
// Traiter les filtres de type 'select' et 'custom' de la même manière
filteredItems = filteredItems.filter(item =>
item[filter.key] === filter.value,
)
}
}
// Mettre à jour le nombre total d'éléments
serverItemsLength.value = filteredItems.length
// Appliquer la pagination
const page = options.value?.page || 1
const itemsPerPage = options.value?.itemsPerPage || 4
const start = (page - 1) * itemsPerPage
const end = start + itemsPerPage
items.value = filteredItems.slice(start, end)
loading.value = false
}
function handleFilterChange(val) {
// Ensure options.value.filters is initialized
if (!options.value.filters) {
options.value.filters = []
}
// Create a new filters array with proper typing
const currentFilters = options.value.filters as import('../common/types').FilterOption[]
const newFilters = [...currentFilters].filter(f => f.key !== 'status')
// Add the new filter if a value is selected
if (val) {
newFilters.push({
key: 'status',
value: val,
type: 'select' as FilterType, // Use 'select' type for compatibility with filtering logic
})
}
// Update the options with the new filters
options.value = {
...options.value,
filters: newFilters,
}
}
// Initialize data
fetchData()
return {
args,
options,
items,
customFilterValue,
statusOptions,
loading,
serverItemsLength,
handleFilterChange,
fetchData,
}
},
template: `
Filtre personnalisé :
{
// Use updateFilter provided by the slot props
updateFilter(val);
// Also update our local state
handleFilterChange(val);
}"
/>
`,
}
},
}
export const CustomFilterInputs: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'serverItemsLength': 15,
'headers': [
{
title: 'Prénom',
key: 'firstname',
filterable: true,
filterType: 'text',
},
{
title: 'Nom',
key: 'lastname',
filterable: true,
filterType: 'text',
},
{
title: 'Email',
key: 'email',
filterable: true,
filterType: 'text',
},
],
'caption': '',
'options': {
itemsPerPage: 5,
page: 1,
filters: [],
},
'filterInputConfig': {
variant: 'outlined',
density: 'comfortable',
hideDetails: true,
clearable: false,
disableErrorHandling: true,
},
'showFilters': true,
'suffix': 'server-filter-text',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const totalFilteredUsers = ref(0)
const filteredUsers = ref[]>([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
// Get all users
let items = [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
{ firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
{ firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
{ firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
{ firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
]
// Apply filters on server side
if (options.value?.filters && options.value.filters.length > 0) {
options.value.filters.forEach((filter) => {
const { key, value } = filter
items = items.filter((item) => {
const itemValue = item[key]
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
})
})
}
const total = items.length
// Apply pagination
const { page = 1, itemsPerPage = 10 } = options.value || {}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
filteredUsers.value = items as Record[]
totalFilteredUsers.value = total
state.value = StateEnum.RESOLVED
}
// Initialize data
fetchData()
return {
args,
filteredUsers,
totalFilteredUsers,
options,
state,
fetchData,
StateEnum,
}
},
template: `
`,
}
},
}
export const ManyServerTables: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'serverItemsLength': 15, // Add required serverItemsLength property
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'suffix': 'multi',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
// Table 1
const totalUsersTable1 = ref(0)
const usersTable1 = ref([])
const stateTable1 = ref(StateEnum.IDLE)
const optionsTable1 = ref>({
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
})
const fetchDataTable1 = async (options?: DataOptions): Promise => {
const optionsToUse = options || optionsTable1.value as DataOptions
const { items, total } = await getDataFromApi(optionsToUse)
usersTable1.value = items
totalUsersTable1.value = total
}
// Table 2
const totalUsersTable2 = ref(0)
const usersTable2 = ref([])
const stateTable2 = ref(StateEnum.IDLE)
const optionsTable2 = ref>({
itemsPerPage: 3,
sortBy: [{ key: 'firstname', order: 'asc' }],
page: 1,
})
const fetchDataTable2 = async (options?: DataOptions): Promise => {
const optionsToUse = options || optionsTable2.value as DataOptions
const { items, total } = await getDataFromApi(optionsToUse)
usersTable2.value = items
totalUsersTable2.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
const state = sortBy[0]!.key === 'lastname' ? stateTable1 : stateTable2
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
]
}
// Chargement initial des données
fetchDataTable1()
fetchDataTable2()
return {
args,
usersTable1,
totalUsersTable1,
optionsTable1,
stateTable1,
fetchDataTable1,
usersTable2,
totalUsersTable2,
optionsTable2,
stateTable2,
fetchDataTable2,
StateEnum,
}
},
template: `
`,
}
},
}
export const DataAlignment: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{
title: 'ID',
key: 'id',
align: 'center',
},
{
title: 'Nom',
key: 'lastname',
align: 'start',
},
{
title: 'Date de naissance',
key: 'birthdate',
align: 'center',
},
{
title: 'NIR',
key: 'nir',
align: 'end',
},
],
'caption': '',
'serverItemsLength': 3,
'suffix': 'server-resizable-columns',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
// @ts-expect-error - fetchData is not defined
const { items, total } = await getDataFromApi(options.value)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions) => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = () => {
return [
{
id: '1',
lastname: 'Lefebvre',
birthdate: '18/02/1989',
nir: '1 89 02 75 120 005 79',
},
{
id: '2',
lastname: 'Richard',
birthdate: '22/05/1991',
nir: '2 91 05 75 120 005 76',
},
{
id: '3',
lastname: 'Fournier',
birthdate: '11/11/2000',
nir: '2 00 11 42 120 008 87',
},
]
}
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ResizableColumns: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-resizable-columns',
'density': 'default',
'striped': false,
'resizableColumns': true,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ClickableRow: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Ligne cliquée
Nom: {{ selectedRow.lastname }}
Prénom: {{ selectedRow.firstname }}
Email: {{ selectedRow.email }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'items': [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Alice', lastname: 'Dupont', email: 'alice.dupont@example.com' },
{ firstname: 'Marc', lastname: 'Lefevre', email: 'marc.lefevre@example.com' },
],
'serverItemsLength': 4,
'options': { itemsPerPage: 5, filters: [] },
'clickableRow': true,
'suffix': 'clickable-row-server-table',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
'onRow-click': fn(),
},
render: (args) => {
return {
components: {
ClickableRowServerTableCanvas: defineComponent({
components: { SyServerTable },
emits: ['row-click'],
setup() {
const options = ref({
itemsPerPage: 5,
page: 1,
sortBy: [],
filters: [],
...(args.options ?? {}),
})
const state = ref(StateEnum.IDLE)
const totalUsers = ref(0)
const users = ref[]>([])
const allUsers = [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Alice', lastname: 'Dupont', email: 'alice.dupont@example.com' },
{ firstname: 'Marc', lastname: 'Lefevre', email: 'marc.lefevre@example.com' },
]
const boundArgs = computed(() => {
return Object.fromEntries(
Object.entries(args).filter(([key]) => !['items', 'options', 'serverItemsLength', 'onRow-click'].includes(key)),
)
})
const fetchData = async (nextOptions?: DataOptions) => {
if (nextOptions) {
options.value = { ...options.value, ...nextOptions }
}
state.value = StateEnum.PENDING
await new Promise(resolve => setTimeout(resolve, 500))
const items = [...allUsers]
const { page = 1, itemsPerPage = 5, sortBy = [] } = options.value
if (sortBy.length > 0) {
const [firstSort] = sortBy
if (firstSort?.key && firstSort.order) {
items.sort((a, b) => {
const left = String(a[firstSort.key] ?? '')
const right = String(b[firstSort.key] ?? '')
return firstSort.order === 'asc'
? left.localeCompare(right)
: right.localeCompare(left)
})
}
}
totalUsers.value = items.length
users.value = itemsPerPage > 0
? items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
: items
state.value = StateEnum.RESOLVED
}
fetchData()
return { boundArgs, fetchData, options, state, totalUsers, users, StateEnum }
},
template: `
`,
}),
},
setup() {
const selectedRow = ref | null>(null)
const handleRowClick = (item: Record) => {
selectedRow.value = item
args['onRow-click']?.(item)
}
return { selectedRow, handleRowClick }
},
template: `
Ligne cliquée
Nom: {{ selectedRow.lastname }}
Prénom: {{ selectedRow.firstname }}
Email: {{ selectedRow.email }}
`,
}
},
}
export const RowSelection: Story = {
name: 'Row Selection',
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Item(s) sélectionné(s) ({{ selection.length }})
Nom: {{ typeof item === 'object' ? item.lastname : allUsers.find(i => JSON.stringify(i) === item)?.lastname }}
Prénom: {{ typeof item === 'object' ? item.firstname : allUsers.find(i => JSON.stringify(i) === item)?.firstname }}
Email: {{ typeof item === 'object' ? item.email : allUsers.find(i => JSON.stringify(i) === item)?.email }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'lastname',
},
{
title: 'Prénom',
key: 'firstname',
},
{
title: 'Email',
value: 'email',
},
],
'items': [
{
firstname: 'Virginie',
lastname: 'Beauchesne',
email: 'virginie.beauchesne@example.com',
},
{
firstname: 'Simone',
lastname: 'Bellefeuille',
email: 'simone.bellefeuille@example.com',
},
{
firstname: 'Étienne',
lastname: 'Salois',
email: 'etienne.salois@example.com',
},
{
firstname: 'Thierry',
lastname: 'Bobu',
email: 'thierry.bobu@example.com',
},
{
firstname: 'Bernadette',
lastname: 'Langelier',
email: 'bernadette.langelier@exemple.com',
},
{
firstname: 'Agate',
lastname: 'Roy',
email: 'agate.roy@exemple.com',
},
],
'options': {
itemsPerPage: 4,
page: 1,
filters: [],
},
'caption': '',
'suffix': 'selection-server-table',
'density': 'default',
'striped': false,
'showSelect': true,
'showFilters': true,
'serverItemsLength': 6,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const selection = ref([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
// Create a complete DataOptions object with all required properties
const defaultOptions: DataOptions = {
page: 1,
itemsPerPage: 10,
sortBy: [],
multiSort: false,
}
const options = args.options ? { ...defaultOptions, ...args.options } : defaultOptions
const { items, total } = await getDataFromApi(options)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
let total = items.length // Changed from const to let
// Add filtering logic here
if (filters && filters.length > 0) {
filters.forEach((filter) => {
const { key, value } = filter
items = items.filter((item) => {
const itemValue = item[key]
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
})
})
// Update total after filtering
total = items.length
}
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Thierry', lastname: 'Bobu', email: 'thierry.bobu@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@exemple.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@exemple.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
]
}
// Keep a full copy of all users for selection display across pages
const allUsers = ref(getUsers())
// Call fetchData on mount
fetchData()
return { args, users, allUsers, state, fetchData, totalUsers, selection, StateEnum }
},
template: `
Item(s) sélectionné(s) ({{ selection.length }})
Nom: {{ typeof item === 'object' ? item.lastname : allUsers.find(i => JSON.stringify(i) === item)?.lastname }}
Prénom: {{ typeof item === 'object' ? item.firstname : allUsers.find(i => JSON.stringify(i) === item)?.firstname }}
Email: {{ typeof item === 'object' ? item.email : allUsers.find(i => JSON.stringify(i) === item)?.email }}
`,
}
},
}
export const SingleRowSelection: Story = {
name: 'Single Row Selection',
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Item(s) sélectionné(s) ({{ selection.length }})
Nom: {{ typeof item === 'object' ? item.lastname : allUsers.find(i => JSON.stringify(i) === item)?.lastname }}
Prénom: {{ typeof item === 'object' ? item.firstname : allUsers.find(i => JSON.stringify(i) === item)?.firstname }}
Email: {{ typeof item === 'object' ? item.email : allUsers.find(i => JSON.stringify(i) === item)?.email }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'headers': [
{
title: 'Nom',
key: 'lastname',
},
{
title: 'Prénom',
key: 'firstname',
},
{
title: 'Email',
value: 'email',
},
],
'items': [
{
firstname: 'Virginie',
lastname: 'Beauchesne',
email: 'virginie.beauchesne@example.com',
},
{
firstname: 'Simone',
lastname: 'Bellefeuille',
email: 'simone.bellefeuille@example.com',
},
{
firstname: 'Étienne',
lastname: 'Salois',
email: 'etienne.salois@example.com',
},
{
firstname: 'Thierry',
lastname: 'Bobu',
email: 'thierry.bobu@example.com',
},
{
firstname: 'Bernadette',
lastname: 'Langelier',
email: 'bernadette.langelier@exemple.com',
},
{
firstname: 'Agate',
lastname: 'Roy',
email: 'agate.roy@exemple.com',
},
],
'options': {
itemsPerPage: 4,
page: 1,
filters: [],
},
'caption': '',
'suffix': 'selection-server-table',
'density': 'default',
'striped': false,
'showSelectSingle': true,
'showFilters': true,
'serverItemsLength': 6,
'onUpdate:options': fn(),
},
render(args) {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const selection = ref([])
const state = ref(StateEnum.IDLE)
const fetchData = async (): Promise => {
// Create a complete DataOptions object with all required properties
const defaultOptions: DataOptions = {
page: 1,
itemsPerPage: 10,
sortBy: [],
multiSort: false,
}
const options = args.options ? { ...defaultOptions, ...args.options } : defaultOptions
const { items, total } = await getDataFromApi(options)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
let total = items.length // Changed from const to let
// Add filtering logic here
if (filters && filters.length > 0) {
filters.forEach((filter) => {
const { key, value } = filter
items = items.filter((item) => {
const itemValue = item[key]
return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
})
})
// Update total after filtering
total = items.length
}
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Thierry', lastname: 'Bobu', email: 'thierry.bobu@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@exemple.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@exemple.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
]
}
// Keep a full copy of all users for selection display across pages
const allUsers = ref(getUsers())
// Call fetchData on mount
fetchData()
return { args, users, allUsers, state, fetchData, totalUsers, selection, StateEnum }
},
template: `
Item(s) sélectionné(s) ({{ selection.length }})
Nom: {{ typeof item === 'object' ? item.lastname : allUsers.find(i => JSON.stringify(i) === item)?.lastname }}
Prénom: {{ typeof item === 'object' ? item.firstname : allUsers.find(i => JSON.stringify(i) === item)?.firstname }}
Email: {{ typeof item === 'object' ? item.email : allUsers.find(i => JSON.stringify(i) === item)?.email }}
`,
}
},
}
export const PinnedColumns: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
page: 1,
},
'headers': [
{ title: 'ID', key: 'id', width: 80 },
{ title: 'Nom', key: 'lastname', width: 160 },
{ title: 'Prénom', key: 'firstname', width: 160 },
{ title: 'Email', key: 'email', width: 240 },
{ title: 'Ville', key: 'city', width: 160 },
{ title: 'Pays', key: 'country', width: 160 },
{ title: 'Téléphone', key: 'phone', width: 180 },
{ title: 'Statut', key: 'status', width: 140 },
{ title: 'Dernière connexion', key: 'lastLogin', width: 200 },
{ title: 'Actions', key: 'actions', width: 140 },
],
'serverItemsLength': 30,
'suffix': 'server-pinned-columns',
'showSelect': true,
'stickySelect': true,
'pinnedColumns': [
{ key: 'actions', side: 'right' },
],
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const argsWithoutOptions = Object.fromEntries(
Object.entries(args as Record).filter(([k]) => k !== 'options'),
)
const users = ref[]>([])
const totalUsers = ref(args.serverItemsLength)
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options } as DataOptions)
const getUsers = (): Record[] => {
return Array.from({ length: 30 }).map((_, i) => ({
id: i + 1,
lastname: 'Nom ' + (i + 1),
firstname: 'Prénom ' + (i + 1),
email: 'user' + (i + 1) + '@example.com',
city: 'Paris',
country: 'France',
phone: '01 02 03 04 05',
status: i % 2 === 0 ? 'Actif' : 'Inactif',
lastLogin: dayjs().subtract(i, 'day').format('DD/MM/YYYY'),
actions: '…',
}))
}
const fetchData = async (): Promise => {
state.value = StateEnum.PENDING
await new Promise(resolve => setTimeout(resolve, 1000))
const all = getUsers()
const start = ((options.value.page ?? 1) - 1) * (options.value.itemsPerPage ?? 5)
const end = start + (options.value.itemsPerPage ?? 5)
users.value = all.slice(start, end)
totalUsers.value = all.length
state.value = StateEnum.RESOLVED
}
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
fetchData()
}, { deep: true, immediate: true })
return { args: argsWithoutOptions, users, totalUsers, state, options, fetchData, StateEnum }
},
template: `
`,
}
},
}
export const ColumnControls: StoryObj = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-control-columns',
'density': 'default',
'striped': false,
'enableColumnControls': true,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ExpandableRows: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
|
Informations complémentaires :
Plus de détails pour {{ item.firstname }} {{ item.lastname }}.
|
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'showExpand': true,
'suffix': 'server-expandable',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum, mdiChevronDown, mdiChevronUp }
},
template: `
|
Informations complémentaires :
Plus de détails pour {{ item.firstname }} {{ item.lastname }}.
|
`,
}
},
}
export const SlotItem: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
| {{ item.lastname }} |
{{ item.firstname }}
|
{{ item.email }} |
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const SlotHeaders: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
|
{{ column.title }}
|
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
|
{{ column.title }}
|
`,
}
},
}
export const SlotHeader: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Nom de famille
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
Nom de famille
`,
}
},
}
export const ItemsPerPageOptions: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'lastname', order: 'asc' }],
page: 1,
},
'itemsPerPageOptions': [5, 10, 15],
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'caption': '',
'serverItemsLength': 15,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items: User[] = getUsers()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key]! > b[key]! ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => {
return [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
{ firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
{ firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
{ firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
{ firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
]
}
// Initialize data
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const ComplexItemsDisplay: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
Depuis le {{ item.period.start }} jusqu'au {{ item.period.end }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': {
itemsPerPage: 5,
sortBy: [{ key: 'title', order: 'asc' }],
page: 1,
},
'headers': [
{ title: 'Titre', key: 'title' },
{ title: 'Période', key: 'period' },
{ title: 'Statut', key: 'status' },
],
'caption': '',
'serverItemsLength': 6,
'suffix': 'server-default',
'density': 'default',
'striped': false,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
type Item = {
title: string
period: {
start: string
end: string
}
status: string
}
const totalItems = ref(0)
const items = ref- ([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items: fetchedItems, total } = await getDataFromApi(options.value as DataOptions)
items.value = fetchedItems
totalItems.value = total
}
const wait = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<{ items: Item[], total: number }> => {
state.value = StateEnum.PENDING
await wait(1000)
return new Promise((resolve) => {
let items = getItems()
const total = items.length
if (sortBy && sortBy.length > 0) {
items = items.sort((a, b) => {
const key = sortBy[0]!.key
const order = sortBy[0]!.order === 'asc' ? 1 : -1
return a[key] > b[key] ? order : -order
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getItems = () => {
return [
{ title: 'Projet Alpha', period: { start: '2023-01-01', end: '2023-06-30' }, status: 'En cours' },
{ title: 'Projet Beta', period: { start: '2022-05-15', end: '2022-12-15' }, status: 'Terminé' },
{ title: 'Projet Gamma', period: { start: '2023-03-01', end: '2023-09-30' }, status: 'En cours' },
{ title: 'Projet Delta', period: { start: '2021-11-01', end: '2022-04-30' }, status: 'Terminé' },
{ title: 'Projet Epsilon', period: { start: '2023-07-01', end: '2023-12-31' }, status: 'À venir' },
{ title: 'Projet Zeta', period: { start: '2022-02-01', end: '2022-08-31' }, status: 'Terminé' },
]
}
// Initialize data
fetchData()
return { args, items, state, fetchData, options, totalItems, StateEnum }
},
template: `
Depuis le {{ item.period.start }} jusqu'au {{ item.period.end }}
`,
}
},
}
export const HideDefaultFooter: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': { itemsPerPage: -1, page: 1 },
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'serverItemsLength': 6,
'suffix': 'server-hide-footer',
'density': 'default',
'striped': false,
'hideDefaultFooter': true,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const getDataFromApi = async ({ page, itemsPerPage }: DataOptions): Promise<{ items: User[], total: number }> => {
state.value = StateEnum.PENDING
await new Promise(resolve => setTimeout(resolve, 500))
return new Promise((resolve) => {
const allItems = getUsers()
const total = allItems.length
const items = itemsPerPage > 0
? allItems.slice((page - 1) * itemsPerPage, page * itemsPerPage)
: allItems
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
]
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}
export const PageInput: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
'options': { itemsPerPage: 5, page: 1 },
'headers': [
{ title: 'Nom', key: 'lastname' },
{ title: 'Prénom', key: 'firstname' },
{ title: 'Email', key: 'email' },
],
'serverItemsLength': 11,
'suffix': 'server-page-input',
'density': 'default',
'striped': false,
'pageInput': true,
'onUpdate:options': fn(),
},
render: (args) => {
return {
components: { SyServerTable },
setup() {
const totalUsers = ref(0)
const users = ref([])
const state = ref(StateEnum.IDLE)
const options = ref({ ...args.options })
watch(options, (newVal) => {
if (args.options) {
Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
}
}, { deep: true })
const fetchData = async (): Promise => {
const { items, total } = await getDataFromApi(options.value as DataOptions)
users.value = items
totalUsers.value = total
}
const getDataFromApi = async ({ page, itemsPerPage }: DataOptions): Promise<{ items: User[], total: number }> => {
state.value = StateEnum.PENDING
await new Promise(resolve => setTimeout(resolve, 500))
return new Promise((resolve) => {
const allItems = getUsers()
const total = allItems.length
const items = itemsPerPage > 0
? allItems.slice((page - 1) * itemsPerPage, page * itemsPerPage)
: allItems
resolve({ items, total })
state.value = StateEnum.RESOLVED
})
}
const getUsers = (): User[] => [
{ firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
{ firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
{ firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
{ firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
{ firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
{ firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
{ firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
{ firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
{ firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
{ firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
{ firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
]
fetchData()
return { args, users, state, fetchData, options, totalUsers, StateEnum }
},
template: `
`,
}
},
}