static/app/components/core/disclosure/disclosure.mdx
import {useState} from 'react';
import {IconEdit} from 'sentry/icons';
import {Button} from '@sentry/scraps/button'; import {Disclosure} from '@sentry/scraps/disclosure'; import {Flex, Stack} from '@sentry/scraps/layout';
import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
import * as Storybook from 'sentry/stories';
export const documentation = import('!!type-loader!@sentry/scraps/disclosure/disclosure');
To create a disclosure, wrap content in a <Disclosure> component.
The disclosure component supports a controlled and uncontrolled state via the defaultExpanded and expanded props.
<Storybook.Demo> <Flex width="100%"> <Disclosure> <Disclosure.Title>This is a disclosure</Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> </Flex> </Storybook.Demo>
<Disclosure>
<Disclosure.Title>This is a disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
The disclosure can be uncontrolled by setting the defaultExpanded prop, or controlled by setting the expanded prop.
<Storybook.Demo> <Flex width="100%"> <Disclosure defaultExpanded> <Disclosure.Title>This is a default expanded disclosure</Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> </Flex> </Storybook.Demo>
<Disclosure defaultExpanded>
<Disclosure.Title>This is a default expanded disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
The disclosure can be sized by setting the size prop.
<Storybook.Demo> <Flex width="100%"> <Stack direction="column" justify="center" gap="2xl"> <Disclosure size="md" defaultExpanded> <Disclosure.Title>This is medium disclosure</Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> <Disclosure size="sm" defaultExpanded> <Disclosure.Title>This is small disclosure</Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> <Disclosure size="xs" defaultExpanded> <Disclosure.Title>This is extra small disclosure</Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> </Stack> </Flex> </Storybook.Demo>
<Disclosure size="md" defaultExpanded>
<Disclosure.Title>This is medium disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
<Disclosure size="sm" defaultExpanded>
<Disclosure.Title>This is small disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
<Disclosure size="xs" defaultExpanded>
<Disclosure.Title>This is extra small disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
Add interactive elements like buttons, links or badges to the right side of disclosures using the trailingItems prop.
Note: The trailing items should be sized to match the disclosure size - this is currently not done automatically.
<Storybook.Demo> <Flex width="100%"> <Disclosure size="sm" defaultExpanded> <Disclosure.Title trailingItems={ <Button size="xs" icon={<IconEdit />}> Trailing Item </Button> } > Title With Trailing Items </Disclosure.Title> <Disclosure.Content>This is the content of the disclosure</Disclosure.Content> </Disclosure> </Flex> </Storybook.Demo>
<Disclosure size="sm" defaultExpanded>
<Disclosure.Title
trailingItems={
<Button size="xs" icon={<IconEdit />}>
Trailing Item
</Button>
}
>
Title With Trailing Items
</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>
The disclosure component provides a onExpandedChange prop that can be used to listen for changes to the expanded state - this allows you to store the state in a storage of your choice.
Consider storing it inside URL State or localStorage for persistent storage.
export function LocalStorageDisclosure() { const [expanded, setExpanded] = useLocalStorageState('my-disclosure-key', false);
return (
<Disclosure expanded={expanded} onExpandedChange={setExpanded}>
<Disclosure.Title>The expanded state is saved to localStorage</Disclosure.Title>
<Disclosure.Content>Reload the page to see the expanded state is remembered</Disclosure.Content>
</Disclosure>
);
}
Disclosures can maintain their expanded state across page reloads by using localStorage or URL state.
<Storybook.Demo> <Flex width="100%"> <LocalStorageDisclosure /> </Flex> </Storybook.Demo>
function LocalStorageDisclosure() {
const [expanded, setExpanded] = useLocalStorageState('my-disclosure-key', false);
return (
<Disclosure expanded={expanded} onExpandedChange={setExpanded}>
<Disclosure.Title>The expanded state is saved to localStorage</Disclosure.Title>
<Disclosure.Content>
Reload the page to see the expanded state is remembered
</Disclosure.Content>
</Disclosure>
);
}
Disclosures can maintain their expanded state across page reloads by using URL state.
import {parseAsBoolean, useQueryState} from 'nuqs';
export function URLStateDisclosure() { const [expanded, setExpanded] = useQueryState('my-disclosure-key', parseAsBoolean.withDefault(false));
return (
<Disclosure defaultExpanded={expanded} onExpandedChange={setExpanded}>
<Disclosure.Title>The expanded state is saved to URL state</Disclosure.Title>
<Disclosure.Content>Reload the page to see the expanded state is remembered and new params are added to the URL</Disclosure.Content>
</Disclosure>
);
}
<Storybook.Demo> <Flex width="100%"> <URLStateDisclosure /> </Flex> </Storybook.Demo>
import {parseAsBoolean, useQueryState} from 'nuqs';
function URLStateDisclosure() {
const [expanded, setExpanded] = useQueryState(
'my-disclosure-key',
parseAsBoolean.withDefault(false)
);
return (
<Disclosure defaultExpanded={expanded} onExpandedChange={setExpanded}>
<Disclosure.Title>The expanded state is saved to URL state</Disclosure.Title>
<Disclosure.Content>
Reload the page to see the expanded state is remembered
</Disclosure.Content>
</Disclosure>
);
}
Disclosures can be scrolled to with the help of a render ref.
<Disclosure
ref={ref => {
if (ref) ref.scrollIntoView();
}}
>
<Disclosure.Title>This is a disclosure</Disclosure.Title>
<Disclosure.Content>This is the content of the disclosure</Disclosure.Content>
</Disclosure>