Skip to content

Commit

Permalink
fix: make NeTable responsive and accessible (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
andre8244 authored May 8, 2024
1 parent 7972151 commit c4eb5d3
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 53 deletions.
65 changes: 63 additions & 2 deletions src/components/NeTable.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,70 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { provide, ref, type PropType } from 'vue'
import NeTableSkeleton from './NeTableSkeleton.vue'

export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl'

const props = defineProps({
ariaLabel: {
type: String,
required: true
},
cardBreakpoint: {
type: String as PropType<Breakpoint>,
default: 'md'
},
loading: {
type: Boolean,
default: false
},
skeletonRows: {
type: Number,
default: 8
},
skeletonColumns: {
type: Number,
default: 4
}
})

// provide cardBreakpoint prop to children components
provide('cardBreakpoint', ref(props.cardBreakpoint))

const tableCardStyle: Record<Breakpoint, string> = {
sm: 'sm:table sm:divide-y sm:divide-gray-300 sm:dark:divide-gray-600',
md: 'md:table md:divide-y md:divide-gray-300 md:dark:divide-gray-600',
lg: 'lg:table lg:divide-y lg:divide-gray-300 lg:dark:divide-gray-600',
xl: 'xl:table xl:divide-y xl:divide-gray-300 xl:dark:divide-gray-600',
'2xl': '2xl:table 2xl:divide-y 2xl:divide-gray-300 2xl:dark:divide-gray-600'
}
</script>
<template>
<div class="overflow-x-auto rounded-lg border border-gray-300 shadow-sm dark:border-gray-600">
<table
class="w-full table-auto divide-y divide-gray-300 bg-white text-left text-sm font-normal text-gray-700 dark:divide-gray-600 dark:bg-gray-950 dark:text-gray-200"
role="grid"
:aria-label="ariaLabel"
:class="[
`grid w-full table-auto bg-white text-left text-sm font-normal text-gray-700 dark:bg-gray-950 dark:text-gray-200`,
tableCardStyle[cardBreakpoint]
]"
>
<slot />
<template v-if="loading">
<NeTableSkeleton
:rows="skeletonRows"
:columns="skeletonColumns"
:card-breakpoint="cardBreakpoint"
/>
</template>
<template v-else>
<slot />
</template>
</table>
</div>
<div v-if="$slots.paginator" class="mt-6 flex flex-row justify-end">
<slot name="paginator" />
</div>
</template>
23 changes: 22 additions & 1 deletion src/components/NeTableBody.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { inject } from 'vue'
import type { Breakpoint } from './NeTable.vue'

// inject cardBreakpoint from NeTable.vue
const cardBreakpoint = inject('cardBreakpoint', 'md')

const tbodyCardStyle: Record<Breakpoint, string> = {
sm: 'sm:table-row-group',
md: 'md:table-row-group',
lg: 'lg:table-row-group',
xl: 'xl:table-row-group',
'2xl': '2xl:table-row-group'
}
</script>
<template>
<tbody class="divide-y divide-gray-300 dark:divide-gray-600">
<tbody
:class="[`block divide-y divide-gray-300 dark:divide-gray-600`, tbodyCardStyle[cardBreakpoint]]"
>
<slot />
</tbody>
</template>
42 changes: 41 additions & 1 deletion src/components/NeTableCell.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { inject } from 'vue'
import type { Breakpoint } from './NeTable.vue'

defineProps({
dataLabel: {
// this attribute replaces table header in mobile viewport
type: String,
required: true
}
})

// inject cardBreakpoint from NeTable.vue
const cardBreakpoint = inject('cardBreakpoint', 'md')

const tdCardStyle: Record<Breakpoint, string> = {
sm: 'sm:table-cell',
md: 'md:table-cell',
lg: 'lg:table-cell',
xl: 'xl:table-cell',
'2xl': '2xl:table-cell'
}

const dataLabelCardStyle: Record<Breakpoint, string> = {
sm: 'sm:hidden',
md: 'md:hidden',
lg: 'lg:hidden',
xl: 'xl:hidden',
'2xl': '2xl:hidden'
}
</script>
<template>
<td class="px-6 py-4">
<td :data-label="dataLabel" :class="[`grid grid-cols-2 px-6 py-4`, tdCardStyle[cardBreakpoint]]">
<span
:class="[`font-medium text-gray-900 dark:text-gray-50`, dataLabelCardStyle[cardBreakpoint]]"
>
{{ dataLabel }}
</span>
<slot />
</td>
</template>
26 changes: 25 additions & 1 deletion src/components/NeTableHead.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { inject } from 'vue'
import type { Breakpoint } from './NeTable.vue'

// inject cardBreakpoint from NeTable.vue
const cardBreakpoint = inject('cardBreakpoint', 'md')

const tbodyCardStyle: Record<Breakpoint, string> = {
sm: 'sm:table-header-group',
md: 'md:table-header-group',
lg: 'lg:table-header-group',
xl: 'xl:table-header-group',
'2xl': '2xl:table-header-group'
}
</script>
<template>
<thead class="bg-gray-100 font-medium text-gray-900 dark:bg-gray-800 dark:text-gray-50">
<thead
:class="[
`hidden bg-gray-100 font-medium text-gray-900 dark:bg-gray-800 dark:text-gray-50`,
tbodyCardStyle[cardBreakpoint]
]"
>
<tr>
<slot></slot>
</tr>
Expand Down
6 changes: 5 additions & 1 deletion src/components/NeTableHeadCell.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<template>
<th class="px-6 py-3">
<th scope="col" class="px-6 py-3">
<slot></slot>
</th>
</template>
21 changes: 20 additions & 1 deletion src/components/NeTableRow.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { inject } from 'vue'
import type { Breakpoint } from './NeTable.vue'

// inject cardBreakpoint from NeTable.vue
const cardBreakpoint = inject('cardBreakpoint', 'md')

const trCardStyle: Record<Breakpoint, string> = {
sm: 'sm:table-row',
md: 'md:table-row',
lg: 'lg:table-row',
xl: 'xl:table-row',
'2xl': '2xl:table-row'
}
</script>
<template>
<tr>
<tr :class="[`grid`, trCardStyle[cardBreakpoint]]">
<slot />
</tr>
</template>
44 changes: 44 additions & 0 deletions src/components/NeTableSkeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->
<script lang="ts" setup>
import { type PropType } from 'vue'
import NeTableHead from './NeTableHead.vue'
import NeTableHeadCell from './NeTableHeadCell.vue'
import NeSkeleton from './NeSkeleton.vue'
import NeTableBody from './NeTableBody.vue'
import NeTableRow from './NeTableRow.vue'
import NeTableCell from './NeTableCell.vue'

export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl'

defineProps({
rows: {
type: Number,
required: true
},
columns: {
type: Number,
required: true
},
cardBreakpoint: {
type: String as PropType<Breakpoint>,
default: 'md'
}
})
</script>
<template>
<NeTableHead :card-breakpoint="cardBreakpoint">
<NeTableHeadCell v-for="i in columns" :key="i">
<NeSkeleton size="lg" />
</NeTableHeadCell>
</NeTableHead>
<NeTableBody :card-breakpoint="cardBreakpoint">
<NeTableRow v-for="i in rows" :key="i" :card-breakpoint="cardBreakpoint">
<NeTableCell v-for="j in columns" :key="j" :card-breakpoint="cardBreakpoint" data-label="">
<NeSkeleton size="lg" />
</NeTableCell>
</NeTableRow>
</NeTableBody>
</template>
115 changes: 69 additions & 46 deletions stories/NeTable.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ const meta: Meta<typeof NeTable> = {
title: 'Visual/NeTable',
component: NeTable,
tags: ['autodocs'],
argTypes: {
cardBreakpoint: { control: 'inline-radio', options: ['sm', 'md', 'lg', 'xl', '2xl'] }
},
args: {
ariaLabel: 'Aria label for the table',
cardBreakpoint: 'md',
loading: false,
skeletonRows: 8,
skeletonColumns: 4
},
render: (args) => ({
components: {
NeTable,
Expand All @@ -25,54 +35,67 @@ const meta: Meta<typeof NeTable> = {
return { args }
},
template: `
<NeTable>
<NeTableHead>
<NeTableHeadCell>Game</NeTableHeadCell>
<NeTableHeadCell>Platform</NeTableHeadCell>
<NeTableHeadCell>Year</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<NeTableRow>
<NeTableCell>The Legend of Zelda: Breath of the Wild</NeTableCell>
<NeTableCell>Nintendo Switch</NeTableCell>
<NeTableCell>2017</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>Super Mario Odyssey</NeTableCell>
<NeTableCell>Nintendo Switch</NeTableCell>
<NeTableCell>2017</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>The Legend of Zelda: Ocarina of Time</NeTableCell>
<NeTableCell>Nintendo 64</NeTableCell>
<NeTableCell>1998</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>Super Mario 64</NeTableCell>
<NeTableCell>Nintendo 64</NeTableCell>
<NeTableCell>1996</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>The Legend of Zelda: A Link to the Past</NeTableCell>
<NeTableCell>Super Nintendo</NeTableCell>
<NeTableCell>1991</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>Super Mario World</NeTableCell>
<NeTableCell>Super Nintendo</NeTableCell>
<NeTableCell>1990</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell>The Legend of Zelda</NeTableCell>
<NeTableCell>Nintendo Entertainment System</NeTableCell>
<NeTableCell>1986</NeTableCell>
</NeTableRow>
</NeTableBody>
</NeTable>
`
<NeTable v-bind="args">
<NeTableHead>
<NeTableHeadCell>Game</NeTableHeadCell>
<NeTableHeadCell>Platform</NeTableHeadCell>
<NeTableHeadCell>Year</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<NeTableRow>
<NeTableCell data-label="Game">The Legend of Zelda: Breath of the Wild</NeTableCell>
<NeTableCell data-label="Platform">Nintendo Switch</NeTableCell>
<NeTableCell data-label="Year">2017</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">Super Mario Odyssey</NeTableCell>
<NeTableCell data-label="Platform">Nintendo Switch</NeTableCell>
<NeTableCell data-label="Year">2017</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">The Legend of Zelda: Ocarina of Time</NeTableCell>
<NeTableCell data-label="Platform">Nintendo 64</NeTableCell>
<NeTableCell data-label="Year">1998</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">Super Mario 64</NeTableCell>
<NeTableCell data-label="Platform">Nintendo 64</NeTableCell>
<NeTableCell data-label="Year">1996</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">The Legend of Zelda: A Link to the Past</NeTableCell>
<NeTableCell data-label="Platform">Super Nintendo</NeTableCell>
<NeTableCell data-label="Year">1991</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">Super Mario World</NeTableCell>
<NeTableCell data-label="Platform">Super Nintendo</NeTableCell>
<NeTableCell data-label="Year">1990</NeTableCell>
</NeTableRow>
<NeTableRow>
<NeTableCell data-label="Game">The Legend of Zelda</NeTableCell>
<NeTableCell data-label="Platform">Nintendo Entertainment System</NeTableCell>
<NeTableCell data-label="Year">1986</NeTableCell>
</NeTableRow>
</NeTableBody>
</NeTable>`
})
}

export default meta

export const Default: StoryObj<typeof NeTable> = {}
export const Default: StoryObj<typeof NeTable> = {
args: {}
}

export const Loading: StoryObj<typeof NeTable> = {
args: {
loading: true
}
}

export const CardBreakpointXL: StoryObj<typeof NeTable> = {
args: {
cardBreakpoint: 'xl'
}
}

0 comments on commit c4eb5d3

Please sign in to comment.