From 544769924f8752109d6c5cc2261834f4b150a5c3 Mon Sep 17 00:00:00 2001 From: Jared Lunde Date: Mon, 20 Jul 2020 09:41:40 -0600 Subject: [PATCH] refactor: add new hooks and update disclosure rename useTarget() to useA11yTarget(), useTrigger() to useA11yTrigger(), useCloseButton() to useA11yCloseButton() BREAKING CHANGE: Renames useTarget() to useA11yTarget(), useTrigger() to useA11yTrigger(), useCloseButton() to useA11yCloseButton() --- src/__snapshots__/index.test.tsx.snap | 281 --------- src/index.test.tsx | 804 +++++++++++++++++++++++++- src/index.tsx | 152 +++-- types/index.d.ts | 119 +++- 4 files changed, 963 insertions(+), 393 deletions(-) delete mode 100644 src/__snapshots__/index.test.tsx.snap mode change 100644 => 100755 src/index.test.tsx diff --git a/src/__snapshots__/index.test.tsx.snap b/src/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 0873297..0000000 --- a/src/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,281 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should open and close on Trigger click: closed 1`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed 2`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed 3`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed 4`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed initially 1`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed initially 2`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed initially 3`] = ` - - - - -`; - -exports[` should open and close on Trigger click: closed initially 4`] = ` - - - - -`; - -exports[` should open and close on Trigger click: open 1`] = ` - -
- Hello world -
- -
-`; - -exports[` should open and close on Trigger click: open 2`] = ` - -
- Hello world -
- -
-`; - -exports[` should open and close on Trigger click: open 3`] = ` - -
- Hello world -
- -
-`; - -exports[` should open and close on Trigger click: open 4`] = ` - -
- Hello world -
- -
-`; - -exports[` should open on the left by default: closed initially 1`] = ` - - - - -`; - -exports[` should open on the left by default: open 1`] = ` - -
- Hello world -
- -
-`; diff --git a/src/index.test.tsx b/src/index.test.tsx old mode 100644 new mode 100755 index c2c89fa..07f5852 --- a/src/index.test.tsx +++ b/src/index.test.tsx @@ -1,49 +1,795 @@ /* jest */ import * as React from 'react' -import {render, cleanup} from '@testing-library/react' +import {renderHook} from '@testing-library/react-hooks' +import {render, fireEvent, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' -import {Drawer, Target, Trigger} from './index' +import { + Drawer, + Trigger, + Target, + CloseButton, + useDrawer, + useA11yTarget, +} from './index' + +describe('', () => { + it('should have a custom id', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute('id', 'foobar') + }) + + it('should invoke onChange callback when open state changes', () => { + const handleChange = jest.fn() + + render( + + + + + + ) + + expect(handleChange).not.toBeCalled() + userEvent.click(screen.getByRole('button')) + expect(handleChange).toBeCalledWith(true, false) + userEvent.click(screen.getByRole('button')) + expect(handleChange).toBeCalledWith(false, true) + }) +}) describe('', () => { it('should open and close on Trigger click', () => { - for (const placement of ['top', 'right', 'bottom', 'left']) { - const result = render( - - -
Hello world
-
- - - - -
- ) - - expect(result.asFragment()).toMatchSnapshot('closed initially') - userEvent.click(result.getByRole('button')) - expect(result.asFragment()).toMatchSnapshot('open') - userEvent.click(result.getByRole('button')) - expect(result.asFragment()).toMatchSnapshot('closed') - cleanup() - document.getElementsByTagName('html')[0].innerHTML = '' - } - }) - - it('should open on the left by default', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'true' + ) + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + }) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'visible', + }) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'true' + ) + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + }) + }) + + it('should default to "left"', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 0, + right: 'auto', + transform: 'translate3d(-100%, 0, 0)', + }) + }) + + it('should return correct props for "left"', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 0, + right: 'auto', + transform: 'translate3d(-100%, 0, 0)', + }) + }) + + it('should return correct props for "top"', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 'auto', + right: 0, + transform: 'translate3d(0, -100%, 0)', + }) + }) + + it('should return correct props for "right"', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + position: 'fixed', + left: 'auto', + top: 0, + bottom: 0, + right: 0, + transform: 'translate3d(100%, 0, 0)', + }) + }) + + it('should return correct props for "bottom"', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 'auto', + bottom: 0, + right: 0, + transform: 'translate3d(0, 100%, 0)', + }) + }) + + it('should close on escape key', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + + fireEvent.keyDown(screen.getByText('Hello world'), { + key: 'Escape', + which: 27, + }) + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'true' + ) + }) + + it(`shouldn't close on escape key if prop is false`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + fireEvent.keyDown(screen.getByText('Hello world'), { + key: 'Escape', + code: 27, + }) + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + }) + + it(`should assign to custom styles when opened or closed`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({fontSize: '2rem'}) + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).toHaveStyle({fontSize: '2rem'}) + }) + + it(`should apply custom classname when opened or closed`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveClass('custom') + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).toHaveClass('custom') + }) + + it(`should apply user defined openClass and closedClass`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveClass('closed') + expect(screen.getByText('Hello world')).not.toHaveClass('open') + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).not.toHaveClass('closed') + expect(screen.getByText('Hello world')).toHaveClass('open') + }) + + it(`should apply user defined openStyle and closedStyle`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveStyle({display: 'none'}) + expect(screen.getByText('Hello world')).not.toHaveStyle({display: 'block'}) + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).not.toHaveStyle({display: 'none'}) + expect(screen.getByText('Hello world')).toHaveStyle({display: 'block'}) + }) + + it(`should be initially open when defined as such`, () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + }) + + it(`should act like a controlled component when 'open' prop is specified`, () => { const result = render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'false' + ) + + result.rerender( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByText('Hello world')).toHaveAttribute( + 'aria-hidden', + 'true' + ) + }) + + it('should render into a portal by default ID', () => { + const portalRoot = document.createElement('div') + portalRoot.setAttribute('id', 'portals') + document.body.appendChild(portalRoot) + + render( + + +
Hello world
+
+ + + + +
+ ) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world').parentNode).toHaveAttribute( + 'id', + 'portals' + ) + document.body.removeChild(portalRoot) + }) + + it('should render into a portal by custom selector object', () => { + const portalRoot = document.createElement('div') + portalRoot.setAttribute('class', 'portals') + document.body.appendChild(portalRoot) + + render( + + +
Hello world
+
+ + + + +
+ ) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world').parentNode).toHaveClass('portals') + document.body.removeChild(portalRoot) + }) + + it('should render into a portal by custom selector', () => { + const portalRoot = document.createElement('div') + portalRoot.setAttribute('class', 'portals') + document.body.appendChild(portalRoot) + + render( + + +
Hello world
+
+ + + + +
+ ) + + userEvent.click(screen.getByRole('button')) + expect(screen.getByText('Hello world').parentNode).toHaveClass('portals') + document.body.removeChild(portalRoot) + }) +}) + +describe('', () => { + it('should have correct aria-controls prop', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByRole('button')).toHaveAttribute('aria-controls', 'test') + }) + + it('should have correct aria-expanded prop', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'false') + userEvent.click(screen.getByRole('button')) + expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true') + }) + + it('should have openClass and closedClass', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByRole('button')).toHaveClass('closed') + expect(screen.getByRole('button')).not.toHaveClass('open') + userEvent.click(screen.getByRole('button')) + expect(screen.getByRole('button')).not.toHaveClass('closed') + expect(screen.getByRole('button')).toHaveClass('open') + }) + + it('should have openStyle and closedStyle', () => { + render( + + +
Hello world
+
+ + + + +
+ ) + + expect(screen.getByRole('button')).toHaveStyle({display: 'flex'}) + expect(screen.getByRole('button')).not.toHaveStyle({display: 'block'}) + userEvent.click(screen.getByText('open me')) + expect(screen.getByRole('button')).not.toHaveStyle({display: 'flex'}) + expect(screen.getByRole('button')).toHaveStyle({display: 'block'}) + }) + + it('should fire user-defined onClick handler', () => { + const cb = jest.fn() + render(
Hello world
+ + + +
+ ) + + userEvent.click(screen.getByRole('button')) + expect(cb).toBeCalledTimes(1) + }) +}) + +describe('', () => { + it('should have correct aria-controls prop', () => { + render( + + +
+ + + + Hello world +
+
+ + + + +
+ ) + + expect(screen.getByText('Close me')).toHaveAttribute( + 'aria-controls', + 'test' + ) + }) + + it('should have correct aria-expanded prop', () => { + render( + + +
+ + + + Hello world +
+
+ + + + +
+ ) + + expect(screen.getByText('open me')).toHaveAttribute('aria-expanded', 'true') + userEvent.click(screen.getByTestId('close')) + expect(screen.getByText('open me')).toHaveAttribute( + 'aria-expanded', + 'false' + ) + }) + + it('should override the aria label', () => { + render( + + +
+ + + + Hello world +
+
+
+ ) + + expect(screen.getByText('Close me')).toHaveAttribute( + 'aria-label', + 'close me' + ) + }) + + it('should close the target', () => { + render( + + +
+ + + + Hello world +
+
+
) - expect(result.asFragment()).toMatchSnapshot('closed initially') - userEvent.click(result.getByRole('button')) - expect(result.asFragment()).toMatchSnapshot('open') + expect(screen.getByTestId('target')).toHaveAttribute('aria-hidden', 'false') + userEvent.click(screen.getByTestId('close')) + expect(screen.getByTestId('target')).toHaveAttribute('aria-hidden', 'true') + }) + + it('should fire user-defined onClick handler', () => { + const cb = jest.fn() + render( + + +
+ + + + Hello world +
+
+ + + + +
+ ) + + userEvent.click(screen.getByTestId('close')) + expect(cb).toBeCalledTimes(1) + }) +}) + +describe('useDrawer()', () => { + it('should return context', () => { + const {result} = renderHook(() => useDrawer(), {wrapper: Drawer}) + expect(typeof result.current.close).toBe('function') + expect(typeof result.current.open).toBe('function') + expect(typeof result.current.toggle).toBe('function') + expect(typeof result.current.isOpen).toBe('boolean') + expect(typeof result.current.id).toBe('string') + expect(Object.keys(result.current).sort()).toEqual( + ['close', 'open', 'toggle', 'isOpen', 'id'].sort() + ) + }) +}) + +describe('useA11yTarget()', () => { + it('should default to "left"', () => { + const ref = {current: null} + const {result} = renderHook(() => useA11yTarget(ref), { + wrapper: Drawer, + }) + + expect(result.current.style).toStrictEqual({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 0, + right: 'auto', + transform: 'translate3d(-100%, 0, 0)', + }) + }) + + it('should return correct props for "left"', () => { + const ref = {current: null} + const {result} = renderHook(() => useA11yTarget(ref, {placement: 'left'}), { + wrapper: Drawer, + }) + + expect(result.current.style).toStrictEqual({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 0, + right: 'auto', + transform: 'translate3d(-100%, 0, 0)', + }) + }) + + it('should return correct props for "top"', () => { + const ref = {current: null} + const {result} = renderHook(() => useA11yTarget(ref, {placement: 'top'}), { + wrapper: Drawer, + }) + + expect(result.current.style).toStrictEqual({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 0, + bottom: 'auto', + right: 0, + transform: 'translate3d(0, -100%, 0)', + }) + }) + + it('should return correct props for "right"', () => { + const ref = {current: null} + const {result} = renderHook( + () => useA11yTarget(ref, {placement: 'right'}), + { + wrapper: Drawer, + } + ) + + expect(result.current.style).toStrictEqual({ + visibility: 'hidden', + position: 'fixed', + left: 'auto', + top: 0, + bottom: 0, + right: 0, + transform: 'translate3d(100%, 0, 0)', + }) + }) + + it('should return correct props for "bottom"', () => { + const ref = {current: null} + const {result} = renderHook( + () => useA11yTarget(ref, {placement: 'bottom'}), + { + wrapper: Drawer, + } + ) + + expect(result.current.style).toStrictEqual({ + visibility: 'hidden', + position: 'fixed', + left: 0, + top: 'auto', + bottom: 0, + right: 0, + transform: 'translate3d(0, 100%, 0)', + }) }) }) diff --git a/src/index.tsx b/src/index.tsx index 24611df..395c2e0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,30 +1,81 @@ import * as React from 'react' import { + Disclosure, Target as DisclosureTarget, - TargetProps as DisclosureTargetProps, - DisclosureControls, - CloseProps as DisclosureCloseProps, - TriggerProps as DisclosureTriggerProps, + useA11yTarget as useA11yDisclosureTarget, + useDisclosure, +} from '@accessible/disclosure' +import type { DisclosureProps, DisclosureContextValue, + TargetProps as DisclosureTargetProps, + UseA11yTargetOptions as UseA11yDisclosureTargetOptions, } from '@accessible/disclosure' -export { - Disclosure as Drawer, - DisclosureContext as DrawerContext, - DisclosureConsumer as DrawerConsumer, - useDisclosure as useDrawer, - Close, - Trigger, - useIsOpen, - useControls, -} from '@accessible/disclosure' -const __DEV__ = - typeof process !== 'undefined' && process.env.NODE_ENV !== 'production' +/** + * This hook provides the current value of the drawer's context object + */ +export function useDrawer(): DrawerContextValue { + return useDisclosure() +} + +/** + * This component creates the context for your drawer target and trigger + * and contains some configuration options. + */ +export function Drawer(props: DrawerProps) { + return +} + +/** + * A React hook for creating a headless drawer target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-drawer/dialog.html). + * + * @param target A React ref or HTML element + * @param options Configuration options + */ +export function useA11yTarget( + target: React.RefObject | T | null, + options: UseA11yTargetOptions = {} +) { + const disclosureProps = useA11yDisclosureTarget( + target, + Object.assign({}, options, { + openStyle: Object.assign(defaultOpenStyles, options.openStyle), + }) + ) + + return Object.assign(disclosureProps, { + style: Object.assign( + disclosureProps.style, + defaultClosedStyles[options.placement || 'left'] + ), + } as const) +} + +/** + * This component wraps any React element and turns it into a + * drawer target. + */ +export function Target(props: TargetProps) { + const childProps = props.children.props + return React.createElement( + DisclosureTarget, + Object.assign({}, props, { + openStyle: Object.assign({}, defaultOpenStyles, props.openStyle), + }), + React.cloneElement(props.children, { + style: Object.assign( + {}, + defaultClosedStyles[props.placement || 'left'], + childProps.style + ), + }) + ) +} const defaultClosedStyles = { top: { - margin: 0, + position: 'fixed', top: 0, right: 0, bottom: 'auto', @@ -32,7 +83,7 @@ const defaultClosedStyles = { transform: 'translate3d(0, -100%, 0)', }, right: { - margin: 0, + position: 'fixed', top: 0, right: 0, bottom: 0, @@ -40,7 +91,7 @@ const defaultClosedStyles = { transform: 'translate3d(100%, 0, 0)', }, bottom: { - margin: 0, + position: 'fixed', top: 'auto', right: 0, bottom: 0, @@ -48,59 +99,48 @@ const defaultClosedStyles = { transform: 'translate3d(0, 100%, 0)', }, left: { - margin: 0, + position: 'fixed', top: 0, right: 'auto', bottom: 0, left: 0, transform: 'translate3d(-100%, 0, 0)', }, -} +} as const const defaultOpenStyles = { transform: 'translate3d(0, 0, 0)', -} +} as const -const defaultStyles = { - position: 'fixed', - margin: 'auto', - left: '50%', - top: '50%', - transform: 'translate3d(-50%, -50%, 0)', -} +export { + Trigger, + CloseButton, + useA11yTrigger, + useA11yCloseButton, +} from '@accessible/disclosure' -export const Target: React.FC = ({ - placement = 'left', - openStyle, - ...props -}) => { - const childProps = props.children.props - return React.createElement( - DisclosureTarget, - Object.assign(props, { - openStyle: Object.assign({}, defaultOpenStyles, openStyle), - }), - React.cloneElement(props.children, { - style: Object.assign( - {}, - defaultStyles, - defaultClosedStyles[placement], - childProps.style - ), - }) - ) -} +export type { + TriggerProps, + CloseButtonProps, + UseA11yTriggerOptions, + UseA11yCloseButtonOptions, +} from '@accessible/disclosure' -export interface DrawerContextValue extends DisclosureContextValue {} export interface DrawerProps extends DisclosureProps {} -export interface DrawerControls extends DisclosureControls {} -export interface TriggerProps extends DisclosureTriggerProps {} -export interface CloseProps extends DisclosureCloseProps {} -export interface TargetProps extends DisclosureTargetProps { +export interface DrawerContextValue extends DisclosureContextValue {} +export interface UseA11yTargetOptions extends UseA11yDisclosureTargetOptions { + /** + * Sets the placement of the drawer menu + * @default "left" + */ placement?: 'top' | 'right' | 'bottom' | 'left' } +export interface TargetProps + extends DisclosureTargetProps, + UseA11yTargetOptions {} /* istanbul ignore next */ -if (__DEV__) { +if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') { + Drawer.displayName = 'Drawer' Target.displayName = 'Target' } diff --git a/types/index.d.ts b/types/index.d.ts index 55edd9b..291b511 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,28 +1,93 @@ -import * as React from 'react' -import { - TargetProps as DisclosureTargetProps, - DisclosureControls, - CloseProps as DisclosureCloseProps, - TriggerProps as DisclosureTriggerProps, - DisclosureProps, - DisclosureContextValue, -} from '@accessible/disclosure' -export { - Disclosure as Drawer, - DisclosureContext as DrawerContext, - DisclosureConsumer as DrawerConsumer, - useDisclosure as useDrawer, - Close, - Trigger, - useIsOpen, - useControls, -} from '@accessible/disclosure' -export declare const Target: React.FC -export interface DrawerContextValue extends DisclosureContextValue {} -export interface DrawerProps extends DisclosureProps {} -export interface DrawerControls extends DisclosureControls {} -export interface TriggerProps extends DisclosureTriggerProps {} -export interface CloseProps extends DisclosureCloseProps {} -export interface TargetProps extends DisclosureTargetProps { - placement?: 'top' | 'right' | 'bottom' | 'left' +import * as React from 'react'; +import type { DisclosureProps, DisclosureContextValue, TargetProps as DisclosureTargetProps, UseA11yTargetOptions as UseA11yDisclosureTargetOptions } from '@accessible/disclosure'; +/** + * This hook provides the current value of the drawer's context object + */ +export declare function useDrawer(): DrawerContextValue; +/** + * This component creates the context for your drawer target and trigger + * and contains some configuration options. + */ +export declare function Drawer(props: DrawerProps): JSX.Element; +export declare namespace Drawer { + var displayName: string; +} +/** + * A React hook for creating a headless drawer target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-drawer/dialog.html). + * + * @param target A React ref or HTML element + * @param options Configuration options + */ +export declare function useA11yTarget(target: React.RefObject | T | null, options?: UseA11yTargetOptions): { + readonly 'aria-hidden': boolean; + readonly id: string | undefined; + readonly className: string | undefined; + readonly style: { + readonly visibility: "hidden" | "visible"; + } & React.CSSProperties; +} & { + readonly style: ({ + readonly visibility: "hidden" | "visible"; + } & React.CSSProperties & { + readonly position: "fixed"; + readonly top: 0; + readonly right: 0; + readonly bottom: "auto"; + readonly left: 0; + readonly transform: "translate3d(0, -100%, 0)"; + }) | ({ + readonly visibility: "hidden" | "visible"; + } & React.CSSProperties & { + readonly position: "fixed"; + readonly top: 0; + readonly right: 0; + readonly bottom: 0; + readonly left: "auto"; + readonly transform: "translate3d(100%, 0, 0)"; + }) | ({ + readonly visibility: "hidden" | "visible"; + } & React.CSSProperties & { + readonly position: "fixed"; + readonly top: "auto"; + readonly right: 0; + readonly bottom: 0; + readonly left: 0; + readonly transform: "translate3d(0, 100%, 0)"; + }) | ({ + readonly visibility: "hidden" | "visible"; + } & React.CSSProperties & { + readonly position: "fixed"; + readonly top: 0; + readonly right: "auto"; + readonly bottom: 0; + readonly left: 0; + readonly transform: "translate3d(-100%, 0, 0)"; + }); +}; +/** + * This component wraps any React element and turns it into a + * drawer target. + */ +export declare function Target(props: TargetProps): React.FunctionComponentElement; +export declare namespace Target { + var displayName: string; +} +export { Trigger, CloseButton, useA11yTrigger, useA11yCloseButton, } from '@accessible/disclosure'; +export type { TriggerProps, CloseButtonProps, UseA11yTriggerOptions, UseA11yCloseButtonOptions, } from '@accessible/disclosure'; +export interface DrawerProps extends DisclosureProps { +} +export interface DrawerContextValue extends DisclosureContextValue { +} +export interface UseA11yTargetOptions extends UseA11yDisclosureTargetOptions { + /** + * Sets the placement of the drawer menu + * @default "left" + */ + placement?: 'top' | 'right' | 'bottom' | 'left'; +} +export interface TargetProps extends DisclosureTargetProps, UseA11yTargetOptions { }