Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port the table component from Wikit back to the Mismatch Finder #850

Merged
merged 7 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
<TableComponent>
hasanakg marked this conversation as resolved.
Show resolved Hide resolved
<thead>
<tr>
<th class="column-mismatch">
Expand Down Expand Up @@ -34,12 +34,11 @@
:id="`mismatch-${mismatch.id}`"
/>
</tbody>
</wikit-table>
</TableComponent>
</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
229 changes: 229 additions & 0 deletions resources/js/Components/TableComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<template>
<table
ref="TableComponent"
:class="[
'wikit',
'wikit-Table',
`wikit-Table--linear-${breakpoint}`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guergana @hasanakg renaming those lines breaks the whole table, but I'm not actually sure why, we don't assign any explicit styles to these selectors. any ideas?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain it a bit more @chukarave ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use ['table-component', `table-component--linear${breakpoint}`], right now this doesn't use the style from TableComponent.vue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! I think you got it

]"
>
<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;
}
}

.TableComponent {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This selector can be renamed to 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( 'wikit-Table--linear-desktop' );
});

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

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