diff --git a/docs-ui/src/content/PasswordField.tsx b/docs-ui/src/content/PasswordField.tsx new file mode 100644 index 0000000000..953ec3515e --- /dev/null +++ b/docs-ui/src/content/PasswordField.tsx @@ -0,0 +1,8 @@ +'use client'; + +import * as stories from '../../../packages/ui/src/components/PasswordField/PasswordField.stories'; + +export const Default = () => { + const { CustomField } = stories; + return ; +}; diff --git a/docs-ui/src/content/password-field.mdx b/docs-ui/src/content/password-field.mdx index 8dda2b4f2a..2b6d3c1ca6 100644 --- a/docs-ui/src/content/password-field.mdx +++ b/docs-ui/src/content/password-field.mdx @@ -13,53 +13,11 @@ import { Theming } from '@/components/Theming'; import { ChangelogComponent } from '@/components/ChangelogComponent'; import { CodeBlock } from '@/components/CodeBlock'; import { PasswordFieldDefinition } from '../utils/definitions'; +import { Default } from './PasswordField'; -} - code={passwordFieldDefaultSnippet} -/> - -## Usage - - - -## API reference - - - -## Examples - -### Sizes - -We support two different sizes: `small`, `medium`. - -} - code={passwordFieldSizesSnippet} -/> - -### With description - -Here's a simple PasswordField with a description. - -} - code={passwordFieldDescriptionSnippet} -/> - - - - + diff --git a/docs-ui/src/content/popover.mdx b/docs-ui/src/content/popover.mdx new file mode 100644 index 0000000000..ca3f728b01 --- /dev/null +++ b/docs-ui/src/content/popover.mdx @@ -0,0 +1,102 @@ +import { PropsTable } from '@/components/PropsTable'; +import { PopoverSnippet } from '@/snippets/stories-snippets'; +import { Snippet } from '@/components/Snippet'; +import { CodeBlock } from '@/components/CodeBlock'; +import { + popoverPropDefs, + popoverUsageSnippet, + popoverDefaultSnippet, + popoverPlacementsSnippet, + popoverNoArrowSnippet, + popoverRichContentSnippet, + popoverNonModalSnippet, +} from './popover.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { PopoverDefinition } from '../utils/definitions'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; + + + +} + code={popoverDefaultSnippet} +/> + +## Usage + + + +## API reference + +### Popover + +The main popover component that displays floating content with automatic positioning. + + + +## Examples + +### Placements + +Popover supports multiple placement options to position the content relative to the trigger. + +} + code={popoverPlacementsSnippet} +/> + +### Without Arrow + +You can hide the arrow by setting the `hideArrow` prop. + +} + code={popoverNoArrowSnippet} +/> + +### Rich Content + +Popovers can contain complex content including buttons, forms, and other interactive elements. + +} + code={popoverRichContentSnippet} +/> + +### Non-Modal + +Non-modal popovers allow interaction with other elements on the page while the popover is open. + +} + code={popoverNonModalSnippet} +/> + +### Long Content + +Popovers automatically handle scrolling when content exceeds the available viewport space. + +} + code={popoverDefaultSnippet} +/> + + + + diff --git a/docs-ui/src/content/popover.props.ts b/docs-ui/src/content/popover.props.ts new file mode 100644 index 0000000000..74194c6120 --- /dev/null +++ b/docs-ui/src/content/popover.props.ts @@ -0,0 +1,174 @@ +import { + childrenPropDefs, + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; + +export const popoverPropDefs: Record = { + hideArrow: { + type: 'boolean', + default: 'false', + description: + 'Whether to hide the arrow pointing to the trigger element. Arrow is also automatically hidden for MenuTrigger and SubmenuTrigger contexts.', + }, + placement: { + type: 'enum', + values: [ + 'top', + 'top start', + 'top end', + 'bottom', + 'bottom start', + 'bottom end', + 'left', + 'left start', + 'left end', + 'right', + 'right start', + 'right end', + ], + default: 'bottom', + description: 'The placement of the popover relative to the trigger.', + }, + containerPadding: { + type: 'number', + default: '12', + description: + 'The padding between the popover and the edge of the viewport.', + }, + offset: { + type: 'number', + default: '0', + description: 'The offset from the trigger element.', + }, + crossOffset: { + type: 'number', + default: '0', + description: 'The cross-axis offset from the trigger element.', + }, + shouldFlip: { + type: 'boolean', + default: 'true', + description: + 'Whether the popover should flip to the opposite side when there is not enough space.', + }, + isOpen: { + type: 'boolean', + description: 'Whether the popover is open (controlled).', + }, + defaultOpen: { + type: 'boolean', + description: 'Whether the popover is open by default (uncontrolled).', + }, + onOpenChange: { + type: 'enum', + values: ['(isOpen: boolean) => void'], + description: + "Handler that is called when the popover's open state changes.", + }, + isNonModal: { + type: 'boolean', + default: 'false', + description: + 'Whether the popover is non-modal. Non-modal popovers do not block interaction with the rest of the page.', + }, + isKeyboardDismissDisabled: { + type: 'boolean', + default: 'false', + description: + 'Whether pressing the escape key to close the popover should be disabled.', + }, + shouldCloseOnBlur: { + type: 'boolean', + default: 'true', + description: + 'Whether to close the popover when the user interacts outside it.', + }, + triggerRef: { + type: 'enum', + values: ['RefObject'], + description: 'A ref for the trigger element.', + }, + ...childrenPropDefs, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const popoverUsageSnippet = `import { Popover, DialogTrigger, Button } from '@backstage/ui'; + + + + + This is a popover + +`; + +export const popoverDefaultSnippet = ` + + + This is a popover + +`; + +export const popoverPlacementsSnippet = ` + + + Top placement + + + + + + + Right placement + + + + + + + Bottom placement + + + + + + + Left placement + +`; + +export const popoverNoArrowSnippet = ` + + + Popover without arrow + +`; + +export const popoverRichContentSnippet = ` + + + + Popover Title + + This is a popover with rich content. It can contain multiple + elements and formatted text. + + + + + + + +`; + +export const popoverNonModalSnippet = ` + + + + This is a non-modal popover. You can interact with other + elements on the page while it's open. + + +`; diff --git a/docs-ui/src/snippets/stories-snippets.tsx b/docs-ui/src/snippets/stories-snippets.tsx index ba722a5edb..9297844baa 100644 --- a/docs-ui/src/snippets/stories-snippets.tsx +++ b/docs-ui/src/snippets/stories-snippets.tsx @@ -1,6 +1,5 @@ 'use client'; -import { composeStories } from '@storybook/react'; import * as BoxStories from '../../../packages/ui/src/components/Box/Box.stories'; import * as ButtonStories from '../../../packages/ui/src/components/Button/Button.stories'; import * as ButtonIconStories from '../../../packages/ui/src/components/ButtonIcon/ButtonIcon.stories'; @@ -29,6 +28,7 @@ import * as HeaderPageStories from '../../../packages/ui/src/components/HeaderPa import * as TableStories from '../../../packages/ui/src/components/Table/stories/Table.docs.stories'; import * as TagGroupStories from '../../../packages/ui/src/components/TagGroup/TagGroup.stories'; import * as PasswordFieldStories from '../../../packages/ui/src/components/PasswordField/PasswordField.stories'; +import * as PopoverStories from '../../../packages/ui/src/components/Popover/Popover.stories'; import * as VisuallyHiddenStories from '../../../packages/ui/src/components/VisuallyHidden/VisuallyHidden.stories'; import * as ToggleButtonStories from '../../../packages/ui/src/components/ToggleButton/ToggleButton.stories'; import * as ToggleButtonGroupStories from '../../../packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories'; @@ -37,11 +37,9 @@ import * as ToggleButtonGroupStories from '../../../packages/ui/src/components/T // eslint-disable-next-line @typescript-eslint/no-explicit-any const createSnippetComponent = (stories: any) => { return function SnippetComponent({ story }: { story: string }) { - const composedStories = composeStories(stories); - const StoryComponent = composedStories[ - story as keyof typeof composedStories - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ] as any; + // In CSF3, stories are already React components that can be rendered directly + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const StoryComponent = stories[story as keyof typeof stories] as any; return StoryComponent ? : null; }; @@ -58,6 +56,7 @@ export const GridSnippet = createSnippetComponent(GridStories); export const TextFieldSnippet = createSnippetComponent(TextFieldStories); export const PasswordFieldSnippet = createSnippetComponent(PasswordFieldStories); +export const PopoverSnippet = createSnippetComponent(PopoverStories); export const TextSnippet = createSnippetComponent(TextStories); export const FlexSnippet = createSnippetComponent(FlexStories); export const SelectSnippet = createSnippetComponent(SelectStories); diff --git a/docs-ui/src/utils/data.ts b/docs-ui/src/utils/data.ts index b20725142c..1b046fd3e5 100644 --- a/docs-ui/src/utils/data.ts +++ b/docs-ui/src/utils/data.ts @@ -76,6 +76,11 @@ export const components: Page[] = [ title: 'PasswordField', slug: 'password-field', }, + { + title: 'Popover', + slug: 'popover', + status: 'new', + }, { title: 'RadioGroup', slug: 'radio-group',