diff --git a/static/app/components/core/slideOverPanel/index.tsx b/static/app/components/core/slideOverPanel/index.tsx new file mode 100644 index 00000000000000..f7f7b7ac1c0da8 --- /dev/null +++ b/static/app/components/core/slideOverPanel/index.tsx @@ -0,0 +1 @@ +export {SlideOverPanel} from './slideOverPanel'; diff --git a/static/app/components/core/slideOverPanel/slideOverPanel.mdx b/static/app/components/core/slideOverPanel/slideOverPanel.mdx new file mode 100644 index 00000000000000..1b718616b3cd5a --- /dev/null +++ b/static/app/components/core/slideOverPanel/slideOverPanel.mdx @@ -0,0 +1,38 @@ +--- +title: SlideOverPanel +description: A multi-purpose panel that can be used to reveal more information in a UI without directing to another page. +source: 'sentry/components/core/slideOverPanel' +resources: + js: https://github.com/getsentry/sentry/blob/master/static/app/components/slideOverPanel.tsx +--- + +import * as Storybook from 'sentry/stories'; + +import {SlideOverPanelPlayground} from 'sentry/stories/playground/slideOverPanel'; + +import SlideOverDocumentation from '!!type-loader!@sentry/scraps/slideOverPanel'; + +export const documentation = SlideOverDocumentation; + +The `SlideOverPanel` component is a panel that can appear on the right, left, or bottom of the page content to reveal more information. It's commonly used for UIs like the Widget Builder to open panels that provide more details or supplementary UIs. + + + + + +
+ +## Basic Usage + +```jsx + + + + + + + +``` + +> [!NOTE] +> To enable exit animations, you'll need to wrap `` in an `` component, and render the panel conditionally (e.g., `{isPanelOpen &&}`) instead of using the `collapsed` prop. diff --git a/static/app/components/slideOverPanel.tsx b/static/app/components/core/slideOverPanel/slideOverPanel.tsx similarity index 89% rename from static/app/components/slideOverPanel.tsx rename to static/app/components/core/slideOverPanel/slideOverPanel.tsx index 6bb2e81a334001..ea7e981523d262 100644 --- a/static/app/components/slideOverPanel.tsx +++ b/static/app/components/core/slideOverPanel/slideOverPanel.tsx @@ -24,20 +24,27 @@ const COLLAPSED_STYLES = { type SlideOverPanelProps = { children: React.ReactNode; + /** + * Whether the panel is visible. In most cases it's better to conditionally render this component rather than use this prop, since it'll defer rendering the panel contents until they're needed. + */ collapsed: boolean; ariaLabel?: string; className?: string; 'data-test-id'?: string; + /** + * Callback that fires every time the panel opens. + */ onOpen?: () => void; panelWidth?: string; ref?: React.Ref; slidePosition?: 'right' | 'bottom' | 'left'; + /** + * A Framer Motion `Transition` object that specifies the transition properties that apply when the panel opens and closes. + */ transitionProps?: Transition; }; -export default SlideOverPanel; - -function SlideOverPanel({ +export function SlideOverPanel({ 'data-test-id': testId, ariaLabel, collapsed, diff --git a/static/app/components/globalDrawer/components.tsx b/static/app/components/globalDrawer/components.tsx index e910255acd515d..13a9ade6395a52 100644 --- a/static/app/components/globalDrawer/components.tsx +++ b/static/app/components/globalDrawer/components.tsx @@ -3,9 +3,10 @@ import styled from '@emotion/styled'; import {mergeRefs} from '@react-aria/utils'; import type {Transition} from 'framer-motion'; +import {SlideOverPanel} from '@sentry/scraps/slideOverPanel'; + import {Button} from 'sentry/components/core/button'; import type {DrawerOptions} from 'sentry/components/globalDrawer'; -import SlideOverPanel from 'sentry/components/slideOverPanel'; import {IconClose} from 'sentry/icons/iconClose'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; diff --git a/static/app/stories/playground/slideOverPanel.tsx b/static/app/stories/playground/slideOverPanel.tsx new file mode 100644 index 00000000000000..cea49221374dce --- /dev/null +++ b/static/app/stories/playground/slideOverPanel.tsx @@ -0,0 +1,26 @@ +import {Fragment, useState} from 'react'; +import {AnimatePresence} from 'framer-motion'; + +import {Button} from '@sentry/scraps/button'; +import {Container} from '@sentry/scraps/layout'; +import {SlideOverPanel} from '@sentry/scraps/slideOverPanel'; + +export function SlideOverPanelPlayground() { + const [isPanelOpen, setIsPanelOpen] = useState(false); + + return ( + + + + + {isPanelOpen && ( + + + + + + )} + + + ); +} diff --git a/static/app/views/dashboards/widgetBuilder/components/widgetBuilderSlideout.tsx b/static/app/views/dashboards/widgetBuilder/components/widgetBuilderSlideout.tsx index 6df5704a393329..be705c81d11166 100644 --- a/static/app/views/dashboards/widgetBuilder/components/widgetBuilderSlideout.tsx +++ b/static/app/views/dashboards/widgetBuilder/components/widgetBuilderSlideout.tsx @@ -3,13 +3,14 @@ import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; import isEqual from 'lodash/isEqual'; +import {SlideOverPanel} from '@sentry/scraps/slideOverPanel'; + import {Breadcrumbs} from 'sentry/components/breadcrumbs'; import {openConfirmModal} from 'sentry/components/confirm'; import {Alert} from 'sentry/components/core/alert'; import {Button} from 'sentry/components/core/button'; import {ExternalLink, Link} from 'sentry/components/core/link'; import ErrorBoundary from 'sentry/components/errorBoundary'; -import SlideOverPanel from 'sentry/components/slideOverPanel'; import {IconClose} from 'sentry/icons'; import {t, tctCode} from 'sentry/locale'; import {space} from 'sentry/styles/space'; diff --git a/static/app/views/preprod/buildDetails/main/insights/appSizeInsightsSidebar.tsx b/static/app/views/preprod/buildDetails/main/insights/appSizeInsightsSidebar.tsx index 5444bb31a6735d..d5cfd9c713e4ae 100644 --- a/static/app/views/preprod/buildDetails/main/insights/appSizeInsightsSidebar.tsx +++ b/static/app/views/preprod/buildDetails/main/insights/appSizeInsightsSidebar.tsx @@ -4,10 +4,10 @@ import {AnimatePresence, motion} from 'framer-motion'; import {Button} from '@sentry/scraps/button'; import {Flex} from '@sentry/scraps/layout'; +import {SlideOverPanel} from '@sentry/scraps/slideOverPanel'; import {Heading} from '@sentry/scraps/text/heading'; import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip'; -import SlideOverPanel from 'sentry/components/slideOverPanel'; import {IconClose, IconGrabbable} from 'sentry/icons'; import {t} from 'sentry/locale'; import {useResizableDrawer} from 'sentry/utils/useResizableDrawer'; diff --git a/static/app/views/prevent/preventAI/manageReposPanel.tsx b/static/app/views/prevent/preventAI/manageReposPanel.tsx index 82d8abc5ed661b..790ba73c4011fc 100644 --- a/static/app/views/prevent/preventAI/manageReposPanel.tsx +++ b/static/app/views/prevent/preventAI/manageReposPanel.tsx @@ -1,5 +1,7 @@ import {Fragment} from 'react'; +import {SlideOverPanel} from '@sentry/scraps/slideOverPanel'; + import {Alert} from 'sentry/components/core/alert'; import {Button} from 'sentry/components/core/button'; import {CompactSelect} from 'sentry/components/core/compactSelect'; @@ -9,7 +11,6 @@ import {Switch} from 'sentry/components/core/switch'; import {Heading, Text} from 'sentry/components/core/text'; import FieldGroup from 'sentry/components/forms/fieldGroup'; import LoadingIndicator from 'sentry/components/loadingIndicator'; -import SlideOverPanel from 'sentry/components/slideOverPanel'; import {IconClose} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import type {OrganizationIntegration, Repository} from 'sentry/types/integrations';