Skip to content

Commit b0eb263

Browse files
authored
feat(react-keytips): add useKeytipsManager hook (microsoft#406)
1 parent eff6a0b commit b0eb263

19 files changed

+432
-381
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "BREAKING: add useKeytipsManager hook. useEventServices is no longer exported",
4+
"packageName": "@fluentui-contrib/react-keytips",
5+
"email": "vgenaev@gmail.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-keytips/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,29 @@ export const App = () => {
6464
};
6565
```
6666

67+
## Manual dispatching of keytip events
68+
69+
In some cases, you may want to manually trigger keytip events. For example, you might need to programmatically enter or exit keytip mode based on action.
70+
The [useKeytipsManager](https://github.com/microsoft/fluentui-contrib/blob/main/packages/react-keytips/src/hooks/useKeytipsManager.ts) hook provides access to dispatch
71+
method, allowing you to send keytip events manually.
72+
73+
```tsx
74+
import * as React from 'react';
75+
import { Button } from '@fluentui/react-components';
76+
import { useKeytipsManager } from '@fluentui-contrib/react-keytips';
77+
78+
export const App = () => {
79+
const { enterKeytipMode } = useKeytipsManager();
80+
81+
return (
82+
<>
83+
<Button onClick={enterKeytipMode}>Show Keytips</Button>
84+
</>
85+
);
86+
};
87+
```
88+
89+
```tsx
90+
6791
Follow up on the [Storybook](https://microsoft.github.io/fluentui-contrib/react-keytips) for examples on how to use the components provided by this package.
92+
```

packages/react-keytips/src/components/Keytip/Keytip.types.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export type ReturnKeytipEventHandler<E = HTMLElement | null> = EventHandler<
3030
>;
3131

3232
export type KeytipProps = ComponentProps<KeytipSlots> & {
33+
/**
34+
* Unique identifier for the Keytip.
35+
*/
36+
uniqueId?: string;
3337
/**
3438
* Positioning props to be passed to Keytip.
3539
* @default { align: 'center', position: 'below' }
@@ -68,11 +72,6 @@ export type KeytipProps = ComponentProps<KeytipSlots> & {
6872
overflowSequence?: string[];
6973
};
7074

71-
/** @internal */
72-
export type KeytipWithId = KeytipProps & {
73-
uniqueId: string;
74-
};
75-
7675
export type KeytipState = ComponentState<KeytipSlots> &
7776
Required<Pick<KeytipProps, 'visible' | 'positioning' | 'content'>> & {
7877
/**
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { KeytipProps } from '../Keytip.types';
2+
3+
/** @internal */
4+
export type KeytipWithId = KeytipProps & {
5+
uniqueId: string;
6+
};

packages/react-keytips/src/components/Keytip/useKeytip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import type { KeytipProps, KeytipState } from './Keytip.types';
33
import { usePositioning } from '@fluentui/react-positioning';
44
import { useControllableState } from '@fluentui/react-utilities';
5-
import { KEYTIP_BORDER_RADIUS, KTP_ROOT_ID } from '../../constants';
5+
import { KEYTIP_BORDER_RADIUS } from '../../constants';
66
import {
77
useFluent,
88
slot,

packages/react-keytips/src/components/Keytips/Keytips.component-browser-spec.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
KeytipsDynamicExample,
88
KeytipsDisabledTargetExample,
99
KeytipsDisabledMenuTargetExample,
10+
KeytipsManualDispatchedEvents,
1011
} from './KeytipsExamples.component-browser-spec';
1112

1213
test.use({ viewport: { width: 500, height: 500 } });
@@ -24,6 +25,25 @@ test.describe('enter and exit from keytip mode interactions', () => {
2425
await expect(tooltip).toBeHidden();
2526
});
2627

28+
test('should enter and exit keytips mode by manually dispatching events', async ({
29+
mount,
30+
page,
31+
}) => {
32+
await mount(<KeytipsManualDispatchedEvents startSequence="alt+meta" />);
33+
34+
const tooltip = page.getByRole('tooltip');
35+
await expect(tooltip).toBeHidden();
36+
37+
const enterButton = page.getByRole('button', { name: 'Enter keytip mode' });
38+
const exitButton = page.getByRole('button', { name: 'Exit keytip mode' });
39+
await enterButton.click();
40+
41+
await expect(tooltip).toBeVisible();
42+
43+
await exitButton.click();
44+
await expect(tooltip).toBeHidden();
45+
});
46+
2747
test('should not show keytip for disabled target', async ({
2848
mount,
2949
page,

packages/react-keytips/src/components/Keytips/KeytipsExamples.component-browser-spec.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import { MoreHorizontal20Filled } from '@fluentui/react-icons';
3535
import { Keytips } from './Keytips';
3636
import type { KeytipsProps } from './Keytips.types';
37+
import { useKeytipsManager } from '../../hooks/useKeytipsManager';
3738

3839
const useStyles = makeStyles({
3940
container: {
@@ -480,3 +481,27 @@ export const KeytipsDynamicExample = (props: KeytipsProps) => {
480481
</>
481482
);
482483
};
484+
485+
export const KeytipsManualDispatchedEvents = (props: KeytipsProps) => {
486+
const classes = useStyles();
487+
const { enterKeytipMode, exitKeytipMode } = useKeytipsManager();
488+
489+
const normalButton = useKeytipRef({
490+
keySequences: ['1c'],
491+
content: '1C',
492+
onExecute,
493+
});
494+
495+
return (
496+
<>
497+
<Keytips {...props} />
498+
<Button onClick={enterKeytipMode}>Enter keytip mode</Button>
499+
<Button onClick={exitKeytipMode}>Exit keytip mode</Button>
500+
<div className={classes.column}>
501+
<div className={classes.row}>
502+
<Button ref={normalButton}>Button</Button>
503+
</div>
504+
</div>
505+
</>
506+
);
507+
};

0 commit comments

Comments
 (0)