@react-magma/charts 14.0.0-rc.3 → 14.0.0-rc.4
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.
- package/dist/charts.js +154 -1
- package/dist/charts.js.map +1 -1
- package/dist/charts.modern.module.js +154 -1
- package/dist/charts.modern.module.js.map +1 -1
- package/dist/charts.umd.js +606 -202
- package/dist/charts.umd.js.map +1 -1
- package/dist/hooks/useCarbonModalFocusManagement.d.ts +2 -0
- package/package.json +9 -4
- package/src/components/CarbonChart/CarbonChart.test.js +176 -1
- package/src/components/CarbonChart/CarbonChart.tsx +17 -1
- package/src/hooks/useCarbonModalFocusManagement.ts +173 -0
|
@@ -782,6 +782,148 @@ var curriedTransparentize = /*#__PURE__*/curry
|
|
|
782
782
|
/* ::<number | string, string, string> */
|
|
783
783
|
(transparentize);
|
|
784
784
|
|
|
785
|
+
var FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
786
|
+
function getFocusableElements(container) {
|
|
787
|
+
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(function (el) {
|
|
788
|
+
var style = window.getComputedStyle(el);
|
|
789
|
+
return (style.display !== 'none' &&
|
|
790
|
+
style.visibility !== 'hidden' &&
|
|
791
|
+
!el.hasAttribute('disabled'));
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
function findVisibleModal(wrapper) {
|
|
795
|
+
var modal = wrapper.querySelector('.cds--modal');
|
|
796
|
+
if (!modal)
|
|
797
|
+
return null;
|
|
798
|
+
var isVisible = modal.getAttribute('aria-modal') === 'true' ||
|
|
799
|
+
modal.style.visibility === 'visible' ||
|
|
800
|
+
modal.classList.contains('is-visible');
|
|
801
|
+
return isVisible ? modal : null;
|
|
802
|
+
}
|
|
803
|
+
function useCarbonModalFocusManagement(wrapperRef) {
|
|
804
|
+
var previouslyFocusedElement = React.useRef(null);
|
|
805
|
+
var keydownHandler = React.useRef(null);
|
|
806
|
+
var focusinHandler = React.useRef(null);
|
|
807
|
+
var currentModal = React.useRef(null);
|
|
808
|
+
React.useEffect(function () {
|
|
809
|
+
var wrapper = wrapperRef.current;
|
|
810
|
+
if (!wrapper)
|
|
811
|
+
return;
|
|
812
|
+
function focusModalCloseButton(modal) {
|
|
813
|
+
var closeButton = modal.querySelector('.cds--modal-close');
|
|
814
|
+
if (closeButton) {
|
|
815
|
+
closeButton.focus();
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
var focusable = getFocusableElements(modal);
|
|
819
|
+
if (focusable.length > 0) {
|
|
820
|
+
focusable[0].focus();
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function handleModalOpen(modal) {
|
|
825
|
+
currentModal.current = modal;
|
|
826
|
+
previouslyFocusedElement.current = document.activeElement;
|
|
827
|
+
// Permanent guard: redirect focus back into modal whenever it escapes
|
|
828
|
+
// (e.g. Carbon's overflow menu returning focus to its trigger).
|
|
829
|
+
focusinHandler.current = function (event) {
|
|
830
|
+
var target = event.target;
|
|
831
|
+
if (!modal.contains(target)) {
|
|
832
|
+
setTimeout(function () {
|
|
833
|
+
if (currentModal.current === modal) {
|
|
834
|
+
focusModalCloseButton(modal);
|
|
835
|
+
}
|
|
836
|
+
}, 0);
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
document.addEventListener('focusin', focusinHandler.current);
|
|
840
|
+
var pollAttempts = 0;
|
|
841
|
+
var pollAndFocus = function () {
|
|
842
|
+
if (currentModal.current !== modal)
|
|
843
|
+
return;
|
|
844
|
+
if (modal.contains(document.activeElement))
|
|
845
|
+
return;
|
|
846
|
+
var closeBtn = modal.querySelector('.cds--modal-close');
|
|
847
|
+
if (closeBtn &&
|
|
848
|
+
window.getComputedStyle(closeBtn).visibility !== 'hidden') {
|
|
849
|
+
closeBtn.focus();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (++pollAttempts < 30) {
|
|
853
|
+
requestAnimationFrame(pollAndFocus);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
requestAnimationFrame(pollAndFocus);
|
|
857
|
+
keydownHandler.current = function (event) {
|
|
858
|
+
if (event.key !== 'Tab')
|
|
859
|
+
return;
|
|
860
|
+
var focusable = getFocusableElements(modal);
|
|
861
|
+
if (focusable.length === 0) {
|
|
862
|
+
event.preventDefault();
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
if (focusable.length === 1) {
|
|
866
|
+
event.preventDefault();
|
|
867
|
+
if (focusable[0] !== document.activeElement) {
|
|
868
|
+
focusable[0].focus();
|
|
869
|
+
}
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
var firstItem = focusable[0];
|
|
873
|
+
var lastItem = focusable[focusable.length - 1];
|
|
874
|
+
if (!event.shiftKey && document.activeElement === lastItem) {
|
|
875
|
+
event.preventDefault();
|
|
876
|
+
firstItem.focus();
|
|
877
|
+
}
|
|
878
|
+
else if (event.shiftKey && document.activeElement === firstItem) {
|
|
879
|
+
event.preventDefault();
|
|
880
|
+
lastItem.focus();
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
document.addEventListener('keydown', keydownHandler.current);
|
|
884
|
+
}
|
|
885
|
+
function handleModalClose() {
|
|
886
|
+
// Null out currentModal first so any pending setTimeout redirects
|
|
887
|
+
// (scheduled by the focusin guard) see a closed modal and bail out.
|
|
888
|
+
currentModal.current = null;
|
|
889
|
+
if (focusinHandler.current) {
|
|
890
|
+
document.removeEventListener('focusin', focusinHandler.current);
|
|
891
|
+
focusinHandler.current = null;
|
|
892
|
+
}
|
|
893
|
+
if (keydownHandler.current) {
|
|
894
|
+
document.removeEventListener('keydown', keydownHandler.current);
|
|
895
|
+
keydownHandler.current = null;
|
|
896
|
+
}
|
|
897
|
+
if (previouslyFocusedElement.current instanceof HTMLElement) {
|
|
898
|
+
previouslyFocusedElement.current.focus();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
var observer = new MutationObserver(function () {
|
|
902
|
+
var visibleModal = findVisibleModal(wrapper);
|
|
903
|
+
if (visibleModal && !currentModal.current) {
|
|
904
|
+
handleModalOpen(visibleModal);
|
|
905
|
+
}
|
|
906
|
+
else if (!visibleModal && currentModal.current) {
|
|
907
|
+
handleModalClose();
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
observer.observe(wrapper, {
|
|
911
|
+
attributes: true,
|
|
912
|
+
attributeFilter: ['class', 'style', 'aria-modal'],
|
|
913
|
+
subtree: true,
|
|
914
|
+
});
|
|
915
|
+
return function () {
|
|
916
|
+
observer.disconnect();
|
|
917
|
+
if (keydownHandler.current) {
|
|
918
|
+
document.removeEventListener('keydown', keydownHandler.current);
|
|
919
|
+
}
|
|
920
|
+
if (focusinHandler.current) {
|
|
921
|
+
document.removeEventListener('focusin', focusinHandler.current);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}, [wrapperRef]);
|
|
925
|
+
}
|
|
926
|
+
|
|
785
927
|
function styleInject(css, ref) {
|
|
786
928
|
if ( ref === void 0 ) ref = {};
|
|
787
929
|
var insertAt = ref.insertAt;
|
|
@@ -990,6 +1132,17 @@ var CarbonChart = React.forwardRef(function (props, ref) {
|
|
|
990
1132
|
var testId = props.testId, isInverseProp = props.isInverse, type = props.type, dataSet = props.dataSet, options = props.options, ariaLabel = props.ariaLabel, rest = __rest(props, ["testId", "isInverse", "type", "dataSet", "options", "ariaLabel"]);
|
|
991
1133
|
var theme = React.useContext(ThemeContext);
|
|
992
1134
|
var isInverse = useIsInverse(isInverseProp);
|
|
1135
|
+
var internalRef = React.useRef(null);
|
|
1136
|
+
var mergedRef = React.useCallback(function (node) {
|
|
1137
|
+
internalRef.current = node;
|
|
1138
|
+
if (typeof ref === 'function') {
|
|
1139
|
+
ref(node);
|
|
1140
|
+
}
|
|
1141
|
+
else if (ref) {
|
|
1142
|
+
ref.current = node;
|
|
1143
|
+
}
|
|
1144
|
+
}, [ref]);
|
|
1145
|
+
useCarbonModalFocusManagement(internalRef);
|
|
993
1146
|
var allCharts = {
|
|
994
1147
|
area: AreaChart,
|
|
995
1148
|
areaStacked: StackedAreaChart,
|
|
@@ -1044,7 +1197,7 @@ var CarbonChart = React.forwardRef(function (props, ref) {
|
|
|
1044
1197
|
}
|
|
1045
1198
|
});
|
|
1046
1199
|
var groupsLength = Object.keys(buildColors()).length;
|
|
1047
|
-
return (React.createElement(CarbonChartWrapper, __assign({ "data-testid": testId, ref:
|
|
1200
|
+
return (React.createElement(CarbonChartWrapper, __assign({ "data-testid": testId, ref: mergedRef, isInverse: isInverse, theme: theme, className: "carbon-chart-wrapper", groupsLength: groupsLength < 6 ? groupsLength : 14 }, rest),
|
|
1048
1201
|
React.createElement(ChartType, { data: dataSet, options: newOptions })));
|
|
1049
1202
|
});
|
|
1050
1203
|
var templateObject_1;
|