Skip to content

Commit

Permalink
feat(disclosure): add state prop
Browse files Browse the repository at this point in the history
  • Loading branch information
richbachman committed Jul 28, 2020
1 parent a7cfe42 commit 9ce28b4
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
@@ -1,7 +1,15 @@
import * as React from 'react';
import {axe} from 'jest-axe';
import {render, screen, fireEvent} from '@testing-library/react';
import {Disclosure, DisclosureContent, DisclosureHeading, DisclosureHeadingProps, DisclosureProps} from '../src';
import {
Disclosure,
DisclosureContent,
DisclosureHeading,
DisclosureHeadingProps,
DisclosureProps,
DisclosureStateReturn,
useDisclosureState,
} from '../src';

export const MockDisclosure: React.FC<{
visible?: DisclosureProps['visible'];
Expand All @@ -18,6 +26,32 @@ export const MockDisclosure: React.FC<{
);
};

const useVisibleDisclosureState = (): DisclosureStateReturn => {
const disclosure = useDisclosureState();
const [visible, setVisible] = React.useState(true);
return {
...disclosure,
visible,
toggle: () => {
setVisible(false);
},
};
};

const StateHookMock: React.FC = () => {
const disclosure = useVisibleDisclosureState();
return (
<>
<Disclosure variant="contained" state={disclosure}>
<DisclosureHeading as="h2" variant="heading20">
Clickable heading
</DisclosureHeading>
<DisclosureContent>Disclosure content</DisclosureContent>
</Disclosure>
</>
);
};

describe('Disclosure', () => {
it('should render a disclosure button with aria attributes', () => {
render(<MockDisclosure />);
Expand Down Expand Up @@ -50,6 +84,13 @@ describe('Disclosure', () => {
expect(renderedDisclosureButton.getAttribute('aria-disabled')).toEqual('true');
expect(renderedDisclosureButton.getAttribute('tabindex')).toEqual('0');
});
it('should render a disclosure open and update attributes when clicked using a state hook', () => {
render(<StateHookMock />);
const renderedDisclosureButton = screen.getByRole('button');
expect(renderedDisclosureButton.getAttribute('aria-expanded')).toEqual('true');
fireEvent.click(renderedDisclosureButton);
expect(renderedDisclosureButton.getAttribute('aria-expanded')).toEqual('false');
});
describe('accessibility', () => {
it('should have no accessibility violations', async () => {
const {container} = render(<MockDisclosure />);
Expand Down
13 changes: 11 additions & 2 deletions packages/paste-core/components/disclosure/src/Disclosure.tsx
Expand Up @@ -16,12 +16,18 @@ export interface DisclosureContextProps {

export const DisclosureContext = React.createContext<DisclosureContextProps>({} as any);

export interface DisclosureStateReturn extends DisclosurePrimitveStateReturn {
[key: string]: any;
}

export interface DisclosureProps extends DisclosurePrimitiveInitialState {
children: NonNullable<React.ReactNode>;
state?: DisclosureStateReturn;
variant?: Variants;
}
const Disclosure: React.FC<DisclosureProps> = ({children, variant = 'default', ...props}) => {
const disclosure = useDisclosurePrimitiveState({...props});

const Disclosure: React.FC<DisclosureProps> = ({children, variant = 'default', state, ...props}) => {
const disclosure = state || useDisclosurePrimitiveState({...props});
const disclosureContext = {
disclosure,
variant,
Expand All @@ -34,6 +40,7 @@ const Disclosure: React.FC<DisclosureProps> = ({children, variant = 'default', .
</DisclosureContext.Provider>
);
}

return <DisclosureContext.Provider value={disclosureContext}>{children}</DisclosureContext.Provider>;
};
Disclosure.displayName = 'Disclosure';
Expand All @@ -45,3 +52,5 @@ if (process.env.NODE_ENV === 'development') {
};
}
export {Disclosure};

export {useDisclosurePrimitiveState as useDisclosureState};
Expand Up @@ -3,7 +3,16 @@ import {storiesOf} from '@storybook/react';
import {withKnobs, text, select} from '@storybook/addon-knobs';
import {HeadingProps} from '@twilio-paste/heading';
import {Stack} from '@twilio-paste/stack';
import {Disclosure, DisclosureHeading, DisclosureContent, Variants, DisclosureHeadingProps} from '../src';
import {Paragraph} from '@twilio-paste/paragraph';
import {
Disclosure,
DisclosureHeading,
DisclosureContent,
Variants,
DisclosureHeadingProps,
useDisclosureState,
DisclosureStateReturn,
} from '../src';

const headingVariantOptions = ['heading10', 'heading20', 'heading30', 'heading40', 'heading50', 'heading60'];

Expand All @@ -30,6 +39,40 @@ export const ExampleDisclosures: React.FC<{
);
};

const useDelayedDisclosureState = ({delay, ...initialState}): DisclosureStateReturn => {
const disclosure = useDisclosureState(initialState);
const [transitioning, setTransitioning] = React.useState(false);
return {
...disclosure,
transitioning,
toggle: () => {
setTransitioning(true);
setTimeout(() => {
disclosure.toggle();
setTransitioning(false);
}, delay);
},
};
};

const StateHookExample: React.FC = () => {
const {transitioning, ...disclosure} = useDelayedDisclosureState({
delay: 500,
});
const clickableHeading = disclosure.visible ? 'Hide with delay' : 'Show with delay';
return (
<>
<Paragraph>This Disclosure should be visible on load, and take 1 second to open and close.</Paragraph>
<Disclosure variant="contained" state={disclosure}>
<DisclosureHeading as="h2" variant="heading20">
{transitioning ? 'Please wait...' : clickableHeading}
</DisclosureHeading>
<DisclosureContent>Disclosure content</DisclosureContent>
</Disclosure>
</>
);
};

storiesOf('Components|Disclosure', module)
.addDecorator(withKnobs)
.add('All variants', () => {
Expand Down Expand Up @@ -90,4 +133,7 @@ storiesOf('Components|Disclosure', module)
})
.add('Contained disabled', () => {
return <ExampleDisclosures disabled headingVariant="heading10" variant="contained" />;
})
.add('State hook', () => {
return <StateHookExample />;
});

0 comments on commit 9ce28b4

Please sign in to comment.