Table unit tests (#554)

* update to useIntl()

* update to useIntl()

* implement unit tests for ./components/table/*

* fix lint

* update for useIntl()

* remove comments
This commit is contained in:
Scott Bishel 2021-06-18 08:52:36 -06:00 committed by GitHub
parent d7442739b5
commit 967e6e8078
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 3014 additions and 21 deletions

View file

@ -17663,9 +17663,9 @@
}
},
"@testing-library/dom": {
"version": "7.30.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.0.tgz",
"integrity": "sha512-v4GzWtltaiDE0yRikLlcLAfEiiK8+ptu6OuuIebm9GdC2XlZTNDPGEfM2UkEtnH7hr9TRq2sivT5EA9P1Oy7bw==",
"version": "7.31.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
"integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@ -17673,7 +17673,7 @@
"@types/aria-query": "^4.2.0",
"aria-query": "^4.2.2",
"chalk": "^4.1.0",
"dom-accessibility-api": "^0.5.4",
"dom-accessibility-api": "^0.5.6",
"lz-string": "^1.4.4",
"pretty-format": "^26.6.2"
}

View file

@ -50,7 +50,7 @@
},
"jest": {
"moduleNameMapper": {
"\\.(scss)$": "<rootDir>/__mocks__/styleMock.js"
"\\.(scss|css)$": "<rootDir>/__mocks__/styleMock.js"
},
"globals": {
"ts-jest": {
@ -69,6 +69,7 @@
"devDependencies": {
"@formatjs/cli": "^3.2.0",
"@formatjs/ts-transformer": "^3.2.1",
"@testing-library/dom": "^7.31.2",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.9",

View file

@ -166,7 +166,6 @@ class CenterPanel extends React.Component<Props, State> {
showCard={this.showCard}
addCard={this.addCard}
onCardClicked={this.cardClicked}
intl={this.props.intl}
/>}
{activeView.viewType === 'gallery' &&

View file

@ -0,0 +1,700 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/table/Table should match snapshot 1`] = `
<div>
<div
class="octo-table-body Table"
>
<div
class="octo-table-header"
id="mainBoardHeader"
>
<div
class="octo-table-cell header-cell"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Name
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Property 1
<svg
class="SortUpIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,40 50,20 70,40"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Property 2
<svg
class="SortDownIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,60 50,80 70,60"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
</div>
<div
class="table-row-container"
>
<div
class="TableRow octo-table-row"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-14tjw7f-singleValue"
>
<span
class="Label color1 Label-single-select"
>
<span
class="Label-text"
>
value 1
</span>
</span>
</div>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-2-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class=" css-tpaeio-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
/>
</svg>
</div>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-19sxey8-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
>
<span
id="aria-selection"
/>
<span
id="aria-context"
>
Select is focused ,type to refine list, press Down to open the menu,
</span>
</span>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-1wa3eu0-placeholder"
/>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-3-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-hl9mox-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-table-footer"
>
<div
class="octo-table-cell"
>
+ New
</div>
</div>
</div>
</div>
`;
exports[`components/table/Table should match snapshot with GroupBy 1`] = `
<div>
<div
class="octo-table-body Table"
>
<div
class="octo-table-header"
id="mainBoardHeader"
>
<div
class="octo-table-cell header-cell"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Name
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Property 1
<svg
class="SortUpIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,40 50,20 70,40"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
Property 2
<svg
class="SortDownIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,60 50,80 70,60"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
</div>
<div
class="table-row-container"
>
<div
class="octo-table-group"
>
<div
class="octo-group-header-cell expanded"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label empty "
title="Items with an empty Property 1 property will go here. This column cannot be removed."
>
No Property 1
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
</div>
<div
class="octo-table-footer"
/>
</div>
</div>
`;
exports[`components/table/Table should match snapshot, read-only 1`] = `
<div>
<div
class="octo-table-body Table"
>
<div
class="octo-table-header"
id="mainBoardHeader"
>
<div
class="octo-table-cell header-cell"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper disabled"
>
<span
class="Label empty "
>
Name
</span>
</div>
<div
class="octo-spacer"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper disabled"
>
<span
class="Label empty "
>
Property 1
<svg
class="SortUpIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,40 50,20 70,40"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
</div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper disabled"
>
<span
class="Label empty "
>
Property 2
<svg
class="SortDownIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,60 50,80 70,60"
/>
</svg>
</span>
</div>
<div
class="octo-spacer"
/>
</div>
</div>
<div
class="table-row-container"
>
<div
class="TableRow octo-table-row"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable readonly undefined"
placeholder="Untitled"
readonly=""
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="octo-property-value"
tabindex="0"
>
<span
class="Label color1 "
>
value 1
</span>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="octo-property-value"
tabindex="0"
>
<span
class="Label empty "
/>
</div>
</div>
</div>
</div>
<div
class="octo-table-footer"
/>
</div>
</div>
`;

View file

@ -0,0 +1,499 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should match snapshot on read only 1`] = `
<div>
<div
class="octo-group-header-cell expanded"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton readonly"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label color1 "
>
<input
class="Editable readonly undefined"
placeholder="New Select"
readonly=""
spellcheck="true"
title="value 1"
value="value 1"
/>
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
</div>
</div>
`;
exports[`should match snapshot with Group 1`] = `
<div>
<div
class="octo-group-header-cell expanded"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label color1 "
>
<input
class="Editable undefined"
placeholder="New Select"
spellcheck="true"
title="value 1"
value="value 1"
/>
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
`;
exports[`should match snapshot, add new 1`] = `
<div>
<div
class="octo-group-header-cell"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label color1 "
>
<input
class="Editable undefined"
placeholder="New Select"
spellcheck="true"
title="value 1"
value="value 1"
/>
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
`;
exports[`should match snapshot, edit title 1`] = `
<div>
<div
class="octo-group-header-cell"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label color1 "
>
<input
class="Editable undefined"
placeholder="New Select"
spellcheck="true"
title="value 1"
value="value 1"
/>
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
`;
exports[`should match snapshot, hide group 1`] = `
<div>
<div
class="octo-group-header-cell expanded"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label color1 "
>
<input
class="Editable undefined"
placeholder="New Select"
spellcheck="true"
title="value 1"
value="value 1"
/>
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
`;
exports[`should match snapshot, no groups 1`] = `
<div>
<div
class="octo-group-header-cell expanded"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="Button IconButton"
>
<svg
class="DisclosureTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="37,35 37,65 63,50"
/>
</svg>
</div>
<span
class="Label empty "
title="Items with an empty Property 1 property will go here. This column cannot be removed."
>
No Property 1
</span>
</div>
<button
class="Button "
type="button"
>
0
</button>
<div
class="MenuWrapper"
>
<div
class="Button IconButton"
>
<svg
class="OptionsIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="30"
cy="50"
r="5"
/>
<circle
cx="50"
cy="50"
r="5"
/>
<circle
cx="70"
cy="50"
r="5"
/>
</svg>
</div>
</div>
<div
class="Button IconButton"
>
<svg
class="AddIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="30,50 70,50"
/>
<polyline
points="50,30 50,70"
/>
</svg>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/table/TableHeaderMenu should match snapshot, title column 1`] = `
<div>
<div
class="octo-table-cell header-cell"
draggable="true"
style="overflow: unset; width: 100px; opacity: 1;"
>
<div
class="MenuWrapper"
>
<span
class="Label empty "
>
my Name
</span>
</div>
<div
class="octo-spacer"
/>
<div
class="HorizontalGrip"
draggable="true"
/>
</div>
</div>
`;

View file

@ -0,0 +1,244 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/table/TableHeaderMenu should match snapshot, other column 1`] = `
<div>
<div
class="Menu noselect bottom"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Sort ascending
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Sort descending
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Insert left
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Insert right
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Hide
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Duplicate
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Delete
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
class="MenuOption TextOption menu-option menu-cancel"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableHeaderMenu should match snapshot, title column 1`] = `
<div>
<div
class="Menu noselect bottom"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Sort ascending
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Sort descending
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Insert left
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption TextOption menu-option"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Insert right
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
class="MenuOption TextOption menu-option menu-cancel"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,628 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/table/TableRow should match snapshot 1`] = `
<div>
<div
class="TableRow octo-table-row"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableRow should match snapshot, collapsed tree 1`] = `
<div>
<div
class="TableRow octo-table-row hidden"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableRow should match snapshot, display properties 1`] = `
<div>
<div
class="TableRow octo-table-row hidden"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-14tjw7f-singleValue"
>
<span
class="Label color1 Label-single-select"
>
<span
class="Label-text"
>
value 1
</span>
</span>
</div>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-2-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class=" css-tpaeio-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
/>
</svg>
</div>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-19sxey8-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
>
<span
id="aria-selection"
/>
<span
id="aria-context"
>
Select is focused ,type to refine list, press Down to open the menu,
</span>
</span>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-1wa3eu0-placeholder"
/>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-3-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-hl9mox-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableRow should match snapshot, isSelected 1`] = `
<div>
<div
class="TableRow octo-table-row selected"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableRow should match snapshot, read-only 1`] = `
<div>
<div
class="TableRow octo-table-row"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable readonly undefined"
placeholder="Untitled"
readonly=""
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
</div>
</div>
`;
exports[`components/table/TableRow should match snapshot, resizing column 1`] = `
<div>
<div
class="TableRow octo-table-row hidden"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-14tjw7f-singleValue"
>
<span
class="Label color1 Label-single-select"
>
<span
class="Label-text"
>
value 1
</span>
</span>
</div>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-4-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class=" css-tpaeio-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
/>
</svg>
</div>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-19sxey8-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div
class="octo-table-cell"
style="width: 100px;"
>
<div
class="ValueSelector css-2b097c-container"
>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
>
<span
id="aria-selection"
/>
<span
id="aria-context"
>
Select is focused ,type to refine list, press Down to open the menu,
</span>
</span>
<div
class=" css-18140j1-Control"
>
<div
class=" css-5conz1-ValueContainer"
>
<div
class=" css-1wa3eu0-placeholder"
/>
<div
class="css-1shkodo-Input"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-5-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1hb7zxy-IndicatorsContainer"
>
<span
class=" css-43ykx9-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-hl9mox-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/table/TableRows should match snapshot, fire events 1`] = `
<div>
<div
class="TableRow octo-table-row"
draggable="true"
style="opacity: 1;"
>
<div
class="octo-table-cell title-cell"
id="mainBoardHeader"
style="width: 100px;"
>
<div
class="octo-icontitle"
>
<div
class="octo-icon"
>
i
</div>
<input
class="Editable undefined"
placeholder="Untitled"
spellcheck="true"
title="title"
value="title"
/>
</div>
<div
class="open-button"
>
<button
class="Button "
type="button"
>
Open
</button>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,139 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import Table from './table'
global.fetch = FetchMock.fn
beforeEach(() => {
FetchMock.fn.mockReset()
})
const wrapProviders = (children: any) => {
return (
<DndProvider backend={HTML5Backend}>
<IntlProvider locale='en'>{children}</IntlProvider>
</DndProvider>
)
}
describe('components/table/Table', () => {
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
view.viewType = 'table'
view.groupById = undefined
view.visiblePropertyIds = ['property1', 'property2']
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
test('should match snapshot', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).not.toBeUndefined()
if (!boardTree) {
fail('sync')
}
expect(FetchMock.fn).toBeCalledTimes(1)
expect(boardTree.cards).toBeDefined()
expect(boardTree.cards).toEqual([card])
const callback = jest.fn()
const addCard = jest.fn()
const component = wrapProviders(
<Table
boardTree={boardTree!}
selectedCardIds={[]}
readonly={false}
cardIdToFocusOnRender=''
showCard={callback}
addCard={addCard}
onCardClicked={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, read-only', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const callback = jest.fn()
const addCard = jest.fn()
const component = wrapProviders(
<Table
boardTree={boardTree!}
selectedCardIds={[]}
readonly={true}
cardIdToFocusOnRender=''
showCard={callback}
addCard={addCard}
onCardClicked={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot with GroupBy', async () => {
// Sync
view.groupById = 'property1'
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).not.toBeUndefined()
if (!boardTree) {
fail('sync')
}
expect(FetchMock.fn).toBeCalledTimes(1)
expect(boardTree.cards).toBeDefined()
expect(boardTree.cards).toEqual([card])
const callback = jest.fn()
const addCard = jest.fn()
const component = wrapProviders(
<Table
boardTree={boardTree!}
selectedCardIds={[]}
readonly={false}
cardIdToFocusOnRender=''
showCard={callback}
addCard={addCard}
onCardClicked={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
})

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react'
import {FormattedMessage, IntlShape} from 'react-intl'
import {FormattedMessage, useIntl} from 'react-intl'
import {useDrop, useDragLayer} from 'react-dnd'
import {IPropertyOption, IPropertyTemplate} from '../../blocks/board'
@ -26,7 +26,6 @@ type Props = {
selectedCardIds: string[]
readonly: boolean
cardIdToFocusOnRender: string
intl: IntlShape
showCard: (cardId?: string) => void
addCard: (groupByOptionId?: string) => Promise<void>
onCardClicked: (e: React.MouseEvent, card: Card) => void
@ -36,6 +35,7 @@ const Table = (props: Props) => {
const {boardTree} = props
const {board, cards, activeView, visibleGroups} = boardTree
const isManualSort = activeView.sortOptions.length < 1
const intl = useIntl()
const {offset, resizingColumn} = useDragLayer((monitor) => {
if (monitor.getItemType() === 'horizontalGrip') {
@ -84,7 +84,7 @@ const Table = (props: Props) => {
if (!template) {
return
}
displayValue = (OctoUtils.propertyDisplayValue(card, card.properties[columnID], template, props.intl) || '') as string
displayValue = (OctoUtils.propertyDisplayValue(card, card.properties[columnID], template, intl) || '') as string
if (template.type === 'select') {
displayValue = displayValue.toUpperCase()
}
@ -281,7 +281,6 @@ const Table = (props: Props) => {
key={group.option.id}
boardTree={boardTree}
group={group}
intl={props.intl}
readonly={props.readonly}
columnRefs={columnRefs}
selectedCardIds={props.selectedCardIds}
@ -307,7 +306,6 @@ const Table = (props: Props) => {
selectedCardIds={props.selectedCardIds}
readonly={props.readonly}
cardIdToFocusOnRender={props.cardIdToFocusOnRender}
intl={props.intl}
showCard={props.showCard}
addCard={props.addCard}
onCardClicked={props.onCardClicked}

View file

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import React from 'react'
import {IntlShape} from 'react-intl'
import {useDrop} from 'react-dnd'
@ -16,7 +15,6 @@ import TableRows from './tableRows'
type Props = {
boardTree: BoardTree
group: BoardTreeGroup
intl: IntlShape
readonly: boolean
columnRefs: Map<string, React.RefObject<HTMLDivElement>>
selectedCardIds: string[]
@ -61,7 +59,6 @@ const TableGroup = React.memo((props: Props): JSX.Element => {
<TableGroupHeaderRow
group={group}
boardTree={boardTree}
intl={props.intl}
hideGroup={props.hideGroup}
addCard={props.addCard}
readonly={props.readonly}
@ -77,7 +74,6 @@ const TableGroup = React.memo((props: Props): JSX.Element => {
selectedCardIds={props.selectedCardIds}
readonly={props.readonly}
cardIdToFocusOnRender={props.cardIdToFocusOnRender}
intl={props.intl}
showCard={props.showCard}
addCard={props.addCard}
onCardClicked={props.onCardClicked}

View file

@ -0,0 +1,230 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {fireEvent, render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {act} from 'react-dom/test-utils'
import userEvent from '@testing-library/user-event'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import TableGroupHeaderRowElement from './tableGroupHeaderRow'
global.fetch = FetchMock.fn
beforeEach(() => {
FetchMock.fn.mockReset()
})
const wrapProviders = (children: any) => {
return (
<DndProvider backend={HTML5Backend}>
<IntlProvider locale='en'>{children}</IntlProvider>
</DndProvider>
)
}
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
const boardTreeNoGroup = {
option: {
id: '',
value: '',
color: 'color1',
},
cards: [],
}
const boardTreeGroup = {
option: {
id: 'value1',
value: 'value 1',
color: 'color1',
},
cards: [],
}
test('should match snapshot, no groups', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeNoGroup}
readonly={false}
hideGroup={jest.fn()}
addCard={jest.fn()}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot with Group', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeGroup}
readonly={false}
hideGroup={jest.fn()}
addCard={jest.fn()}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot on read only', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeGroup}
readonly={true}
hideGroup={jest.fn()}
addCard={jest.fn()}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, hide group', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const hideGroup = jest.fn()
view.collapsedOptionIds = [boardTreeGroup.option.id]
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeGroup}
readonly={false}
hideGroup={hideGroup}
addCard={jest.fn()}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
const triangle = container.querySelector('svg.DisclosureTriangleIcon')
expect(triangle).not.toBeNull()
act(() => {
fireEvent.click(triangle as Element)
})
expect(hideGroup).toBeCalled()
expect(container).toMatchSnapshot()
})
test('should match snapshot, add new', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const addNew = jest.fn()
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeGroup}
readonly={false}
hideGroup={jest.fn()}
addCard={addNew}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
const triangle = container.querySelector('svg.AddIcon')
expect(triangle).not.toBeNull()
act(() => {
fireEvent.click(triangle as Element)
})
expect(addNew).toBeCalled()
expect(container).toMatchSnapshot()
})
test('should match snapshot, edit title', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableGroupHeaderRowElement
boardTree={boardTree!}
group={boardTreeGroup}
readonly={false}
hideGroup={jest.fn()}
addCard={jest.fn()}
propertyNameChanged={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container, getByTitle} = render(component)
const input = getByTitle(/value 1/)
act(() => {
userEvent.click(input)
userEvent.keyboard('{enter}')
})
expect(container).toMatchSnapshot()
})

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import React, {useState, useEffect} from 'react'
import {FormattedMessage, IntlShape} from 'react-intl'
import {FormattedMessage, useIntl} from 'react-intl'
import {Constants} from '../../constants'
import {IPropertyOption} from '../../blocks/board'
@ -24,7 +24,6 @@ import Label from '../../widgets/label'
type Props = {
boardTree: BoardTree
group: BoardTreeGroup
intl: IntlShape
readonly: boolean
hideGroup: (groupByOptionId: string) => void
addCard: (groupByOptionId?: string) => Promise<void>
@ -33,11 +32,12 @@ type Props = {
}
const TableGroupHeaderRow = React.memo((props: Props): JSX.Element => {
const {boardTree, intl, group} = props
const {boardTree, group} = props
const {activeView} = boardTree
const [groupTitle, setGroupTitle] = useState(group.option.value)
const [isDragging, isOver, groupHeaderRef] = useSortable('groupHeader', group.option, !props.readonly, props.onDrop)
const intl = useIntl()
useEffect(() => {
setGroupTitle(group.option.value)

View file

@ -0,0 +1,68 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import TableHeader from './tableHeader'
global.fetch = FetchMock.fn
beforeEach(() => {
FetchMock.fn.mockReset()
})
const wrapProviders = (children: any) => {
return (
<DndProvider backend={HTML5Backend}>
<IntlProvider locale='en'>{children}</IntlProvider>
</DndProvider>
)
}
describe('components/table/TableHeaderMenu', () => {
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
test('should match snapshot, title column', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const onAutoSizeColumn = jest.fn()
const component = wrapProviders(
<TableHeader
readonly={false}
sorted={'none'}
name={'my Name'}
boardTree={boardTree!}
template={board.cardProperties[0]}
offset={0}
onDrop={jest.fn()}
onAutoSizeColumn={onAutoSizeColumn}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
})

View file

@ -0,0 +1,119 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {fireEvent, render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {Constants} from '../../constants'
import mutator from '../../mutator'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import TableHeaderMenu from './tableHeaderMenu'
global.fetch = FetchMock.fn
// import mutator from '../../mutator'
jest.mock('../../mutator', () => ({
changeViewSortOptions: jest.fn(),
insertPropertyTemplate: jest.fn(),
changeViewVisibleProperties: jest.fn(),
duplicatePropertyTemplate: jest.fn(),
deleteProperty: jest.fn(),
}))
beforeEach(() => {
jest.resetAllMocks()
FetchMock.fn.mockReset()
})
const wrapIntl = (children: any) => <IntlProvider locale='en'>{children}</IntlProvider>
describe('components/table/TableHeaderMenu', () => {
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
test('should match snapshot, title column', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapIntl(
<TableHeaderMenu
templateId={Constants.titleColumnId}
boardTree={boardTree!}
/>,
)
const {container, getByText} = render(component)
let sort = getByText(/Sort ascending/i)
fireEvent.click(sort)
sort = getByText(/Sort descending/i)
fireEvent.click(sort)
expect(mutator.changeViewSortOptions).toHaveBeenCalledTimes(2)
let insert = getByText(/Insert left/i)
fireEvent.click(insert)
insert = getByText(/Insert right/i)
fireEvent.click(insert)
expect(mutator.insertPropertyTemplate).toHaveBeenCalledTimes(0)
expect(container).toMatchSnapshot()
})
test('should match snapshot, other column', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapIntl(
<TableHeaderMenu
templateId={'property 1'}
boardTree={boardTree!}
/>,
)
const {container, getByText} = render(component)
let sort = getByText(/Sort ascending/i)
fireEvent.click(sort)
sort = getByText(/Sort descending/i)
fireEvent.click(sort)
expect(mutator.changeViewSortOptions).toHaveBeenCalledTimes(2)
let insert = getByText(/Insert left/i)
fireEvent.click(insert)
insert = getByText(/Insert right/i)
fireEvent.click(insert)
expect(mutator.insertPropertyTemplate).toHaveBeenCalledTimes(2)
const hide = getByText(/Hide/i)
fireEvent.click(hide)
expect(mutator.changeViewVisibleProperties).toHaveBeenCalled()
const duplicate = getByText(/Duplicate/i)
fireEvent.click(duplicate)
expect(mutator.duplicatePropertyTemplate).toHaveBeenCalled()
const del = getByText(/Delete/i)
fireEvent.click(del)
expect(mutator.deleteProperty).toHaveBeenCalled()
expect(container).toMatchSnapshot()
})
})

View file

@ -0,0 +1,211 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import TableRow from './tableRow'
global.fetch = FetchMock.fn
beforeEach(() => {
FetchMock.fn.mockReset()
})
const wrapProviders = (children: any) => {
return (
<DndProvider backend={HTML5Backend}>
<IntlProvider locale='en'>{children}</IntlProvider>
</DndProvider>
)
}
describe('components/table/TableRow', () => {
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
test('should match snapshot', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={false}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={false}
offset={0}
resizingColumn={''}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, read-only', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={false}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={true}
offset={0}
resizingColumn={''}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, isSelected', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={true}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={false}
offset={0}
resizingColumn={''}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, collapsed tree', async () => {
// Sync
view.collapsedOptionIds = ['value1']
view.hiddenOptionIds = []
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={false}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={false}
offset={0}
resizingColumn={''}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, display properties', async () => {
// Sync
view.visiblePropertyIds = ['property1', 'property2']
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={false}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={false}
offset={0}
resizingColumn={''}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
test('should match snapshot, resizing column', async () => {
// Sync
view.visiblePropertyIds = ['property1', 'property2']
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const component = wrapProviders(
<TableRow
boardTree={boardTree!}
card={card}
isSelected={false}
focusOnMount={false}
onSaveWithEnter={jest.fn()}
showCard={jest.fn()}
readonly={false}
offset={0}
resizingColumn={'property1'}
columnRefs={new Map()}
onDrop={jest.fn()}
/>,
)
const {container} = render(component)
expect(container).toMatchSnapshot()
})
})

View file

@ -0,0 +1,90 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {fireEvent, render} from '@testing-library/react'
import '@testing-library/jest-dom'
import {IntlProvider} from 'react-intl'
import 'isomorphic-fetch'
import {act} from 'react-dom/test-utils'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import userEvent from '@testing-library/user-event'
import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {MutableBoardTree} from '../../viewModel/boardTree'
import TableRows from './tableRows'
global.fetch = FetchMock.fn
beforeEach(() => {
FetchMock.fn.mockReset()
})
const wrapProviders = (children: any) => {
return (
<DndProvider backend={HTML5Backend}>
<IntlProvider locale='en'>{children}</IntlProvider>
</DndProvider>
)
}
describe('components/table/TableRows', () => {
const board = TestBlockFactory.createBoard()
const view = TestBlockFactory.createBoardView(board)
const view2 = TestBlockFactory.createBoardView(board)
view2.sortOptions = []
const card = TestBlockFactory.createCard(board)
const cardTemplate = TestBlockFactory.createCard(board)
cardTemplate.isTemplate = true
test('should match snapshot, fire events', async () => {
// Sync
FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify([board, view, view2, card, cardTemplate])))
const boardTree = await MutableBoardTree.sync(board.id, view.id)
expect(boardTree).toBeDefined()
expect(FetchMock.fn).toBeCalledTimes(1)
const callback = jest.fn()
const addCard = jest.fn()
const component = wrapProviders(
<TableRows
boardTree={boardTree!}
columnRefs={new Map()}
cards={[card]}
selectedCardIds={[]}
readonly={false}
cardIdToFocusOnRender=''
showCard={callback}
addCard={addCard}
onCardClicked={jest.fn()}
onDrop={jest.fn()}
/>,
)
const {container, getByTitle, getByText} = render(<DndProvider backend={HTML5Backend}>{component}</DndProvider>)
const open = getByText(/Open/i)
fireEvent.click(open)
expect(callback).toBeCalled()
const input = getByTitle(/title/)
act(() => {
userEvent.click(input)
userEvent.keyboard('{enter}')
})
expect(addCard).toBeCalled()
expect(container).toMatchSnapshot()
})
})

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {IntlShape} from 'react-intl'
import {useDragLayer} from 'react-dnd'
import {Card} from '../../blocks/card'
@ -18,11 +17,11 @@ type Props = {
selectedCardIds: string[]
readonly: boolean
cardIdToFocusOnRender: string
intl: IntlShape
showCard: (cardId?: string) => void
addCard: (groupByOptionId?: string) => Promise<void>
onCardClicked: (e: React.MouseEvent, card: Card) => void
onDrop: (srcCard: Card, dstCard: Card) => void}
onDrop: (srcCard: Card, dstCard: Card) => void
}
const TableRows = (props: Props) => {
const {boardTree, cards} = props