@@ -8,6 +8,7 @@ import TableHeaderCell from "../../src/TableHeaderCell.js";
88import TableHeaderCellActionAI from "../../src/TableHeaderCellActionAI.js" ;
99import Label from "../../src/Label.js" ;
1010import Input from "../../src/Input.js" ;
11+ import TableRowAction from "../../src/TableRowAction.js" ;
1112import Bar from "../../src/Bar.js" ;
1213import Title from "../../src/Title.js" ;
1314import Slider from "../../src/Slider.js" ;
@@ -429,8 +430,6 @@ describe("Table - Popin Mode", () => {
429430 cy . get ( "ui5-table" ) . then ( $table => {
430431 $table . css ( "width" , "150px" ) ;
431432 } ) ;
432-
433- // eslint-disable-next-line cypress/no-unnecessary-waiting
434433 cy . wait ( 50 ) ;
435434
436435 cy . get ( "ui5-table" ) . then ( $table => {
@@ -443,7 +442,7 @@ describe("Table - Popin Mode", () => {
443442 for ( const cell of row . cells ) {
444443 if ( cell . _popin ) {
445444 popinCellCount ++ ;
446- const popinText = cell . _headerCell . popinText || cell . _headerCell . textContent ;
445+ const popinText = cell . _headerCell ! . popinText || cell . _headerCell ! . textContent ;
447446 if ( cell . shadowRoot ! . textContent === `${ popinText } :` ) {
448447 validPopinTextCount ++ ;
449448 }
@@ -883,26 +882,24 @@ describe("Table - Navigated Rows", () => {
883882 </ Table >
884883 ) ;
885884
886- cy . get ( "#row1" )
887- . shadow ( )
888- . find ( "#navigated-cell" )
885+ // Navigated cell rendered on all rows and header row
886+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#navigated-cell" )
889887 . should ( "exist" )
888+ . should ( "have.attr" , "aria-hidden" , "true" ) ;
889+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#navigated-cell" )
890890 . should ( "have.attr" , "data-excluded-from-navigation" ) ;
891891
892- cy . get ( "#row2" )
893- . shadow ( )
894- . find ( "#navigated-cell" )
892+ cy . get ( "#row1" ) . shadow ( ) . find ( "#navigated-cell" )
895893 . should ( "exist" )
896894 . should ( "have.attr" , "data-excluded-from-navigation" ) ;
897895
898- cy . get ( "#row1" )
899- . shadow ( )
900- . find ( "#navigated" )
901- . as ( "navigated1" ) ;
896+ cy . get ( "#row2" ) . shadow ( ) . find ( "#navigated-cell" )
897+ . should ( "exist" )
898+ . should ( "have.attr" , "data-excluded-from-navigation" ) ;
902899
903- cy . get ( "#row2" )
904- . shadow ( )
905- . find ( "#navigated" )
900+ // Navigated indicator differs between navigated and non-navigated rows
901+ cy . get ( "#row1" ) . shadow ( ) . find ( "#navigated" ) . as ( "navigated1" ) ;
902+ cy . get ( "#row2" ) . shadow ( ) . find ( "#navigated" )
906903 . then ( $navigated2 => {
907904 cy . get ( "@navigated1" )
908905 . should ( $navigated1 => {
@@ -1146,7 +1143,7 @@ describe("Table - Cell Merging", () => {
11461143 cy . wait ( 50 ) ;
11471144
11481145 // Merged cell border should fall back to normal border color (not transparent)
1149- cy . get ( "#row2" ) . should ( "have.attr" , "_haspopin " ) ;
1146+ cy . get ( "#row2" ) . should ( "have.attr" , "_has-popin " ) ;
11501147 cy . get ( "#r2cA" ) . should ( "not.have.css" , "border-top-color" , TRANSPARENT ) ;
11511148 cy . get ( "#row2" ) . shadow ( ) . find ( "#selection-cell" ) . should ( "not.have.css" , "border-top-color" , TRANSPARENT ) ;
11521149
@@ -1193,3 +1190,180 @@ describe("Table - Cell Merging", () => {
11931190 cy . get ( "#row2" ) . shadow ( ) . find ( "#selection-cell" ) . should ( "have.css" , "border-top-color" , TRANSPARENT ) ;
11941191 } ) ;
11951192} ) ;
1193+
1194+ describe ( "Table - Dummy Cell" , ( ) => {
1195+ it ( "should render dummy cell with border and nofocus when all columns have fixed widths" , ( ) => {
1196+ cy . mount (
1197+ < Table id = "table" >
1198+ < TableHeaderRow slot = "headerRow" >
1199+ < TableHeaderCell width = "200px" > Product</ TableHeaderCell >
1200+ < TableHeaderCell width = "40%" > Supplier</ TableHeaderCell >
1201+ </ TableHeaderRow >
1202+ < TableRow id = "row1" >
1203+ < TableCell > < Label > Product 1</ Label > </ TableCell >
1204+ < TableCell > < Label > Supplier 1</ Label > </ TableCell >
1205+ </ TableRow >
1206+ < TableRow id = "row2" interactive >
1207+ < TableCell > < Label > Product 2</ Label > </ TableCell >
1208+ < TableCell > < Label > Supplier 2</ Label > </ TableCell >
1209+ </ TableRow >
1210+ </ Table >
1211+ ) ;
1212+
1213+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" ) . should ( "exist" ) ;
1214+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" ) . should ( "exist" ) ;
1215+
1216+ // Should have left border
1217+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" )
1218+ . should ( "not.have.css" , "border-inline-start-width" , "0px" ) ;
1219+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1220+ . should ( "not.have.css" , "border-inline-start-width" , "0px" ) ;
1221+
1222+ // Should have data-excluded-from-navigation="nofocus" and data-border-merged when no popin
1223+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1224+ . should ( "have.attr" , "data-excluded-from-navigation" , "nofocus" )
1225+ . should ( "have.attr" , "data-border-merged" ) ;
1226+
1227+ // Should not focus row or fire row-click when clicking dummy cell
1228+ cy . get ( "#table" ) . invoke ( "on" , "row-click" , cy . stub ( ) . as ( "rowClickHandler" ) ) ;
1229+ cy . get ( "#row2" ) . shadow ( ) . find ( "#dummy-cell" ) . realClick ( ) ;
1230+ cy . get ( "#row2" ) . should ( "not.be.focused" ) ;
1231+ cy . get ( "#row2" ) . should ( "not.have.attr" , "_active" ) ;
1232+ cy . get ( "@rowClickHandler" ) . should ( "not.have.been.called" ) ;
1233+ } ) ;
1234+
1235+ it ( "should not render dummy cell when columns are flexible" , ( ) => {
1236+ const cases = [
1237+ { widths : [ "200px" , "auto" ] } ,
1238+ { widths : [ "Auto" ] } ,
1239+ { widths : [ "200px" , undefined ] } ,
1240+ ] ;
1241+
1242+ cases . forEach ( ( { widths } ) => {
1243+ cy . mount (
1244+ < Table id = "table" >
1245+ < TableHeaderRow slot = "headerRow" >
1246+ { widths . map ( ( w , i ) => (
1247+ < TableHeaderCell width = { w } > { `Col ${ i } ` } </ TableHeaderCell >
1248+ ) ) }
1249+ </ TableHeaderRow >
1250+ < TableRow id = "row1" >
1251+ { widths . map ( ( _ , i ) => (
1252+ < TableCell > < Label > { `Cell ${ i } ` } </ Label > </ TableCell >
1253+ ) ) }
1254+ </ TableRow >
1255+ </ Table >
1256+ ) ;
1257+
1258+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" ) . should ( "not.exist" ) ;
1259+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" ) . should ( "not.exist" ) ;
1260+ } ) ;
1261+ } ) ;
1262+
1263+ it ( "should render dummy cell after actions when no popin and before actions when popin" , ( ) => {
1264+ cy . mount (
1265+ < Table id = "table" rowActionCount = { 2 } overflowMode = "Popin" >
1266+ < TableHeaderRow slot = "headerRow" >
1267+ < TableHeaderCell width = "200px" minWidth = "200px" > Product</ TableHeaderCell >
1268+ < TableHeaderCell width = "200px" minWidth = "200px" > Supplier</ TableHeaderCell >
1269+ </ TableHeaderRow >
1270+ < TableRow id = "row1" >
1271+ < TableCell > < Label > Product 1</ Label > </ TableCell >
1272+ < TableCell > < Label > Supplier 1</ Label > </ TableCell >
1273+ < TableRowAction slot = "actions" icon = "edit" text = "Edit" > </ TableRowAction >
1274+ < TableRowAction slot = "actions" icon = "delete" text = "Delete" > </ TableRowAction >
1275+ </ TableRow >
1276+ </ Table >
1277+ ) ;
1278+
1279+ // No popin: dummy cell is after actions cell (rightmost) in both row and header row
1280+ cy . get ( "#row1" ) . shadow ( ) . then ( $shadow => {
1281+ const dummyCell = $shadow . find ( "#dummy-cell" ) [ 0 ] ;
1282+ const actionsCell = $shadow . find ( "#actions-cell" ) [ 0 ] ;
1283+ const position = dummyCell . compareDocumentPosition ( actionsCell ) ;
1284+ expect ( position & Node . DOCUMENT_POSITION_PRECEDING ) . to . be . greaterThan ( 0 ) ;
1285+ } ) ;
1286+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . then ( $shadow => {
1287+ const dummyCell = $shadow . find ( "#dummy-cell" ) [ 0 ] ;
1288+ const actionsCell = $shadow . find ( "#actions-cell" ) [ 0 ] ;
1289+ const position = dummyCell . compareDocumentPosition ( actionsCell ) ;
1290+ expect ( position & Node . DOCUMENT_POSITION_PRECEDING ) . to . be . greaterThan ( 0 ) ;
1291+ } ) ;
1292+
1293+ // Shrink to trigger popin: dummy cell moves before actions cell
1294+ cy . get ( "ui5-table" ) . invoke ( "css" , "width" , "250px" ) ;
1295+ cy . wait ( 50 ) ;
1296+
1297+ cy . get ( "#row1" ) . should ( "have.attr" , "_has-popin" ) ;
1298+ cy . get ( "#row1" ) . shadow ( ) . then ( $shadow => {
1299+ const dummyCell = $shadow . find ( "#dummy-cell" ) [ 0 ] ;
1300+ const actionsCell = $shadow . find ( "#actions-cell" ) [ 0 ] ;
1301+ const position = dummyCell . compareDocumentPosition ( actionsCell ) ;
1302+ expect ( position & Node . DOCUMENT_POSITION_FOLLOWING ) . to . be . greaterThan ( 0 ) ;
1303+ } ) ;
1304+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . then ( $shadow => {
1305+ const dummyCell = $shadow . find ( "#dummy-cell" ) [ 0 ] ;
1306+ const actionsCell = $shadow . find ( "#actions-cell" ) [ 0 ] ;
1307+ const position = dummyCell . compareDocumentPosition ( actionsCell ) ;
1308+ expect ( position & Node . DOCUMENT_POSITION_FOLLOWING ) . to . be . greaterThan ( 0 ) ;
1309+ } ) ;
1310+ } ) ;
1311+
1312+ it ( "should adapt dummy cell border, navigation attribute, and custom focus outline with popin" , ( ) => {
1313+ cy . mount (
1314+ < Table id = "table" overflowMode = "Popin" >
1315+ < TableSelectionMulti slot = "features" />
1316+ < TableHeaderRow slot = "headerRow" >
1317+ < TableHeaderCell width = "200px" minWidth = "200px" > Product</ TableHeaderCell >
1318+ < TableHeaderCell width = "200px" minWidth = "200px" > Supplier</ TableHeaderCell >
1319+ </ TableHeaderRow >
1320+ < TableRow id = "row1" >
1321+ < TableCell > < Label > Product 1</ Label > </ TableCell >
1322+ < TableCell > < Label > Supplier 1</ Label > </ TableCell >
1323+ </ TableRow >
1324+ </ Table >
1325+ ) ;
1326+
1327+ // At full width: left border visible, nofocus attribute on both row and header row
1328+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1329+ . should ( "not.have.css" , "border-inline-start-width" , "0px" )
1330+ . should ( "have.attr" , "data-excluded-from-navigation" , "nofocus" ) ;
1331+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" )
1332+ . should ( "have.attr" , "data-excluded-from-navigation" , "nofocus" ) ;
1333+
1334+ // Focus row - custom outline should be applied
1335+ cy . get ( "#row1" ) . should ( "have.attr" , "_render-dummy-cell" ) ;
1336+ cy . get ( "#row1" ) . should ( "not.have.attr" , "_has-popin" ) ;
1337+ cy . realPress ( "Tab" ) ;
1338+ cy . get ( "#row1" ) . shadow ( ) . find ( "[data-ui5-custom-outline='start']" ) . should ( "exist" ) ;
1339+ cy . get ( "#row1" ) . find ( "[data-ui5-custom-outline='end']" ) . should ( "exist" ) ;
1340+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1341+ . should ( "not.have.attr" , "data-ui5-custom-outline" ) ;
1342+
1343+ // Shrink to trigger popin to test custom outline is removed
1344+ cy . get ( "ui5-table" ) . invoke ( "css" , "width" , "250px" ) ;
1345+ cy . wait ( 50 ) ;
1346+
1347+ cy . get ( "#row1" ) . should ( "have.attr" , "_has-popin" ) ;
1348+
1349+ // Left border removed, data-excluded-from-navigation without nofocus
1350+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1351+ . should ( "have.css" , "border-inline-start-width" , "0px" )
1352+ . should ( "have.attr" , "data-excluded-from-navigation" , "" ) ;
1353+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" )
1354+ . should ( "have.attr" , "data-excluded-from-navigation" , "" ) ;
1355+
1356+ // Expand back - border and nofocus should return, custom outline reapplied via onAfterRendering
1357+ cy . get ( "ui5-table" ) . invoke ( "css" , "width" , "800px" ) ;
1358+ cy . wait ( 50 ) ;
1359+
1360+ cy . get ( "#row1" ) . should ( "not.have.attr" , "_has-popin" ) ;
1361+ cy . get ( "#row1" ) . shadow ( ) . find ( "#dummy-cell" )
1362+ . should ( "not.have.css" , "border-inline-start-width" , "0px" )
1363+ . should ( "have.attr" , "data-excluded-from-navigation" , "nofocus" ) ;
1364+ cy . get ( "[ui5-table-header-row]" ) . shadow ( ) . find ( "#dummy-cell" )
1365+ . should ( "have.attr" , "data-excluded-from-navigation" , "nofocus" ) ;
1366+ cy . get ( "#row1" ) . shadow ( ) . find ( "[data-ui5-custom-outline='start']" ) . should ( "exist" ) ;
1367+ cy . get ( "#row1" ) . find ( "[data-ui5-custom-outline='end']" ) . should ( "exist" ) ;
1368+ } ) ;
1369+ } ) ;
0 commit comments