fix: allow for external focus management (#1730)

* fix: allow for external focus management

* chore: add resetFocus param

* chore: hide new apis behind context option

* chore: fix test check

* fix: unit test
This commit is contained in:
Tobias Åström
2025-05-05 11:17:08 +02:00
committed by GitHub
parent f65c678c80
commit bdf73b6549
4 changed files with 46 additions and 32 deletions

View File

@@ -351,6 +351,7 @@ const Cell = forwardRef(
translator, translator,
language, language,
keyboardNavigation, keyboardNavigation,
externalFocusManagement,
disableCellPadding = false, disableCellPadding = false,
} = useContext(InstanceContext); } = useContext(InstanceContext);
const [internalEmitter] = useState(emitter || createEmitter); const [internalEmitter] = useState(emitter || createEmitter);
@@ -383,18 +384,21 @@ const Cell = forwardRef(
useEffect(() => { useEffect(() => {
eventmixin(focusHandler.current); eventmixin(focusHandler.current);
focusHandler.current.blurCallback = (resetFocus) => {
if (focusHandler.current.onBlurHandler) {
focusHandler.current.onBlurHandler(resetFocus);
return;
}
halo.root.toggleFocusOfCells();
if (resetFocus && contentNode) {
contentNode.focus();
}
};
focusHandler.current.refocusContent = () => {
state.sn.component && typeof state.sn.component.focus === 'function' && state.sn.component.focus();
};
}, []); }, []);
focusHandler.current.blurCallback = (resetFocus) => {
halo.root.toggleFocusOfCells();
if (resetFocus && contentNode) {
contentNode.focus();
}
};
focusHandler.current.refocusContent = () => {
state.sn.component && typeof state.sn.component.focus === 'function' && state.sn.component.focus();
};
const handleOnMouseEnter = () => { const handleOnMouseEnter = () => {
if (hoveringDebouncer.current.leave) { if (hoveringDebouncer.current.leave) {
clearTimeout(hoveringDebouncer.current.leave); clearTimeout(hoveringDebouncer.current.leave);
@@ -510,7 +514,7 @@ const Cell = forwardRef(
} }
}, },
setOnBlurHandler(cb) { setOnBlurHandler(cb) {
focusHandler.current.blurCallback = cb; focusHandler.current.onBlurHandler = cb;
}, },
setSnOptions, setSnOptions,
setSnPlugins, setSnPlugins,
@@ -650,8 +654,8 @@ const Cell = forwardRef(
</Header> </Header>
)} )}
<Grid <Grid
tabIndex={keyboardNavigation ? 0 : -1} tabIndex={keyboardNavigation && !externalFocusManagement ? 0 : -1}
onKeyDown={keyboardNavigation ? handleKeyDown : null} onKeyDown={keyboardNavigation && !externalFocusManagement ? handleKeyDown : null}
item item
xs xs
className={CellBody.className} className={CellBody.className}

View File

@@ -87,6 +87,31 @@ export default function viz({
} }
}; };
let newExperimental = {};
if (halo.context?.enablePrivateExperimental) {
// ===== undocumented experimental API - use at own risk ======
newExperimental = {
/**
* valid types: viewData, cssScaling, snapshot, exportData, exploration
* questionable types: supportRefresh, quickMobile, fullscreen
* deprecated?: sharing
*
*/
support(type) {
if (mountedReference && successfulRender) {
return cellRef.current.support(type, originalExtensionDef?.support, originalLayout);
}
return false;
},
toggleFocus(focus) {
cellRef.current.toggleFocus(focus);
},
setOnBlurHandler(cb) {
cellRef.current.setOnBlurHandler(cb);
},
};
}
/** /**
* @class * @class
* @alias Viz * @alias Viz
@@ -253,25 +278,7 @@ export default function viz({
await rendered; await rendered;
return cellRef.current.takeSnapshot(); return cellRef.current.takeSnapshot();
}, },
// ===== undocumented experimental API - use at own risk ====== ...newExperimental,
/**
* valid types: viewData, cssScaling, snapshot, exportData, exploration
* questionable types: supportRefresh, quickMobile, fullscreen
* deprecated?: sharing
*
*/
support(type) {
if (mountedReference && successfulRender) {
return cellRef.current.support(type, originalExtensionDef?.support, originalLayout);
}
return false;
},
toggleFocus(focus) {
cellRef.current.toggleFocus(focus);
},
setOnBlurHandler(cb) {
cellRef.current.setOnBlurHandler(cb);
},
// ===== unexposed experimental API - use at own risk ====== // ===== unexposed experimental API - use at own risk ======
__DO_NOT_USE__: { __DO_NOT_USE__: {
mount(element) { mount(element) {

View File

@@ -121,6 +121,7 @@ describe('creator', () => {
opts = { opts = {
nebbie: 'embedAPI', nebbie: 'embedAPI',
keyboardNavigation: false, keyboardNavigation: false,
externalFocusManagement: false,
focusHandler: 'focusHandler', focusHandler: 'focusHandler',
emitter: 'emitter', emitter: 'emitter',
model: 'model', model: 'model',
@@ -145,6 +146,7 @@ describe('creator', () => {
selections: 'selections', selections: 'selections',
nebbie: 'embedAPI', nebbie: 'embedAPI',
keyboardNavigation: false, keyboardNavigation: false,
externalFocusManagement: false,
focusHandler: 'focusHandler', focusHandler: 'focusHandler',
emitter: 'emitter', emitter: 'emitter',
element: undefined, element: undefined,

View File

@@ -82,6 +82,7 @@ function createWithHooks(generator, opts, galaxy) {
layout: {}, layout: {},
appLayout: {}, appLayout: {},
keyboardNavigation: opts.keyboardNavigation, keyboardNavigation: opts.keyboardNavigation,
externalFocusManagement: opts.externalFocusManagement || false,
focusHandler: opts.focusHandler, focusHandler: opts.focusHandler,
constraints: forcedConstraints, constraints: forcedConstraints,
interactions: forcedInteractions, interactions: forcedInteractions,