@hmcts/ccd-case-ui-toolkit 7.4.3-redact → 7.4.4-default-focus

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3960,6 +3960,7 @@ class CaseTab {
3960
3960
  order;
3961
3961
  fields;
3962
3962
  show_condition;
3963
+ defaultFocus;
3963
3964
  }
3964
3965
  __decorate([
3965
3966
  Type(() => CaseField),
@@ -14766,6 +14767,9 @@ class CaseFileViewFieldComponent {
14766
14767
  ngAfterViewInit() {
14767
14768
  const slider = this.elementRef.nativeElement.querySelector('.slider');
14768
14769
  const documentTreeContainer = this.elementRef.nativeElement.querySelector('.document-tree-container');
14770
+ if (!slider || !documentTreeContainer) {
14771
+ return;
14772
+ }
14769
14773
  const mousedown$ = fromEvent(slider, 'mousedown');
14770
14774
  const mousemove$ = fromEvent(document, 'mousemove');
14771
14775
  const mouseup$ = fromEvent(document, 'mouseup');
@@ -27404,8 +27408,12 @@ class WriteRichTextAreaFieldComponent extends AbstractFieldWriteComponent {
27404
27408
  this.removeUnsupportedMarkup(documentElement);
27405
27409
  this.normaliseDateRangeSpacing(documentElement);
27406
27410
  this.removeUnsupportedAttributes(documentElement);
27411
+ const hasMeaningfulContent = this.hasMeaningfulRichTextContent(documentElement);
27412
+ if (hasMeaningfulContent) {
27413
+ this.preserveDocmosisBlankParagraphs(documentElement);
27414
+ }
27407
27415
  const sanitisedValue = sanitiseRichTextDocument(documentElement);
27408
- return this.hasMeaningfulRichTextContent(documentElement)
27416
+ return hasMeaningfulContent
27409
27417
  ? this.normalisePlainTextValue(sanitisedValue)
27410
27418
  : '';
27411
27419
  }
@@ -27425,11 +27433,22 @@ class WriteRichTextAreaFieldComponent extends AbstractFieldWriteComponent {
27425
27433
  this.removeUnsupportedMarkup(documentElement);
27426
27434
  this.normaliseDateRangeSpacing(documentElement);
27427
27435
  this.removeUnsupportedAttributes(documentElement);
27436
+ const hasMeaningfulContent = this.hasMeaningfulRichTextContent(documentElement);
27437
+ if (hasMeaningfulContent) {
27438
+ this.preserveDocmosisBlankParagraphs(documentElement);
27439
+ }
27428
27440
  const sanitisedValue = sanitiseRichTextDocument(documentElement);
27429
- return this.hasMeaningfulRichTextContent(documentElement)
27441
+ return hasMeaningfulContent
27430
27442
  ? this.normalisePlainTextValue(sanitisedValue)
27431
27443
  : '';
27432
27444
  }
27445
+ /**
27446
+ * Gives intentionally blank paragraphs content so Docmosis preserves them as blank lines.
27447
+ * This runs only when the document has meaningful content, so a completely empty field remains empty.
27448
+ */
27449
+ preserveDocmosisBlankParagraphs(documentElement) {
27450
+ this.replaceBlankParagraphContent(documentElement, '\u00a0');
27451
+ }
27433
27452
  hasMeaningfulRichTextContent(documentElement) {
27434
27453
  const visibleText = (documentElement.body.textContent || '')
27435
27454
  .replace(/[\s\u00a0\u200b-\u200d\ufeff]/g, '');
@@ -27550,7 +27569,8 @@ class WriteRichTextAreaFieldComponent extends AbstractFieldWriteComponent {
27550
27569
  emitEvent: false,
27551
27570
  emitModelToViewChange: false
27552
27571
  });
27553
- if (normalisedValue || this.editorHasMeaningfulRichTextContent()) {
27572
+ if ((normalisedValue || this.editorHasMeaningfulRichTextContent())
27573
+ && !this.differsOnlyByDocmosisBlankParagraphs(value, normalisedValue)) {
27554
27574
  this.editor.setContent(normalisedValue || '');
27555
27575
  }
27556
27576
  }
@@ -27558,6 +27578,34 @@ class WriteRichTextAreaFieldComponent extends AbstractFieldWriteComponent {
27558
27578
  this.isNormalisingValue = false;
27559
27579
  }
27560
27580
  }
27581
+ differsOnlyByDocmosisBlankParagraphs(value, normalisedValue) {
27582
+ if (!value || !normalisedValue) {
27583
+ return false;
27584
+ }
27585
+ const originalDocument = new DOMParser().parseFromString(value, 'text/html');
27586
+ const normalisedDocument = new DOMParser().parseFromString(normalisedValue, 'text/html');
27587
+ this.clearBlankParagraphs(originalDocument);
27588
+ this.clearBlankParagraphs(normalisedDocument);
27589
+ return originalDocument.body.innerHTML === normalisedDocument.body.innerHTML;
27590
+ }
27591
+ /**
27592
+ * Canonicalises visually blank paragraphs to <p></p> before comparing two HTML values.
27593
+ * This lets the editor avoid reloading when only the Docmosis blank-line representation changed.
27594
+ */
27595
+ clearBlankParagraphs(documentElement) {
27596
+ this.replaceBlankParagraphContent(documentElement, '');
27597
+ }
27598
+ replaceBlankParagraphContent(documentElement, replacement) {
27599
+ const paragraphs = Array.prototype.slice.call(documentElement.body.querySelectorAll('p'));
27600
+ paragraphs.forEach((paragraph) => {
27601
+ // Ignore standard whitespace (\s), non-breaking spaces (U+00A0), zero-width characters
27602
+ // (U+200B–U+200D), and the byte-order mark (U+FEFF) when checking for visible text.
27603
+ const visibleText = (paragraph.textContent || '').replace(/[\s\u00a0\u200b-\u200d\ufeff]/g, '');
27604
+ if (!visibleText && !paragraph.querySelector('hr')) {
27605
+ paragraph.textContent = replacement;
27606
+ }
27607
+ });
27608
+ }
27561
27609
  editorHasMeaningfulRichTextContent() {
27562
27610
  const documentNode = this.editor.view.state.doc;
27563
27611
  const visibleText = documentNode.textContent.replace(/[\s\u00a0\u200b-\u200d\ufeff]/g, '');
@@ -41457,7 +41505,12 @@ class CaseFullAccessViewComponent {
41457
41505
  let hashValue = url.substring(url.indexOf('#') + 1);
41458
41506
  const hasHash = url.includes('#');
41459
41507
  const isSpecialTabPath = url.includes('roles-and-access') || url.includes('tasks') || url.includes('hearings');
41460
- if (!hasHash && !isSpecialTabPath) {
41508
+ // TODO: Remove defaultFocusDemo once QA'd
41509
+ const defaultFocusDemo = url.includes('default-focus-demo');
41510
+ if (defaultFocusDemo) {
41511
+ this.caseDetails.tabs[Number(url.substring(url.indexOf('default-focus-demo-') + 19))].defaultFocus = true;
41512
+ }
41513
+ if (defaultFocusDemo || (!hasHash && !isSpecialTabPath)) {
41461
41514
  const paths = url.split('/');
41462
41515
  const lastPath = decodeURIComponent(paths[paths.length - 1]);
41463
41516
  let foundTab = null;
@@ -41481,10 +41534,13 @@ class CaseFullAccessViewComponent {
41481
41534
  });
41482
41535
  }
41483
41536
  else {
41484
- this.caseDetails.tabs.sort((aTab, bTab) => aTab.order > bTab.order ? 1 : (bTab.order > aTab.order ? -1 : 0));
41537
+ this.caseDetails.tabs = this.orderService.sort(this.caseDetails.tabs);
41538
+ // Presuming default focus only set for one tab here - multiple tabs with default focus are not handled as is a contradiction
41539
+ let preSelectTab = this.caseDetails.tabs.find((tab) => tab.defaultFocus === true) ?? null;
41540
+ // Attempt to pre-select the tab based on the preferred label for the current case type
41485
41541
  const preferredLabel = FieldsUtils.defaultTabList?.[this.caseDetails?.case_type?.id];
41486
- let preSelectTab = null;
41487
- if (preferredLabel) {
41542
+ // If no tab has default focus, attempt to use the preferred label for the current case type
41543
+ if (!preSelectTab && preferredLabel) {
41488
41544
  preSelectTab = this.caseDetails.tabs.find((tab) => (tab.label === preferredLabel) || (tab.id === preferredLabel)) ?? null;
41489
41545
  }
41490
41546
  if (!preSelectTab) {