-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
4 changed files
with
269 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}); | ||
}) |