Skip to content

Commit fa49ac2

Browse files
rkaraivanovddaribo
andauthored
refactor(splitter): Improved ARIA semantics and code organization (#2145)
Replace `[start-collapsed]` / `[end-collapsed]` reflected attributes with CSS custom states via ElementInternals (`addInternalsController`). Collapsed panes are now exposed as `:state(start-collapsed)` / `:state(end-collapsed)`, keeping the public API surface clean. - Extract internal types (`PanePosition`, `SplitterPaneState`, `PaneResizeSnapshot`, `SplitterResizeState`, event maps) into a dedicated `types.ts` module - Embed `StyleInfo` directly in `SplitterPaneState` to eliminate separate `_startPaneInternalStyles` / `_endPaneInternalStyles` / `_barInternalStyles` fields - Replace mutation-by-spread with direct property assignment on pane state objects - Rename `_barRef` → `_separatorRef` for semantic clarity; remove `_updateCursor()` (cursor now set inline via `styleMap` in `_renderSeparator()`) - Unify `_handleExpanderStartAction` / `_handleExpanderEndAction` into `_handleExpanderAction(pane)` and simplify `_resolvePartNames` - Add `_isCollapsed(which)`, `_getPaneState(which)`, and `_isHorizontal` helpers to reduce repetitive conditionals - Extract `_renderSeparator()` and `_renderAccessibleLabel()` render helpers; add an `igc-visually-hidden` label announcing current collapse state to assistive tech; set `aria-labelledby` on the separator element - Pass pre-computed `totalSize` into `_createPaneState`, `_setMinMaxInPx`, `_setPaneMinMaxSizes`, etc. to avoid redundant `getBoundingClientRect` calls - Register `IgcVisuallyHiddenComponent` as a sub-dependency - Extract `KEYBOARD_RESIZE_STEP = 10` constant - Update base SCSS and all spec assertions to use `:state(...)` selectors - Expand Storybook: add `Vertical`, `WithConstraints`, and `ProgrammaticCollapse` stories; improve all control and component descriptions; fix `NestedSplitters` to pass inner splitters directly into named slots Co-authored-by: Bozhidara Pachilova <35433937+ddaribo@users.noreply.github.com>
1 parent 08b4475 commit fa49ac2

5 files changed

Lines changed: 717 additions & 551 deletions

File tree

src/components/splitter/splitter.spec.ts

Lines changed: 62 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -603,32 +603,32 @@ describe('Splitter', () => {
603603
it('should expand/collapse panes when toggle is invoked', async () => {
604604
splitter.toggle('start');
605605
await elementUpdated(splitter);
606-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
606+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
607607

608608
splitter.toggle('start');
609609
await elementUpdated(splitter);
610-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
610+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
611611

612612
splitter.toggle('end');
613613
await elementUpdated(splitter);
614-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
614+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
615615

616616
// Single collapsed pane constraint
617617
splitter.toggle('start');
618618
await elementUpdated(splitter);
619-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
619+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
620620

621621
splitter.toggle('start');
622622
await elementUpdated(splitter);
623-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
623+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
624624

625625
splitter.toggle('end');
626626
await elementUpdated(splitter);
627-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
627+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
628628

629629
splitter.toggle('start');
630630
await elementUpdated(splitter);
631-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
631+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
632632
});
633633

634634
it('should restore pane sizes as percentages after collapse then expand', async () => {
@@ -675,7 +675,7 @@ describe('Splitter', () => {
675675

676676
parts = getButtonParts(splitter);
677677

678-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
678+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
679679
expect(parts.startCollapseBtn).to.be.null;
680680
expect(parts.endCollapseBtn.hidden).to.be.true;
681681
expect(parts.startExpander).to.be.null;
@@ -701,7 +701,7 @@ describe('Splitter', () => {
701701

702702
parts = getButtonParts(splitter);
703703

704-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
704+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
705705
expect(parts.startCollapseBtn.hidden).to.be.true;
706706
expect(parts.startExpander.hidden).to.be.false;
707707
expect(parts.endCollapseBtn).to.be.null;
@@ -713,8 +713,8 @@ describe('Splitter', () => {
713713

714714
parts = getButtonParts(splitter);
715715

716-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
717-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
716+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
717+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
718718
expect(parts.startCollapseBtn.hidden).to.be.false;
719719
expect(parts.endCollapseBtn.hidden).to.be.false;
720720
expect(parts.startExpander).to.be.null;
@@ -1152,7 +1152,7 @@ describe('Splitter', () => {
11521152

11531153
expect(currentSizes.startSize).to.equal(0);
11541154
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
1155-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
1155+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
11561156

11571157
// Focus is not lost during collapse
11581158
expect(bar.getAttribute('tabindex')).to.equal('0');
@@ -1166,8 +1166,8 @@ describe('Splitter', () => {
11661166
expect(currentSizes.endSize).to.equal(
11671167
splitterSize - barSize - currentSizes.startSize
11681168
);
1169-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1170-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1169+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1170+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
11711171

11721172
simulateKeyboard(bar, [ctrlKey, arrowRight]);
11731173
await elementUpdated(splitter);
@@ -1176,7 +1176,7 @@ describe('Splitter', () => {
11761176

11771177
expect(currentSizes.startSize).to.equal(splitterSize - barSize);
11781178
expect(currentSizes.endSize).to.equal(0);
1179-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
1179+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
11801180
expect(bar.getAttribute('tabindex')).to.equal('0');
11811181

11821182
simulateKeyboard(bar, [ctrlKey, arrowLeft]);
@@ -1188,8 +1188,8 @@ describe('Splitter', () => {
11881188
splitterSize - barSize - currentSizes.endSize
11891189
);
11901190
expect(currentSizes.endSize).to.be.greaterThan(0);
1191-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1192-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1191+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1192+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
11931193
expect(bar.getAttribute('tabindex')).to.equal('0');
11941194
});
11951195

@@ -1211,7 +1211,7 @@ describe('Splitter', () => {
12111211

12121212
expect(currentSizes.startSize).to.equal(0);
12131213
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
1214-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
1214+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
12151215
expect(bar.getAttribute('tabindex')).to.equal('0');
12161216

12171217
simulateKeyboard(bar, [ctrlKey, arrowDown]);
@@ -1223,8 +1223,8 @@ describe('Splitter', () => {
12231223
expect(currentSizes.endSize).to.equal(
12241224
splitterSize - barSize - currentSizes.startSize
12251225
);
1226-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1227-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1226+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1227+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
12281228

12291229
simulateKeyboard(bar, [ctrlKey, arrowDown]);
12301230
await elementUpdated(splitter);
@@ -1233,7 +1233,7 @@ describe('Splitter', () => {
12331233

12341234
expect(currentSizes.startSize).to.equal(splitterSize - barSize);
12351235
expect(currentSizes.endSize).to.equal(0);
1236-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
1236+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
12371237
expect(bar.getAttribute('tabindex')).to.equal('0');
12381238

12391239
simulateKeyboard(bar, [ctrlKey, arrowUp]);
@@ -1245,8 +1245,8 @@ describe('Splitter', () => {
12451245
splitterSize - barSize - currentSizes.endSize
12461246
);
12471247
expect(currentSizes.endSize).to.be.greaterThan(0);
1248-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1249-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1248+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1249+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
12501250
});
12511251

12521252
it('should not resize when disableResize is true', async () => {
@@ -1318,15 +1318,15 @@ describe('Splitter', () => {
13181318
await elementUpdated(splitter);
13191319
await nextFrame();
13201320

1321-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1322-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1321+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1322+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
13231323

13241324
simulateKeyboard(bar, [ctrlKey, arrowRight]);
13251325
await elementUpdated(splitter);
13261326
await nextFrame();
13271327

1328-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1329-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1328+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1329+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
13301330

13311331
splitter.orientation = 'vertical';
13321332
await elementUpdated(splitter);
@@ -1335,15 +1335,15 @@ describe('Splitter', () => {
13351335
await elementUpdated(splitter);
13361336
await nextFrame();
13371337

1338-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1339-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1338+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1339+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
13401340

13411341
simulateKeyboard(bar, [ctrlKey, arrowDown]);
13421342
await elementUpdated(splitter);
13431343
await nextFrame();
13441344

1345-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1346-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1345+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1346+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
13471347
});
13481348

13491349
it('should expand/collapse panes via keyboard and API when hideCollapseButtons is true', async () => {
@@ -1367,7 +1367,7 @@ describe('Splitter', () => {
13671367

13681368
expect(currentSizes.startSize).to.equal(0);
13691369
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
1370-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
1370+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
13711371

13721372
simulateKeyboard(bar, [ctrlKey, arrowRight]);
13731373
await elementUpdated(splitter);
@@ -1378,8 +1378,8 @@ describe('Splitter', () => {
13781378
expect(currentSizes.endSize).to.equal(
13791379
splitterSize - barSize - currentSizes.startSize
13801380
);
1381-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1382-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1381+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1382+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
13831383

13841384
simulateKeyboard(bar, [ctrlKey, arrowRight]);
13851385
await elementUpdated(splitter);
@@ -1388,7 +1388,7 @@ describe('Splitter', () => {
13881388

13891389
expect(currentSizes.startSize).to.equal(splitterSize - barSize);
13901390
expect(currentSizes.endSize).to.equal(0);
1391-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
1391+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
13921392

13931393
simulateKeyboard(bar, [ctrlKey, arrowLeft]);
13941394
await elementUpdated(splitter);
@@ -1399,8 +1399,8 @@ describe('Splitter', () => {
13991399
splitterSize - barSize - currentSizes.endSize
14001400
);
14011401
expect(currentSizes.endSize).to.be.greaterThan(0);
1402-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
1403-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
1402+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
1403+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
14041404

14051405
splitter.toggle('start');
14061406
await elementUpdated(splitter);
@@ -1409,7 +1409,7 @@ describe('Splitter', () => {
14091409

14101410
expect(currentSizes.startSize).to.equal(0);
14111411
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
1412-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
1412+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
14131413
});
14141414

14151415
it('should not be able to resize a pane when it is collapsed', async () => {
@@ -2204,7 +2204,7 @@ describe('Splitter', () => {
22042204

22052205
expect(currentSizes.startSize).to.equal(0);
22062206
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
2207-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
2207+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
22082208

22092209
simulateKeyboard(bar, [ctrlKey, arrowLeft]);
22102210
await elementUpdated(splitter);
@@ -2215,8 +2215,8 @@ describe('Splitter', () => {
22152215
expect(currentSizes.endSize).to.equal(
22162216
splitterSize - barSize - currentSizes.startSize
22172217
);
2218-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
2219-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
2218+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
2219+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
22202220

22212221
simulateKeyboard(bar, [ctrlKey, arrowLeft]);
22222222
await elementUpdated(splitter);
@@ -2225,7 +2225,7 @@ describe('Splitter', () => {
22252225

22262226
expect(currentSizes.startSize).to.equal(splitterSize - barSize);
22272227
expect(currentSizes.endSize).to.equal(0);
2228-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
2228+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
22292229

22302230
simulateKeyboard(bar, [ctrlKey, arrowRight]);
22312231
await elementUpdated(splitter);
@@ -2236,8 +2236,8 @@ describe('Splitter', () => {
22362236
splitterSize - barSize - currentSizes.endSize
22372237
);
22382238
expect(currentSizes.endSize).to.be.greaterThan(0);
2239-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
2240-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
2239+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
2240+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
22412241
});
22422242

22432243
it('should expand/collapse the correct pane through the expander buttons in RTL', async () => {
@@ -2253,7 +2253,7 @@ describe('Splitter', () => {
22532253
expect(currentSizes.startSize).to.equal(0);
22542254
expect(currentSizes.endSize).to.equal(totalAvailable);
22552255

2256-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
2256+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
22572257

22582258
parts = getButtonParts(splitter);
22592259
expect(parts.startCollapseBtn.hidden).to.be.true;
@@ -2271,8 +2271,8 @@ describe('Splitter', () => {
22712271
expect(currentSizes.endSize).to.equal(
22722272
totalAvailable - currentSizes.startSize
22732273
);
2274-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
2275-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
2274+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
2275+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
22762276

22772277
parts = getButtonParts(splitter);
22782278
expect(parts.startCollapseBtn.hidden).to.be.false;
@@ -2288,7 +2288,7 @@ describe('Splitter', () => {
22882288

22892289
expect(currentSizes.startSize).to.equal(totalAvailable);
22902290
expect(currentSizes.endSize).to.equal(0);
2291-
expect(splitter.hasAttribute('end-collapsed')).to.be.true;
2291+
expect(splitter.matches(':state(end-collapsed)')).to.be.true;
22922292

22932293
parts = getButtonParts(splitter);
22942294
expect(parts.startCollapseBtn).to.be.null;
@@ -2306,8 +2306,8 @@ describe('Splitter', () => {
23062306
totalAvailable - currentSizes.endSize
23072307
);
23082308
expect(currentSizes.endSize).to.be.greaterThan(0);
2309-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
2310-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
2309+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
2310+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
23112311

23122312
parts = getButtonParts(splitter);
23132313
expect(parts.startCollapseBtn.hidden).to.be.false;
@@ -2385,7 +2385,7 @@ describe('Splitter', () => {
23852385

23862386
expect(currentSizes.startSize).to.equal(0);
23872387
expect(currentSizes.endSize).to.equal(splitterSize - barSize);
2388-
expect(splitter.hasAttribute('start-collapsed')).to.be.true;
2388+
expect(splitter.matches(':state(start-collapsed)')).to.be.true;
23892389

23902390
simulateKeyboard(bar, [ctrlKey, arrowDown]);
23912391
await elementUpdated(splitter);
@@ -2396,8 +2396,8 @@ describe('Splitter', () => {
23962396
expect(currentSizes.endSize).to.equal(
23972397
splitterSize - barSize - currentSizes.startSize
23982398
);
2399-
expect(splitter.hasAttribute('start-collapsed')).to.be.false;
2400-
expect(splitter.hasAttribute('end-collapsed')).to.be.false;
2399+
expect(splitter.matches(':state(start-collapsed)')).to.be.false;
2400+
expect(splitter.matches(':state(end-collapsed)')).to.be.false;
24012401
});
24022402
});
24032403

@@ -2491,24 +2491,24 @@ describe('Splitter', () => {
24912491
await elementUpdated(outerSplitter);
24922492
await elementUpdated(leftInnerSplitter);
24932493

2494-
expect(outerSplitter.hasAttribute('start-collapsed')).to.be.true;
2495-
expect(leftInnerSplitter.hasAttribute('start-collapsed')).to.be.false;
2496-
expect(rightInnerSplitter.hasAttribute('start-collapsed')).to.be.false;
2494+
expect(outerSplitter.matches(':state(start-collapsed)')).to.be.true;
2495+
expect(leftInnerSplitter.matches(':state(start-collapsed)')).to.be.false;
2496+
expect(rightInnerSplitter.matches(':state(start-collapsed)')).to.be.false;
24972497

24982498
leftInnerSplitter.toggle('start');
24992499
await elementUpdated(leftInnerSplitter);
25002500

2501-
expect(outerSplitter.hasAttribute('start-collapsed')).to.be.true;
2502-
expect(leftInnerSplitter.hasAttribute('start-collapsed')).to.be.true;
2503-
expect(rightInnerSplitter.hasAttribute('start-collapsed')).to.be.false;
2501+
expect(outerSplitter.matches(':state(start-collapsed)')).to.be.true;
2502+
expect(leftInnerSplitter.matches(':state(start-collapsed)')).to.be.true;
2503+
expect(rightInnerSplitter.matches(':state(start-collapsed)')).to.be.false;
25042504

25052505
outerSplitter.toggle('start');
25062506
await elementUpdated(outerSplitter);
25072507
await elementUpdated(leftInnerSplitter);
25082508

2509-
expect(outerSplitter.hasAttribute('start-collapsed')).to.be.false;
2510-
expect(leftInnerSplitter.hasAttribute('start-collapsed')).to.be.true;
2511-
expect(rightInnerSplitter.hasAttribute('start-collapsed')).to.be.false;
2509+
expect(outerSplitter.matches(':state(start-collapsed)')).to.be.false;
2510+
expect(leftInnerSplitter.matches(':state(start-collapsed)')).to.be.true;
2511+
expect(rightInnerSplitter.matches(':state(start-collapsed)')).to.be.false;
25122512
});
25132513

25142514
it('should not interfere with parent/child resize operations', async () => {

0 commit comments

Comments
 (0)