From 55d41a2865c802b211db6c0dc01cbdfe29a3c664 Mon Sep 17 00:00:00 2001 From: Amy Cobb Date: Tue, 10 Dec 2024 09:35:38 -0500 Subject: [PATCH 1/3] accessibility updates - alt text, tabbing, screen reader dates, filter functionality --- .../main/default/classes/TimelineService.cls | 7 + .../labels/CustomLabels.labels-meta.xml | 8 + .../main/default/lwc/timeline/timeline.css | 15 ++ .../main/default/lwc/timeline/timeline.html | 139 ++++++---------- .../main/default/lwc/timeline/timeline.js | 157 ++++++++++++++++-- 5 files changed, 222 insertions(+), 104 deletions(-) diff --git a/force-app/main/default/classes/TimelineService.cls b/force-app/main/default/classes/TimelineService.cls index 35f3506..dce7739 100644 --- a/force-app/main/default/classes/TimelineService.cls +++ b/force-app/main/default/classes/TimelineService.cls @@ -128,6 +128,7 @@ public with sharing class TimelineService { 'Parent_Object__c =:parentConfigType'; //NOPMD List listOfTimelineConfigurations = Database.query(queryTimelineConfiguration); //NOPMD + Map timelineConfigObjectName = new Map ([SELECT Id, Object_Name__c FROM Timeline_Configuration__mdt]); if (listofTimelineConfigurations.size() < 1) { String errorMsg = @@ -141,6 +142,10 @@ public with sharing class TimelineService { for (Timeline_Configuration__mdt timelineConfigurationRecord : listOfTimelineConfigurations) { TimelineRecord timelineRecord = new timelineRecord(); + //String timelineObjectLabel = timelineConfigObjectName.get(timelineConfigurationRecord.Id) + Schema.SObjectType timelineRecordObjectType = Schema.getGlobalDescribe().get(timelineConfigurationRecord.Object_Name__c); + Schema.DescribeSObjectResult timelineDescribeResult = timelineRecordObjectType.getDescribe(); + timelineRecord.objectLabel = timelineDescribeResult.getLabel(); timelineRecord.relationshipName = timelineConfigurationRecord.Relationship_Name__c; timelineRecord.icon = timelineConfigurationRecord.Icon__c; timelineRecord.iconBackground = timelineConfigurationRecord.Icon_Background_Colour__c; @@ -350,6 +355,7 @@ public with sharing class TimelineService { mapData.put('positionDateValue', positionValues.get('value')); mapData.put('positionDateType', positionValues.get('type')); mapData.put('objectName', tr.objectName); + mapData.put('objectLabel', tr.objectLabel); mapData.put('fallbackTooltipField', fallbackValues.get('label')); mapData.put('fallbackTooltipValue', fallbackValues.get('value')); mapData.put('drilldownId', drilldownIdValues.get('value')); @@ -640,6 +646,7 @@ public with sharing class TimelineService { private String positionDateValue; private String positionDateType; private String objectName; + private String objectLabel; private String type; private String tooltipIdField; private String tooltipObject; diff --git a/force-app/main/default/labels/CustomLabels.labels-meta.xml b/force-app/main/default/labels/CustomLabels.labels-meta.xml index 72a897d..73ad7bf 100644 --- a/force-app/main/default/labels/CustomLabels.labels-meta.xml +++ b/force-app/main/default/labels/CustomLabels.labels-meta.xml @@ -104,6 +104,14 @@ Timeline_Label_Filter_Type_Legend Types to Show + + Timeline_Label_Filter_Date_Legend + Timeline + en_US + true + Timeline_Label_Filter_Date_Legend + Dates in View + Timeline_Label_Filters Timeline diff --git a/force-app/main/default/lwc/timeline/timeline.css b/force-app/main/default/lwc/timeline/timeline.css index 3250d6b..727febc 100644 --- a/force-app/main/default/lwc/timeline/timeline.css +++ b/force-app/main/default/lwc/timeline/timeline.css @@ -306,3 +306,18 @@ .timeline-summary-short { display: none; } + +.sr-only { + font-size: 0; +} + +.sticky-button { + position: sticky; + bottom: 0; + background-color: white; + padding: 5px; +} + +.slds-panel__body { + padding-bottom: 50px; +} \ No newline at end of file diff --git a/force-app/main/default/lwc/timeline/timeline.html b/force-app/main/default/lwc/timeline/timeline.html index e4d5c40..cd130c1 100755 --- a/force-app/main/default/lwc/timeline/timeline.html +++ b/force-app/main/default/lwc/timeline/timeline.html @@ -6,7 +6,7 @@
- +
- + \ No newline at end of file diff --git a/force-app/main/default/lwc/timeline/timeline.js b/force-app/main/default/lwc/timeline/timeline.js index d2c71e3..04f28c2 100755 --- a/force-app/main/default/lwc/timeline/timeline.js +++ b/force-app/main/default/lwc/timeline/timeline.js @@ -26,6 +26,7 @@ import SHOWING from '@salesforce/label/c.Timeline_Label_Showing'; import ITEMS from '@salesforce/label/c.Timeline_Label_Items'; import FILTERS from '@salesforce/label/c.Timeline_Label_Filters'; import TYPE_LEGEND from '@salesforce/label/c.Timeline_Label_Filter_Type_Legend'; +import DATE_LEGEND from '@salesforce/label/c.Timeline_Label_Filter_Date_Legend'; import DATE_RANGE_LEGEND from '@salesforce/label/c.Timeline_Label_Date_Range_Legend'; import FILE_TYPE from '@salesforce/label/c.Timeline_Label_Files'; import ALL_TYPES from '@salesforce/label/c.Timeline_Label_Filter_All_Types'; @@ -118,6 +119,7 @@ export default class timeline extends NavigationMixin(LightningElement) { ITEMS, FILTERS, TYPE_LEGEND, + DATE_LEGEND, DATE_RANGE_LEGEND, FILE_TYPE, ALL_TYPES, @@ -205,7 +207,6 @@ export default class timeline extends NavigationMixin(LightningElement) { this.allFilterValues.push(key); } } - this.isFilterLoaded = true; } else if (result.error) { let errorType = 'Error'; let errorHeading, @@ -446,9 +447,9 @@ export default class timeline extends NavigationMixin(LightningElement) { recordCopy.recordId = record.objectId; recordCopy.id = index; - recordCopy.label = - record.detailField.length <= 30 ? record.detailField : record.detailField.slice(0, 30) + '...'; + recordCopy.label = record.detailField; recordCopy.objectName = record.objectName; + recordCopy.objectLabel = record.objectLabel; recordCopy.positionDateField = record.positionDateField; if (record.positionDateType === 'DATE') { @@ -625,6 +626,18 @@ export default class timeline extends NavigationMixin(LightningElement) { timelineCanvas.attr('height', svgHeight - 1); timelineCanvas.SVGHeight = svgHeight; + if(me.isLanguageRightToLeft){ + timelineCanvas.append('clipPath') + .attr('id', 'clipText') + .append('polygon') + .attr('points','-215,0 0,0 0,30 -215,30'); + } + else{ + timelineCanvas.append('clipPath') + .attr('id', 'clipText') + .append('polygon') + .attr('points','0,0 215,0 215,30 0,30'); + } timelineCanvas.data = timelineCanvas .selectAll('[class~=timeline-canvas-record]') @@ -681,6 +694,10 @@ export default class timeline extends NavigationMixin(LightningElement) { .attr('y', 1) .attr('height', 22) .attr('width', 22) + .attr('aria-label', function (d){ + const altText = d.objectLabel; + return altText; + }) .attr('xlink:href', function (d) { let iconImage = ''; @@ -704,9 +721,18 @@ export default class timeline extends NavigationMixin(LightningElement) { return iconImage; }); + timelineCanvas.records + .append('text') + .attr('class', 'sr-only') + .text(function (d) { + return d.positionDateValue; + }); + + //let eye=0; timelineCanvas.records .append('text') .attr('class', 'timeline-canvas-record-label') + .attr('clip-path','url(#clipText)') .attr('x', function () { let x = 30; switch (me.isLanguageRightToLeft) { @@ -721,6 +747,7 @@ export default class timeline extends NavigationMixin(LightningElement) { }) .attr('y', 16) .attr('font-size', 12) + .attr('tabindex', '0') .on('click', function (event, d) { let drilldownId = d.recordId; if (d.drilldownId !== '') { @@ -761,6 +788,49 @@ export default class timeline extends NavigationMixin(LightningElement) { } } }) + .on('keydown', function (event, d) { + if(event.key === ' ' || event.key === 'Enter'){ + + let drilldownId = d.recordId; + if (d.drilldownId !== '') { + drilldownId = d.drilldownId; + } + + switch (d.objectName) { + case 'ContentDocumentLink': { + me[NavigationMixin.Navigate]({ + type: 'standard__namedPage', + attributes: { + pageName: 'filePreview' + }, + state: { + selectedRecordId: d.recordId + } + }); + break; + } + case 'CaseComment': { + const toastEvent = new ShowToastEvent({ + title: me.toast.NAVIGATION_HEADER, + message: me.toast.NAVIGATION_BODY, + messageData: [d.objectName] + }); + this.dispatchEvent(toastEvent); + break; + } + default: { + me[NavigationMixin.Navigate]({ + type: 'standard__recordPage', + attributes: { + recordId: drilldownId, + actionName: 'view' + } + }); + break; + } + } + } + }) .on('mouseover', function (event, d) { let tooltipId = d.recordId; let tooltipObject = d.objectName; @@ -789,21 +859,44 @@ export default class timeline extends NavigationMixin(LightningElement) { switch (me.isLanguageRightToLeft) { case true: - tipPosition = - this.getBoundingClientRect().top - - 30 + - 'px ;left:' + - (this.getBoundingClientRect().left - tooltipDIV.offsetWidth - 15) + - 'px ;visibility:visible'; - break; + if(this.getBoundingClientRect().width < 184){ + tipPosition = + this.getBoundingClientRect().top - + 30 + + 'px ;left:' + + (this.getBoundingClientRect().left - tooltipDIV.offsetWidth - 15) + + 'px ;visibility:visible'; + break; + } + else{ + tipPosition = + this.getBoundingClientRect().top - + 30 + + 'px ;left:' + + (this.getBoundingClientRect().right- tooltipDIV.offsetWidth - 215) + + 'px ;visibility:visible'; + break; + } + default: - tipPosition = - this.getBoundingClientRect().top - - 30 + - 'px ;left:' + - (this.getBoundingClientRect().right + 15) + - 'px ;visibility:visible'; - break; + if(this.getBoundingClientRect().width < 184){ + tipPosition = + this.getBoundingClientRect().top - + 30 + + 'px ;left:' + + (this.getBoundingClientRect().right + 15) + + 'px ;visibility:visible'; + break; + } + else{ + tipPosition = + this.getBoundingClientRect().top - + 30 + + 'px ;left:' + + (this.getBoundingClientRect().left + 205) + + 'px ;visibility:visible'; + break; + } } tooltipDIV.setAttribute('style', 'top:' + tipPosition); }) @@ -845,6 +938,8 @@ export default class timeline extends NavigationMixin(LightningElement) { const axis = targetSVG .insert('g', ':first-child') .attr('class', axisConfig.class + '-' + me.timelineWidth) + .attr('role', 'presentation') + .attr('aria-hidden', 'true') .call(x_axis); if (typeof axisConfig.translate === 'object') { @@ -1256,15 +1351,19 @@ export default class timeline extends NavigationMixin(LightningElement) { const filterPopover = this.template.querySelector('div.timeline-filter'); const filterClasses = String(filterPopover.classList); const refreshButton = this.template.querySelector('lightning-button-icon.timeline-refresh'); + const closeButton = this.template.querySelector('[data-id="closeDialogBtn"]'); + const filterButton = this.template.querySelector('[data-id="filterBtn"]'); if (filterClasses.includes('slds-is-open')) { refreshButton.disabled = false; filterPopover.classList.remove('slds-is-open'); this.isFilter = false; + filterButton.focus(); } else { refreshButton.disabled = true; filterPopover.classList.add('slds-is-open'); this.isFilter = true; + closeButton.focus(); } switch (this.isLanguageRightToLeft) { @@ -1298,6 +1397,16 @@ export default class timeline extends NavigationMixin(LightningElement) { this.isFilterUpdated = true; } } + //let zoomStartDateString; + handleStartDateChange(e){ + this.zoomStartDateString = e.target.value; + this.isFilterUpdated = true; + } + + handleEndDateChange(e){ + this.zoomEndDateString = e.target.value; + this.isFilterUpdated = true; + } handleAllTypesChange(e) { if (e.target.checked === true) { @@ -1334,6 +1443,20 @@ export default class timeline extends NavigationMixin(LightningElement) { } applyFilter() { + console.log('applyFilter start'); + console.log('this.filterValues: '+this.filterValues); + if (this.zoomStartDateString) { + this.zoomStartDate = new Date(this.zoomStartDateString); // Convert string to Date object + console.log('Start Date updated to:', this.zoomStartDate); + } else { + console.log('Please enter a valid startdate.'); + } + if (this.zoomEndDateString) { + this.zoomEndDate = new Date(this.zoomEndDateString); // Convert string to Date object + console.log('End Date updated to:', this.zoomEndDate); + } else { + console.log('Please enter a valid end date.'); + } this.refreshTimeline(); this.isFilterUpdated = false; this.startingFilterValues = this.filterValues; From bce0775811f6041147d72a726123f2acca47c197 Mon Sep 17 00:00:00 2001 From: Amy Cobb Date: Tue, 10 Dec 2024 13:49:21 -0500 Subject: [PATCH 2/3] removed unnecessary lines --- force-app/main/default/lwc/timeline/timeline.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/force-app/main/default/lwc/timeline/timeline.js b/force-app/main/default/lwc/timeline/timeline.js index 04f28c2..586d0b5 100755 --- a/force-app/main/default/lwc/timeline/timeline.js +++ b/force-app/main/default/lwc/timeline/timeline.js @@ -728,7 +728,6 @@ export default class timeline extends NavigationMixin(LightningElement) { return d.positionDateValue; }); - //let eye=0; timelineCanvas.records .append('text') .attr('class', 'timeline-canvas-record-label') @@ -1397,7 +1396,7 @@ export default class timeline extends NavigationMixin(LightningElement) { this.isFilterUpdated = true; } } - //let zoomStartDateString; + handleStartDateChange(e){ this.zoomStartDateString = e.target.value; this.isFilterUpdated = true; From 873c6476fbfd2e93d60b84ad4607eed09eed53ca Mon Sep 17 00:00:00 2001 From: Amy Cobb Date: Tue, 10 Dec 2024 13:58:27 -0500 Subject: [PATCH 3/3] removed unnecessary line --- force-app/main/default/classes/TimelineService.cls | 1 - 1 file changed, 1 deletion(-) diff --git a/force-app/main/default/classes/TimelineService.cls b/force-app/main/default/classes/TimelineService.cls index dce7739..e1547bf 100644 --- a/force-app/main/default/classes/TimelineService.cls +++ b/force-app/main/default/classes/TimelineService.cls @@ -142,7 +142,6 @@ public with sharing class TimelineService { for (Timeline_Configuration__mdt timelineConfigurationRecord : listOfTimelineConfigurations) { TimelineRecord timelineRecord = new timelineRecord(); - //String timelineObjectLabel = timelineConfigObjectName.get(timelineConfigurationRecord.Id) Schema.SObjectType timelineRecordObjectType = Schema.getGlobalDescribe().get(timelineConfigurationRecord.Object_Name__c); Schema.DescribeSObjectResult timelineDescribeResult = timelineRecordObjectType.getDescribe(); timelineRecord.objectLabel = timelineDescribeResult.getLabel();