Skip to content

Commit ed3129a

Browse files
authored
Port the table component from Wikit back to the Mismatch Finder (#850)
* 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
1 parent d56f426 commit ed3129a

File tree

4 files changed

+269
-4
lines changed

4 files changed

+269
-4
lines changed

resources/js/Components/MismatchesTable.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<wikit-table>
2+
<table-component>
33
<thead>
44
<tr>
55
<th class="column-mismatch">
@@ -34,12 +34,11 @@
3434
:id="`mismatch-${mismatch.id}`"
3535
/>
3636
</tbody>
37-
</wikit-table>
37+
</table-component>
3838
</template>
3939

4040
<script setup lang="ts">
41-
import { Table as WikitTable } from '@wmde/wikit-vue-components';
42-
41+
import TableComponent from './TableComponent.vue';
4342
import MismatchRow from './MismatchRow.vue';
4443
4544
import type { LabelledMismatch } from '../types/Mismatch';
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
<template>
2+
<table
3+
ref="TableComponent"
4+
:class="[
5+
'table-component',
6+
`table-component--linear-${breakpoint}`
7+
]"
8+
>
9+
<slot />
10+
</table>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import { PropType, computed, ref } from 'vue';
15+
import { Breakpoint, validateBreakpoint } from '../types/Breakpoint';
16+
17+
/**
18+
* Tables display categorical information organized across rows and columns in
19+
* order to facilitate the comparative analysis of data.
20+
*
21+
* The table component provides a wrapper around the common HTML table
22+
* elements such as `<thead>`, `<tbody>`, `<tr>`, `<th>` and `<td>`, to apply
23+
* design system styles to tabular data.
24+
*
25+
* Adding a `data-header` attribute to the cells allows us to maintain the column
26+
* headers and display them in the table's linearized form to provide additional
27+
* context.
28+
*
29+
* **Example:**
30+
*
31+
* ```html
32+
* <td data-header="Column Header">Content Here</td>
33+
* ```
34+
*/
35+
const TableComponent = ref('TableComponent');
36+
const props = defineProps({
37+
linearize: {
38+
type: String as PropType<Breakpoint>,
39+
default: Breakpoint.Tablet,
40+
validator: (value: Breakpoint): boolean => {
41+
return validateBreakpoint( value );
42+
}
43+
}
44+
});
45+
46+
const breakpoint = computed<string>(() => {
47+
return validateBreakpoint( props.linearize ) ? props.linearize : 'tablet';
48+
});
49+
50+
defineExpose({TableComponent});
51+
52+
</script>
53+
54+
<style lang="scss">
55+
@import "@wikimedia/codex-design-tokens/theme-wikimedia-ui";
56+
57+
@mixin linear-table {
58+
/**
59+
* Completely removes thead, modern screen readers will expose the
60+
* generated content
61+
*/
62+
thead {
63+
display: none;
64+
visibility: hidden;
65+
}
66+
67+
/**
68+
* Make everything display flex for alignment
69+
*/
70+
tbody,
71+
tr,
72+
th,
73+
td {
74+
height: auto;
75+
display: flex;
76+
flex-direction: column;
77+
}
78+
79+
td,
80+
th {
81+
flex-direction: row;
82+
flex-basis: 60%;
83+
}
84+
85+
/**
86+
* Labeling
87+
*
88+
* Adding a data-header attribute to the cells
89+
* lets us add text before the content to provide
90+
* the missing context.
91+
*
92+
* Markup:
93+
* <td data-header="Column Header">Content Here</td>
94+
*/
95+
/* stylelint-disable selector-no-qualifying-type */
96+
th[data-header]::before,
97+
td[data-header]::before {
98+
content: attr(data-header);
99+
display: block;
100+
font-weight: $font-weight-bold;
101+
flex-basis: 40%;
102+
103+
// Ensure headers stay exactly 40%
104+
// even if values are wider than 60%
105+
min-width: 40%;
106+
}
107+
108+
th:not([data-header]) {
109+
font-weight: $font-weight-bold;
110+
}
111+
112+
// Hide empty cells
113+
td:empty {
114+
display: none;
115+
}
116+
}
117+
118+
.table-component {
119+
/**
120+
* Layout
121+
*/
122+
// As the specificationn state that the table columns should gain their
123+
// width from cell content (instead of just header content / width) we
124+
// revert to using the default table layout algorithm. This is done in
125+
// order to avoid changing the table's display proterty and thus
126+
// oblitirating it's inehrit accesibility:
127+
// See: https://www.tpgi.com/short-note-on-what-css-display-properties-do-to-table-semantics/
128+
table-layout: auto;
129+
width: 100%;
130+
131+
/**
132+
* Borders
133+
*/
134+
border-collapse: collapse;
135+
136+
/**
137+
* Colors
138+
*/
139+
background-color: $background-color-base;
140+
color: $color-base;
141+
142+
/**
143+
* Typography
144+
*/
145+
font-family: $font-family-system-sans;
146+
font-size: $font-size-medium;
147+
font-weight: $font-weight-normal;
148+
149+
tr {
150+
/**
151+
* Layout
152+
*/
153+
154+
/**
155+
* Borders
156+
*/
157+
border-bottom: $border-width-base $border-color-subtle solid;
158+
border-radius: 0;
159+
}
160+
161+
tbody tr:hover {
162+
background-color: $background-color-interactive;
163+
transition-duration: $transition-duration-medium;
164+
transition-timing-function: $transition-timing-function-system;
165+
transition-property: $transition-property-base;
166+
}
167+
168+
th,
169+
td {
170+
/**
171+
* Layout
172+
*/
173+
padding-inline: .75em;
174+
175+
/**
176+
* Typography
177+
*/
178+
line-height: 20px;
179+
text-align: start;
180+
overflow-wrap: break-word;
181+
hyphens: auto;
182+
}
183+
184+
td {
185+
/**
186+
* Layout
187+
*/
188+
height: 48px;
189+
padding-block: .5em;
190+
191+
/**
192+
* Typography
193+
*/
194+
vertical-align: middle;
195+
}
196+
197+
th {
198+
/**
199+
* Layout
200+
*/
201+
padding-block: .75em;
202+
203+
/**
204+
* Typography
205+
*/
206+
font-weight: 700;
207+
vertical-align: top;
208+
}
209+
210+
&--linear-mobile {
211+
@media (max-width: $max-width-breakpoint-mobile) {
212+
@include linear-table;
213+
}
214+
}
215+
216+
&--linear-tablet {
217+
@media (max-width: $max-width-breakpoint-tablet) {
218+
@include linear-table;
219+
}
220+
}
221+
222+
&--linear-desktop {
223+
@media (max-width: $max-width-breakpoint-desktop) {
224+
@include linear-table;
225+
}
226+
}
227+
}
228+
</style>

resources/js/types/Breakpoint.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum Breakpoint {
2+
Mobile = 'mobile',
3+
Tablet = 'tablet',
4+
Desktop = 'desktop'
5+
}
6+
7+
function validateBreakpoint( breakpoint: string ): boolean {
8+
return Object.values( Breakpoint ).includes( breakpoint as Breakpoint );
9+
}
10+
11+
export {
12+
Breakpoint,
13+
validateBreakpoint,
14+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { mount } from '@vue/test-utils';
2+
import TableComponent from '@/Components/TableComponent.vue';
3+
import {Breakpoint} from '@/types/Breakpoint.ts';
4+
5+
describe('TableComponent.vue', () => {
6+
it('accepts a linearize property', () => {
7+
const wrapper = mount(TableComponent, {
8+
propsData: {
9+
linearize: Breakpoint.Desktop
10+
}
11+
});
12+
13+
expect( wrapper.props().linearize ).toBe( Breakpoint.Desktop );
14+
expect( wrapper.find( 'table' ).classes() ).toContain( 'table-component--linear-desktop' );
15+
});
16+
17+
it('ignores invalid breakpoint values', () => {
18+
const wrapper = mount(TableComponent, {
19+
propsData: { linearize: 'nonsense' }
20+
});
21+
22+
expect(wrapper.find('table').classes()).toContain('table-component--linear-tablet');
23+
});
24+
})

0 commit comments

Comments
 (0)