mirror of
https://github.com/suitenumerique/docs.git
synced 2026-04-25 17:15:01 +02:00
fixup! ♿️(frontend) fix sidebar resize handle for screen readers
This commit is contained in:
@@ -9,7 +9,7 @@ and this project adheres to
|
||||
### Changed
|
||||
|
||||
- 💄(frontend) improve comments highlights #1961
|
||||
♿️(frontend) fix sidebar resize handle for screen readers #2122
|
||||
- ♿️(frontend) fix sidebar resize handle for screen readers #2122
|
||||
|
||||
## [v4.8.3] - 2026-03-23
|
||||
|
||||
|
||||
@@ -16,6 +16,23 @@ const pxToPercent = (px: number) => {
|
||||
return (px / window.innerWidth) * 100;
|
||||
};
|
||||
|
||||
const RESIZE_HANDLE_ID = 'left-panel-resize-handle';
|
||||
|
||||
const getSidebarWidthLabel = (
|
||||
current: number,
|
||||
min: number,
|
||||
max: number,
|
||||
): 'narrow' | 'medium' | 'wide' => {
|
||||
const ratio = (current - min) / (max - min);
|
||||
if (ratio < 1 / 3) {
|
||||
return 'narrow';
|
||||
}
|
||||
if (ratio < 2 / 3) {
|
||||
return 'medium';
|
||||
}
|
||||
return 'wide';
|
||||
};
|
||||
|
||||
type ResizableLeftPanelProps = {
|
||||
leftPanel: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
@@ -23,8 +40,6 @@ type ResizableLeftPanelProps = {
|
||||
maxPanelSizePx?: number;
|
||||
};
|
||||
|
||||
const RESIZE_HANDLE_ID = 'left-panel-resize-handle';
|
||||
|
||||
export const ResizableLeftPanel = ({
|
||||
leftPanel,
|
||||
children,
|
||||
@@ -102,47 +117,21 @@ export const ResizableLeftPanel = ({
|
||||
|
||||
/**
|
||||
* Workaround: NVDA does not enter focus mode for role="separator"
|
||||
* (https://github.com/nvaccess/nvda/issues/11403), so arrow keys are
|
||||
* intercepted by browse-mode navigation and never reach the handle.
|
||||
* Changing the role to "slider" makes NVDA reliably switch to focus
|
||||
* mode, restoring progressive keyboard resize with arrow keys.
|
||||
*
|
||||
* Note: PanelResizeHandle does not expose a ref (no RefAttributes in its
|
||||
* type definition), so we use id + getElementById as the only viable option.
|
||||
* Only role needs to be overridden here; aria-* props are passed directly.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!isPanelOpen) {
|
||||
return;
|
||||
}
|
||||
const handle = document.getElementById(RESIZE_HANDLE_ID);
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
handle.setAttribute('role', 'slider');
|
||||
handle.setAttribute('aria-orientation', 'vertical');
|
||||
handle.setAttribute('aria-label', t('Resize sidebar'));
|
||||
|
||||
const updateValueText = () => {
|
||||
const value = handle.getAttribute('aria-valuenow');
|
||||
if (value) {
|
||||
const widthPx = Math.round(
|
||||
(parseFloat(value) / 100) * window.innerWidth,
|
||||
);
|
||||
handle.setAttribute(
|
||||
'aria-valuetext',
|
||||
t('Sidebar width: {{widthPx}} pixels', { widthPx }),
|
||||
);
|
||||
}
|
||||
};
|
||||
updateValueText();
|
||||
|
||||
const observer = new MutationObserver(updateValueText);
|
||||
observer.observe(handle, {
|
||||
attributes: true,
|
||||
attributeFilter: ['aria-valuenow'],
|
||||
});
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [isPanelOpen, t]);
|
||||
document.getElementById(RESIZE_HANDLE_ID)?.setAttribute('role', 'slider');
|
||||
}, [isPanelOpen]);
|
||||
|
||||
const handleResize = (sizePercent: number) => {
|
||||
const widthPx = (sizePercent / 100) * window.innerWidth;
|
||||
@@ -181,6 +170,20 @@ export const ResizableLeftPanel = ({
|
||||
{isPanelOpen && (
|
||||
<PanelResizeHandle
|
||||
id={RESIZE_HANDLE_ID}
|
||||
aria-label={t('Resize sidebar')}
|
||||
aria-orientation="vertical"
|
||||
aria-valuemin={Math.round(minPanelSizePercent)}
|
||||
aria-valuemax={Math.round(maxPanelSizePercent)}
|
||||
aria-valuenow={Math.round(panelSizePercent)}
|
||||
aria-valuetext={t(`Sidebar width: {{label}}`, {
|
||||
label: t(
|
||||
getSidebarWidthLabel(
|
||||
panelSizePercent,
|
||||
minPanelSizePercent,
|
||||
maxPanelSizePercent,
|
||||
),
|
||||
),
|
||||
})}
|
||||
style={{
|
||||
borderRightWidth: '1px',
|
||||
borderRightStyle: 'solid',
|
||||
|
||||
Reference in New Issue
Block a user