Skip to content

Commit

Permalink
Port the table component from Wikit back to the Mismatch Finder (#850)
Browse files Browse the repository at this point in the history
* Port the table component from Wikit to Mismatch Finder

* fix ref export and add test

* Rename component and replace discrete values with tokens where possible

* two more tokens

* use correct component name in test and css selector

* update class selector name

* make component tag kebab-case
  • Loading branch information
chukarave committed Jan 30, 2024
1 parent d56f426 commit ed3129a
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 4 deletions.
7 changes: 3 additions & 4 deletions resources/js/Components/MismatchesTable.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<wikit-table>
<table-component>
<thead>
<tr>
<th class="column-mismatch">
Expand Down Expand Up @@ -34,12 +34,11 @@
:id="`mismatch-${mismatch.id}`"
/>
</tbody>
</wikit-table>
</table-component>
</template>

<script setup lang="ts">
import { Table as WikitTable } from '@wmde/wikit-vue-components';
import TableComponent from './TableComponent.vue';
import MismatchRow from './MismatchRow.vue';
import type { LabelledMismatch } from '../types/Mismatch';
Expand Down
228 changes: 228 additions & 0 deletions resources/js/Components/TableComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
<template>
<table
ref="TableComponent"
:class="[
'table-component',
`table-component--linear-${breakpoint}`
]"
>
<slot />
</table>
</template>

<script setup lang="ts">
import { PropType, computed, ref } from 'vue';
import { Breakpoint, validateBreakpoint } from '../types/Breakpoint';
/**
* Tables display categorical information organized across rows and columns in
* order to facilitate the comparative analysis of data.
*
* The table component provides a wrapper around the common HTML table
* elements such as `<thead>`, `<tbody>`, `<tr>`, `<th>` and `<td>`, to apply
* design system styles to tabular data.
*
* Adding a `data-header` attribute to the cells allows us to maintain the column
* headers and display them in the table's linearized form to provide additional
* context.
*
* **Example:**
*
* ```html
* <td data-header="Column Header">Content Here</td>
* ```
*/
const TableComponent = ref('TableComponent');
const props = defineProps({
linearize: {
type: String as PropType<Breakpoint>,
default: Breakpoint.Tablet,
validator: (value: Breakpoint): boolean => {
return validateBreakpoint( value );
}
}
});
const breakpoint = computed<string>(() => {
return validateBreakpoint( props.linearize ) ? props.linearize : 'tablet';
});
defineExpose({TableComponent});
</script>

<style lang="scss">
@import "@wikimedia/codex-design-tokens/theme-wikimedia-ui";
@mixin linear-table {
/**
* Completely removes thead, modern screen readers will expose the
* generated content
*/
thead {
display: none;
visibility: hidden;
}
/**
* Make everything display flex for alignment
*/
tbody,
tr,
th,
td {
height: auto;
display: flex;
flex-direction: column;
}
td,
th {
flex-direction: row;
flex-basis: 60%;
}
/**
* Labeling
*
* Adding a data-header attribute to the cells
* lets us add text before the content to provide
* the missing context.
*
* Markup:
* <td data-header="Column Header">Content Here</td>
*/
/* stylelint-disable selector-no-qualifying-type */
th[data-header]::before,
td[data-header]::before {
content: attr(data-header);
display: block;
font-weight: $font-weight-bold;
flex-basis: 40%;
// Ensure headers stay exactly 40%
// even if values are wider than 60%
min-width: 40%;
}
th:not([data-header]) {
font-weight: $font-weight-bold;
}
// Hide empty cells
td:empty {
display: none;
}
}
.table-component {
/**
* Layout
*/
// As the specificationn state that the table columns should gain their
// width from cell content (instead of just header content / width) we
// revert to using the default table layout algorithm. This is done in
// order to avoid changing the table's display proterty and thus
// oblitirating it's inehrit accesibility:
// See: https://www.tpgi.com/short-note-on-what-css-display-properties-do-to-table-semantics/
table-layout: auto;
width: 100%;
/**
* Borders
*/
border-collapse: collapse;
/**
* Colors
*/
background-color: $background-color-base;
color: $color-base;
/**
* Typography
*/
font-family: $font-family-system-sans;
font-size: $font-size-medium;
font-weight: $font-weight-normal;
tr {
/**
* Layout
*/
/**
* Borders
*/
border-bottom: $border-width-base $border-color-subtle solid;
border-radius: 0;
}
tbody tr:hover {
background-color: $background-color-interactive;
transition-duration: $transition-duration-medium;
transition-timing-function: $transition-timing-function-system;
transition-property: $transition-property-base;
}
th,
td {
/**
* Layout
*/
padding-inline: .75em;
/**
* Typography
*/
line-height: 20px;
text-align: start;
overflow-wrap: break-word;
hyphens: auto;
}
td {
/**
* Layout
*/
height: 48px;
padding-block: .5em;
/**
* Typography
*/
vertical-align: middle;
}
th {
/**
* Layout
*/
padding-block: .75em;
/**
* Typography
*/
font-weight: 700;
vertical-align: top;
}
&--linear-mobile {
@media (max-width: $max-width-breakpoint-mobile) {
@include linear-table;
}
}
&--linear-tablet {
@media (max-width: $max-width-breakpoint-tablet) {
@include linear-table;
}
}
&--linear-desktop {
@media (max-width: $max-width-breakpoint-desktop) {
@include linear-table;
}
}
}
</style>
14 changes: 14 additions & 0 deletions resources/js/types/Breakpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
enum Breakpoint {
Mobile = 'mobile',
Tablet = 'tablet',
Desktop = 'desktop'
}

function validateBreakpoint( breakpoint: string ): boolean {
return Object.values( Breakpoint ).includes( breakpoint as Breakpoint );
}

export {
Breakpoint,
validateBreakpoint,
};
24 changes: 24 additions & 0 deletions tests/Vue/Components/TableComponent.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { mount } from '@vue/test-utils';
import TableComponent from '@/Components/TableComponent.vue';
import {Breakpoint} from '@/types/Breakpoint.ts';

describe('TableComponent.vue', () => {
it('accepts a linearize property', () => {
const wrapper = mount(TableComponent, {
propsData: {
linearize: Breakpoint.Desktop
}
});

expect( wrapper.props().linearize ).toBe( Breakpoint.Desktop );
expect( wrapper.find( 'table' ).classes() ).toContain( 'table-component--linear-desktop' );
});

it('ignores invalid breakpoint values', () => {
const wrapper = mount(TableComponent, {
propsData: { linearize: 'nonsense' }
});

expect(wrapper.find('table').classes()).toContain('table-component--linear-tablet');
});
})

0 comments on commit ed3129a

Please sign in to comment.