diff --git a/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md b/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md index e4cc48f2a1684..6c00a5ed41093 100644 --- a/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md +++ b/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md @@ -4,6 +4,135 @@ ```ts +/// + +import type { ComponentProps } from '@fluentui/react-utilities'; +import type { ComponentState } from '@fluentui/react-utilities'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import * as React_2 from 'react'; +import type { Slot } from '@fluentui/react-utilities'; +import type { SlotClassNames } from '@fluentui/react-utilities'; + +// @public +export const MessageBar: ForwardRefComponent; + +// @public +export const MessageBarActions: ForwardRefComponent; + +// @public (undocumented) +export const messageBarActionsClassNames: SlotClassNames; + +// @public +export type MessageBarActionsProps = ComponentProps & {}; + +// @public (undocumented) +export type MessageBarActionsSlots = { + root: Slot<'div'>; + containerAction?: Slot<'div'>; +}; + +// @public +export type MessageBarActionsState = ComponentState & Pick, 'layout'>; + +// @public +export const MessageBarBody: ForwardRefComponent; + +// @public (undocumented) +export const messageBarBodyClassNames: SlotClassNames; + +// @public +export type MessageBarBodyProps = ComponentProps & {}; + +// @public (undocumented) +export type MessageBarBodySlots = { + root: Slot<'div'>; +}; + +// @public +export type MessageBarBodyState = ComponentState; + +// @public (undocumented) +export const messageBarClassNames: SlotClassNames; + +// @public (undocumented) +export const MessageBarContextProvider: React_2.Provider; + +// @public (undocumented) +export type MessageBarContextValue = { + layout?: 'multiline' | 'singleline'; +}; + +// @public +export type MessageBarProps = ComponentProps & Pick & { + multiline?: boolean; + intent?: 'info' | 'success' | 'warning' | 'error'; +}; + +// @public (undocumented) +export type MessageBarSlots = { + root: Slot<'div'>; + icon?: Slot<'div'>; +}; + +// @public +export type MessageBarState = ComponentState & Required>; + +// @public +export const MessageBarTitle: ForwardRefComponent; + +// @public (undocumented) +export const messageBarTitleClassNames: SlotClassNames; + +// @public +export type MessageBarTitleProps = ComponentProps & {}; + +// @public (undocumented) +export type MessageBarTitleSlots = { + root: Slot<'span'>; +}; + +// @public +export type MessageBarTitleState = ComponentState; + +// @public +export const renderMessageBar_unstable: (state: MessageBarState, contexts: MessageBarContextValues) => JSX.Element; + +// @public +export const renderMessageBarActions_unstable: (state: MessageBarActionsState) => JSX.Element; + +// @public +export const renderMessageBarBody_unstable: (state: MessageBarBodyState) => JSX.Element; + +// @public +export const renderMessageBarTitle_unstable: (state: MessageBarTitleState) => JSX.Element; + +// @public +export const useMessageBar_unstable: (props: MessageBarProps, ref: React_2.Ref) => MessageBarState; + +// @public +export const useMessageBarActions_unstable: (props: MessageBarActionsProps, ref: React_2.Ref) => MessageBarActionsState; + +// @public +export const useMessageBarActionsStyles_unstable: (state: MessageBarActionsState) => MessageBarActionsState; + +// @public +export const useMessageBarBody_unstable: (props: MessageBarBodyProps, ref: React_2.Ref) => MessageBarBodyState; + +// @public +export const useMessageBarBodyStyles_unstable: (state: MessageBarBodyState) => MessageBarBodyState; + +// @public (undocumented) +export const useMessageBarContext: () => MessageBarContextValue; + +// @public +export const useMessageBarStyles_unstable: (state: MessageBarState) => MessageBarState; + +// @public +export const useMessageBarTitle_unstable: (props: MessageBarTitleProps, ref: React_2.Ref) => MessageBarTitleState; + +// @public +export const useMessageBarTitleStyles_unstable: (state: MessageBarTitleState) => MessageBarTitleState; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-message-bar-preview/package.json b/packages/react-components/react-message-bar-preview/package.json index f7c1363951fbd..5f6653aaf116f 100644 --- a/packages/react-components/react-message-bar-preview/package.json +++ b/packages/react-components/react-message-bar-preview/package.json @@ -31,6 +31,8 @@ "@fluentui/scripts-tasks": "*" }, "dependencies": { + "@fluentui/react-button": "^9.3.43", + "@fluentui/react-icons": "^2.0.217", "@fluentui/react-jsx-runtime": "^9.0.12", "@fluentui/react-theme": "^9.1.14", "@fluentui/react-utilities": "^9.13.5", diff --git a/packages/react-components/react-message-bar-preview/src/MessageBar.ts b/packages/react-components/react-message-bar-preview/src/MessageBar.ts new file mode 100644 index 0000000000000..380da827afeaa --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/MessageBar.ts @@ -0,0 +1 @@ +export * from './components/MessageBar/index'; diff --git a/packages/react-components/react-message-bar-preview/src/MessageBarActions.ts b/packages/react-components/react-message-bar-preview/src/MessageBarActions.ts new file mode 100644 index 0000000000000..7ab9044a3ee73 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/MessageBarActions.ts @@ -0,0 +1 @@ +export * from './components/MessageBarActions/index'; diff --git a/packages/react-components/react-message-bar-preview/src/MessageBarBody.ts b/packages/react-components/react-message-bar-preview/src/MessageBarBody.ts new file mode 100644 index 0000000000000..dc59e16a3e414 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/MessageBarBody.ts @@ -0,0 +1 @@ +export * from './components/MessageBarBody/index'; diff --git a/packages/react-components/react-message-bar-preview/src/MessageBarTitle.ts b/packages/react-components/react-message-bar-preview/src/MessageBarTitle.ts new file mode 100644 index 0000000000000..e570c6fb916df --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/MessageBarTitle.ts @@ -0,0 +1 @@ +export * from './components/MessageBarTitle/index'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx new file mode 100644 index 0000000000000..ca8524d34d7bb --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { isConformant } from '../../testing/isConformant'; +import { MessageBar } from './MessageBar'; + +describe('MessageBar', () => { + isConformant({ + Component: MessageBar, + displayName: 'MessageBar', + testOptions: { + 'has-static-classnames': [ + { + props: { + icon: 'Icon', + }, + }, + ], + }, + }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests + + it('renders a default state', () => { + const result = render(Default MessageBar); + expect(result.container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.tsx new file mode 100644 index 0000000000000..27f443e9d07bf --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useMessageBar_unstable } from './useMessageBar'; +import { renderMessageBar_unstable } from './renderMessageBar'; +import { useMessageBarStyles_unstable } from './useMessageBarStyles.styles'; +import type { MessageBarProps } from './MessageBar.types'; +import { useMessageBarContextValue_unstable } from './useMessageBarContextValues'; + +/** + * MessageBar component + */ +export const MessageBar: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useMessageBar_unstable(props, ref); + + useMessageBarStyles_unstable(state); + return renderMessageBar_unstable(state, useMessageBarContextValue_unstable(state)); +}); + +MessageBar.displayName = 'MessageBar'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts new file mode 100644 index 0000000000000..9d0bce2e0aa9e --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts @@ -0,0 +1,25 @@ +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import { MessageBarContextValue } from '../../contexts/messageBarContext'; + +export type MessageBarSlots = { + root: Slot<'div'>; + icon?: Slot<'div'>; +}; + +export type MessageBarContextValues = { + messageBar: MessageBarContextValue; +}; + +/** + * MessageBar Props + */ +export type MessageBarProps = ComponentProps & + Pick & { + multiline?: boolean; + intent?: 'info' | 'success' | 'warning' | 'error'; + }; + +/** + * State used in rendering MessageBar + */ +export type MessageBarState = ComponentState & Required>; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/__snapshots__/MessageBar.test.tsx.snap b/packages/react-components/react-message-bar-preview/src/components/MessageBar/__snapshots__/MessageBar.test.tsx.snap new file mode 100644 index 0000000000000..17d8f7e24e3ea --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/__snapshots__/MessageBar.test.tsx.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageBar renders a default state 1`] = ` +
+
+
+ +
+ Default MessageBar +
+
+`; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/getIntentIcon.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBar/getIntentIcon.tsx new file mode 100644 index 0000000000000..8b8f345ce332f --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/getIntentIcon.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { MessageBarProps } from './MessageBar.types'; +import { CheckmarkCircleFilled, InfoFilled, WarningFilled, ErrorCircleFilled } from '@fluentui/react-icons'; + +export function getIntentIcon(intent: MessageBarProps['intent']) { + switch (intent) { + case 'info': + return ; + case 'warning': + return ; + case 'error': + return ; + case 'success': + return ; + + default: + return null; + } +} diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/index.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/index.ts new file mode 100644 index 0000000000000..1b87b685202c5 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/index.ts @@ -0,0 +1,5 @@ +export * from './MessageBar'; +export * from './MessageBar.types'; +export * from './renderMessageBar'; +export * from './useMessageBar'; +export * from './useMessageBarStyles.styles'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/renderMessageBar.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBar/renderMessageBar.tsx new file mode 100644 index 0000000000000..83aa749e3d1ef --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/renderMessageBar.tsx @@ -0,0 +1,22 @@ +/** @jsxRuntime automatic */ +/** @jsxImportSource @fluentui/react-jsx-runtime */ + +import { assertSlots } from '@fluentui/react-utilities'; +import type { MessageBarState, MessageBarSlots, MessageBarContextValues } from './MessageBar.types'; +import { MessageBarContextProvider } from '../../contexts/messageBarContext'; + +/** + * Render the final JSX of MessageBar + */ +export const renderMessageBar_unstable = (state: MessageBarState, contexts: MessageBarContextValues) => { + assertSlots(state); + + return ( + + + {state.icon && } + {state.root.children} + + + ); +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts new file mode 100644 index 0000000000000..b9759360a050d --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { getNativeElementProps, slot } from '@fluentui/react-utilities'; +import type { MessageBarProps, MessageBarState } from './MessageBar.types'; +import { getIntentIcon } from './getIntentIcon'; + +/** + * Create the state required to render MessageBar. + * + * The returned state can be modified with hooks such as useMessageBarStyles_unstable, + * before being passed to renderMessageBar_unstable. + * + * @param props - props from this instance of MessageBar + * @param ref - reference to root HTMLElement of MessageBar + */ +export const useMessageBar_unstable = (props: MessageBarProps, ref: React.Ref): MessageBarState => { + const { layout = 'singleline', intent = 'info' } = props; + + return { + components: { + root: 'div', + icon: 'div', + }, + root: slot.always( + getNativeElementProps('div', { + ref, + ...props, + }), + { elementType: 'div' }, + ), + + icon: slot.optional(props.icon, { + renderByDefault: true, + elementType: 'div', + defaultProps: { children: getIntentIcon(intent) }, + }), + layout, + intent, + }; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarContextValues.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarContextValues.ts new file mode 100644 index 0000000000000..c3f1d5322b479 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarContextValues.ts @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { MessageBarContextValues, MessageBarState } from './MessageBar.types'; + +export function useMessageBarContextValue_unstable(state: MessageBarState): MessageBarContextValues { + const { layout } = state; + + const messageBarContext = React.useMemo( + () => ({ + layout, + }), + [layout], + ); + + return { + messageBar: messageBarContext, + }; +} diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarStyles.styles.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarStyles.styles.ts new file mode 100644 index 0000000000000..974c68afba45f --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBarStyles.styles.ts @@ -0,0 +1,109 @@ +import { makeResetStyles, makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; +import type { SlotClassNames } from '@fluentui/react-utilities'; +import type { MessageBarSlots, MessageBarState } from './MessageBar.types'; + +export const messageBarClassNames: SlotClassNames = { + root: 'fui-MessageBar', + icon: 'fui-MessageBar__icon', +}; + +const useRootBaseStyles = makeResetStyles({ + display: 'grid', + gridTemplateColumns: 'auto 1fr auto auto', + gridTemplateAreas: '"icon body secondaryActions actions"', + ...shorthands.padding('0', tokens.spacingHorizontalM), + ...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1), + ...shorthands.borderRadius(tokens.borderRadiusMedium), + alignItems: 'center', + minHeight: '36px', + boxSizing: 'border-box', + backgroundColor: tokens.colorNeutralBackground3, +}); + +const useIconBaseStyles = makeResetStyles({ + ...shorthands.gridArea('icon'), + fontSize: tokens.fontSizeBase500, + marginRight: tokens.spacingHorizontalS, + color: tokens.colorNeutralForeground3, +}); + +const useMultilineStyles = makeStyles({ + rootMultiline: { + paddingTop: tokens.spacingVerticalMNudge, + gridTemplateColumns: 'auto 1fr auto', + gridTemplateAreas: ` + "icon body actions" + "secondaryActions secondaryActions secondaryActions" + `, + }, + + secondaryActionsMultiline: { + justifyContent: 'end', + marginTop: tokens.spacingVerticalMNudge, + marginBottom: tokens.spacingVerticalS, + marginRight: '0px', + }, +}); + +const useIconIntentStyles = makeStyles({ + info: { + /** already in base reset styles */ + }, + error: { + color: tokens.colorStatusDangerForeground1, + }, + warning: { + color: tokens.colorStatusWarningForeground3, + }, + success: { + color: tokens.colorStatusSuccessForeground1, + }, +}); + +const useRootIntentStyles = makeStyles({ + info: { + /** already in base reset styles */ + }, + error: { + backgroundColor: tokens.colorStatusDangerBackground1, + ...shorthands.borderColor(tokens.colorStatusDangerBorder1), + }, + warning: { + backgroundColor: tokens.colorStatusWarningBackground1, + ...shorthands.borderColor(tokens.colorStatusWarningBorder1), + }, + success: { + backgroundColor: tokens.colorStatusSuccessBackground1, + ...shorthands.borderColor(tokens.colorStatusSuccessBorder1), + }, +}); + +/** + * Apply styling to the MessageBar slots based on the state + */ +export const useMessageBarStyles_unstable = (state: MessageBarState): MessageBarState => { + const rootBaseStyles = useRootBaseStyles(); + const iconBaseStyles = useIconBaseStyles(); + const multilineStyles = useMultilineStyles(); + const iconIntentStyles = useIconIntentStyles(); + const rootIntntStyles = useRootIntentStyles(); + state.root.className = mergeClasses( + messageBarClassNames.root, + rootBaseStyles, + state.layout === 'multiline' && multilineStyles.rootMultiline, + rootIntntStyles[state.intent], + state.root.className, + ); + + if (state.icon) { + state.icon.className = mergeClasses( + messageBarClassNames.icon, + iconBaseStyles, + iconIntentStyles[state.intent], + state.icon.className, + ); + } + + return state; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.test.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.test.tsx new file mode 100644 index 0000000000000..d62960938f7ba --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.test.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { isConformant } from '../../testing/isConformant'; +import { MessageBarActions } from './MessageBarActions'; +import { MessageBarActionsProps } from './MessageBarActions.types'; + +describe('MessageBarActions', () => { + isConformant({ + Component: MessageBarActions, + displayName: 'MessageBarActions', + disabledTests: [ + // TODO: having problems due to the fact root of DialogTitle is Fragment + 'component-has-static-classnames-object', + ], + }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests + + it('renders a default state', () => { + const result = render(Default MessageBarActions); + expect(result.container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx new file mode 100644 index 0000000000000..ca28530fe5939 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useMessageBarActions_unstable } from './useMessageBarActions'; +import { renderMessageBarActions_unstable } from './renderMessageBarActions'; +import { useMessageBarActionsStyles_unstable } from './useMessageBarActionsStyles.styles'; +import type { MessageBarActionsProps } from './MessageBarActions.types'; + +/** + * MessageBarActions component - TODO: add more docs + */ +export const MessageBarActions: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useMessageBarActions_unstable(props, ref); + + useMessageBarActionsStyles_unstable(state); + return renderMessageBarActions_unstable(state); +}); + +MessageBarActions.displayName = 'MessageBarActions'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts new file mode 100644 index 0000000000000..796972f7bbf8e --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts @@ -0,0 +1,18 @@ +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import { MessageBarContextValue } from '../../contexts/messageBarContext'; + +export type MessageBarActionsSlots = { + root: Slot<'div'>; + containerAction?: Slot<'div'>; +}; + +/** + * MessageBarActions Props + */ +export type MessageBarActionsProps = ComponentProps & {}; + +/** + * State used in rendering MessageBarActions + */ +export type MessageBarActionsState = ComponentState & + Pick, 'layout'>; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/__snapshots__/MessageBarActions.test.tsx.snap b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/__snapshots__/MessageBarActions.test.tsx.snap new file mode 100644 index 0000000000000..aa2eebdf434aa --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/__snapshots__/MessageBarActions.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageBarActions renders a default state 1`] = ` +
+
+ Default MessageBarActions +
+
+`; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/index.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/index.ts new file mode 100644 index 0000000000000..d8ba24f39657b --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/index.ts @@ -0,0 +1,5 @@ +export * from './MessageBarActions'; +export * from './MessageBarActions.types'; +export * from './renderMessageBarActions'; +export * from './useMessageBarActions'; +export * from './useMessageBarActionsStyles.styles'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx new file mode 100644 index 0000000000000..15ddc4bf084e3 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx @@ -0,0 +1,28 @@ +/** @jsxRuntime automatic */ +/** @jsxImportSource @fluentui/react-jsx-runtime */ + +import { assertSlots } from '@fluentui/react-utilities'; +import type { MessageBarActionsState, MessageBarActionsSlots } from './MessageBarActions.types'; +import { ButtonContextProvider } from '@fluentui/react-button'; + +/** + * Render the final JSX of MessageBarActions + */ +export const renderMessageBarActions_unstable = (state: MessageBarActionsState) => { + assertSlots(state); + if (state.layout === 'multiline') { + return ( + + {state.containerAction && } + + + ); + } + + return ( + + + {state.containerAction && } + + ); +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts new file mode 100644 index 0000000000000..5ccf97a36a718 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { getNativeElementProps, slot } from '@fluentui/react-utilities'; +import type { MessageBarActionsProps, MessageBarActionsState } from './MessageBarActions.types'; +import { useMessageBarContext } from '../../contexts/messageBarContext'; + +/** + * Create the state required to render MessageBarActions. + * + * The returned state can be modified with hooks such as useMessageBarActionsStyles_unstable, + * before being passed to renderMessageBarActions_unstable. + * + * @param props - props from this instance of MessageBarActions + * @param ref - reference to root HTMLElement of MessageBarActions + */ +export const useMessageBarActions_unstable = ( + props: MessageBarActionsProps, + ref: React.Ref, +): MessageBarActionsState => { + const { layout = 'singleline' } = useMessageBarContext(); + return { + components: { + root: 'div', + containerAction: 'div', + }, + containerAction: slot.optional(props.containerAction, { renderByDefault: false, elementType: 'div' }), + root: slot.always( + getNativeElementProps('div', { + ref, + ...props, + }), + { elementType: 'div' }, + ), + layout, + }; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsStyles.styles.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsStyles.styles.ts new file mode 100644 index 0000000000000..dead8bcab465b --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsStyles.styles.ts @@ -0,0 +1,58 @@ +import { makeResetStyles, makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import type { SlotClassNames } from '@fluentui/react-utilities'; +import { tokens } from '@fluentui/react-theme'; +import type { MessageBarActionsSlots, MessageBarActionsState } from './MessageBarActions.types'; + +export const messageBarActionsClassNames: SlotClassNames = { + root: 'fui-MessageBarActions', + containerAction: 'fui-MessageBarActions__containerAction', +}; + +/** + * Styles for the root slot + */ +const useRootBaseStyles = makeResetStyles({ + ...shorthands.gridArea('secondaryActions'), + display: 'flex', + alignItems: 'center', + columnGap: tokens.spacingHorizontalM, + marginRight: tokens.spacingHorizontalM, +}); + +const useContainerActionBaseStyles = makeResetStyles({ + ...shorthands.gridArea('actions'), +}); + +const useMultilineStyles = makeStyles({ + root: { + justifyContent: 'end', + marginTop: tokens.spacingVerticalMNudge, + marginBottom: tokens.spacingVerticalS, + marginRight: '0px', + }, +}); + +/** + * Apply styling to the MessageBarActions slots based on the state + */ +export const useMessageBarActionsStyles_unstable = (state: MessageBarActionsState): MessageBarActionsState => { + const rootBaseStyles = useRootBaseStyles(); + const containerActionBaseStyles = useContainerActionBaseStyles(); + const multilineStyles = useMultilineStyles(); + state.root.className = mergeClasses( + messageBarActionsClassNames.root, + rootBaseStyles, + state.layout === 'multiline' && multilineStyles.root, + state.root.className, + ); + + if (state.containerAction) { + state.containerAction.className = mergeClasses( + messageBarActionsClassNames.containerAction, + containerActionBaseStyles, + state.containerAction.className, + ); + } + + return state; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.test.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.test.tsx new file mode 100644 index 0000000000000..b0bc0a9968aa1 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.test.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { isConformant } from '../../testing/isConformant'; +import { MessageBarBody } from './MessageBarBody'; + +describe('MessageBarBody', () => { + isConformant({ + Component: MessageBarBody, + displayName: 'MessageBarBody', + }); + + it('renders a default state', () => { + const result = render(Default MessageBarBody); + expect(result.container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.tsx new file mode 100644 index 0000000000000..21e15ad108048 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useMessageBarBody_unstable } from './useMessageBarBody'; +import { renderMessageBarBody_unstable } from './renderMessageBarBody'; +import { useMessageBarBodyStyles_unstable } from './useMessageBarBodyStyles.styles'; +import type { MessageBarBodyProps } from './MessageBarBody.types'; + +/** + * MessageBarBody component + */ +export const MessageBarBody: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useMessageBarBody_unstable(props, ref); + + useMessageBarBodyStyles_unstable(state); + return renderMessageBarBody_unstable(state); +}); + +MessageBarBody.displayName = 'MessageBarBody'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.types.ts new file mode 100644 index 0000000000000..f471468f316af --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/MessageBarBody.types.ts @@ -0,0 +1,15 @@ +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; + +export type MessageBarBodySlots = { + root: Slot<'div'>; +}; + +/** + * MessageBarBody Props + */ +export type MessageBarBodyProps = ComponentProps & {}; + +/** + * State used in rendering MessageBarBody + */ +export type MessageBarBodyState = ComponentState; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/__snapshots__/MessageBarBody.test.tsx.snap b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/__snapshots__/MessageBarBody.test.tsx.snap new file mode 100644 index 0000000000000..4c73379056bad --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/__snapshots__/MessageBarBody.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageBarBody renders a default state 1`] = ` +
+
+ Default MessageBarBody +
+
+`; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/index.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/index.ts new file mode 100644 index 0000000000000..360550329c1df --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/index.ts @@ -0,0 +1,5 @@ +export * from './MessageBarBody'; +export * from './MessageBarBody.types'; +export * from './renderMessageBarBody'; +export * from './useMessageBarBody'; +export * from './useMessageBarBodyStyles.styles'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/renderMessageBarBody.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/renderMessageBarBody.tsx new file mode 100644 index 0000000000000..fa5e80f1b3da2 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/renderMessageBarBody.tsx @@ -0,0 +1,14 @@ +/** @jsxRuntime automatic */ +/** @jsxImportSource @fluentui/react-jsx-runtime */ + +import { assertSlots } from '@fluentui/react-utilities'; +import type { MessageBarBodyState, MessageBarBodySlots } from './MessageBarBody.types'; + +/** + * Render the final JSX of MessageBarBody + */ +export const renderMessageBarBody_unstable = (state: MessageBarBodyState) => { + assertSlots(state); + + return ; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts new file mode 100644 index 0000000000000..ed99cf2f18a9b --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { getNativeElementProps, slot } from '@fluentui/react-utilities'; +import type { MessageBarBodyProps, MessageBarBodyState } from './MessageBarBody.types'; + +/** + * Create the state required to render MessageBarBody. + * + * The returned state can be modified with hooks such as useMessageBarBodyStyles_unstable, + * before being passed to renderMessageBarBody_unstable. + * + * @param props - props from this instance of MessageBarBody + * @param ref - reference to root HTMLElement of MessageBarBody + */ +export const useMessageBarBody_unstable = ( + props: MessageBarBodyProps, + ref: React.Ref, +): MessageBarBodyState => { + return { + components: { + root: 'div', + }, + root: slot.always( + getNativeElementProps('div', { + ref, + ...props, + }), + { elementType: 'div' }, + ), + }; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBodyStyles.styles.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBodyStyles.styles.ts new file mode 100644 index 0000000000000..1e470a13b0ba1 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBodyStyles.styles.ts @@ -0,0 +1,23 @@ +import { makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; +import type { SlotClassNames } from '@fluentui/react-utilities'; +import type { MessageBarBodySlots, MessageBarBodyState } from './MessageBarBody.types'; +import { typographyStyles } from '@fluentui/react-theme'; + +export const messageBarBodyClassNames: SlotClassNames = { + root: 'fui-MessageBarBody', +}; + +const useRootBaseStyles = makeResetStyles({ + ...typographyStyles.body1, + ...shorthands.gridArea('body'), +}); + +/** + * Apply styling to the MessageBarBody slots based on the state + */ +export const useMessageBarBodyStyles_unstable = (state: MessageBarBodyState): MessageBarBodyState => { + const rootBaseStyles = useRootBaseStyles(); + state.root.className = mergeClasses(messageBarBodyClassNames.root, rootBaseStyles, state.root.className); + + return state; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.test.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.test.tsx new file mode 100644 index 0000000000000..c34b6a2446dbc --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.test.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { isConformant } from '../../testing/isConformant'; +import { MessageBarTitle } from './MessageBarTitle'; + +describe('MessageBarTitle', () => { + isConformant({ + Component: MessageBarTitle, + displayName: 'MessageBarTitle', + }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests + + it('renders a default state', () => { + const result = render(Default MessageBarTitle); + expect(result.container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.tsx new file mode 100644 index 0000000000000..6ce8533ca2ec6 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useMessageBarTitle_unstable } from './useMessageBarTitle'; +import { renderMessageBarTitle_unstable } from './renderMessageBarTitle'; +import { useMessageBarTitleStyles_unstable } from './useMessageBarTitleStyles.styles'; +import type { MessageBarTitleProps } from './MessageBarTitle.types'; + +/** + * MessageBarTitle component + */ +export const MessageBarTitle: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useMessageBarTitle_unstable(props, ref); + + useMessageBarTitleStyles_unstable(state); + return renderMessageBarTitle_unstable(state); +}); + +MessageBarTitle.displayName = 'MessageBarTitle'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.types.ts new file mode 100644 index 0000000000000..d966942d2b9fd --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/MessageBarTitle.types.ts @@ -0,0 +1,15 @@ +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; + +export type MessageBarTitleSlots = { + root: Slot<'span'>; +}; + +/** + * MessageBarTitle Props + */ +export type MessageBarTitleProps = ComponentProps & {}; + +/** + * State used in rendering MessageBarTitle + */ +export type MessageBarTitleState = ComponentState; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/__snapshots__/MessageBarTitle.test.tsx.snap b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/__snapshots__/MessageBarTitle.test.tsx.snap new file mode 100644 index 0000000000000..8d96636aac25e --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/__snapshots__/MessageBarTitle.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageBarTitle renders a default state 1`] = ` +
+ + Default MessageBarTitle + +
+`; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/index.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/index.ts new file mode 100644 index 0000000000000..4bcb0e7df960b --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/index.ts @@ -0,0 +1,5 @@ +export * from './MessageBarTitle'; +export * from './MessageBarTitle.types'; +export * from './renderMessageBarTitle'; +export * from './useMessageBarTitle'; +export * from './useMessageBarTitleStyles.styles'; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/renderMessageBarTitle.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/renderMessageBarTitle.tsx new file mode 100644 index 0000000000000..6b008739d5dcb --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/renderMessageBarTitle.tsx @@ -0,0 +1,19 @@ +/** @jsxRuntime automatic */ +/** @jsxImportSource @fluentui/react-jsx-runtime */ + +import { assertSlots } from '@fluentui/react-utilities'; +import type { MessageBarTitleState, MessageBarTitleSlots } from './MessageBarTitle.types'; + +/** + * Render the final JSX of MessageBarTitle + */ +export const renderMessageBarTitle_unstable = (state: MessageBarTitleState) => { + assertSlots(state); + + // TODO Add additional slots in the appropriate place + return ( + <> + + + ); +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitle.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitle.ts new file mode 100644 index 0000000000000..87329b8eba488 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitle.ts @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { getNativeElementProps, slot } from '@fluentui/react-utilities'; +import type { MessageBarTitleProps, MessageBarTitleState } from './MessageBarTitle.types'; + +/** + * Create the state required to render MessageBarTitle. + * + * The returned state can be modified with hooks such as useMessageBarTitleStyles_unstable, + * before being passed to renderMessageBarTitle_unstable. + * + * @param props - props from this instance of MessageBarTitle + * @param ref - reference to root HTMLElement of MessageBarTitle + */ +export const useMessageBarTitle_unstable = ( + props: MessageBarTitleProps, + ref: React.Ref, +): MessageBarTitleState => { + return { + components: { + root: 'span', + }, + root: slot.always( + getNativeElementProps('span', { + ref, + ...props, + }), + { elementType: 'span' }, + ), + }; +}; diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitleStyles.styles.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitleStyles.styles.ts new file mode 100644 index 0000000000000..7bc54ff127ac8 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarTitle/useMessageBarTitleStyles.styles.ts @@ -0,0 +1,28 @@ +import { makeResetStyles, mergeClasses } from '@griffel/react'; +import { typographyStyles } from '@fluentui/react-theme'; +import type { SlotClassNames } from '@fluentui/react-utilities'; +import type { MessageBarTitleSlots, MessageBarTitleState } from './MessageBarTitle.types'; + +export const messageBarTitleClassNames: SlotClassNames = { + root: 'fui-MessageBarTitle', +}; + +/** + * Styles for the root slot + */ +const useRootBaseStyles = makeResetStyles({ + ...typographyStyles.body1Strong, + '::after': { + content: '" "', + }, +}); + +/** + * Apply styling to the MessageBarTitle slots based on the state + */ +export const useMessageBarTitleStyles_unstable = (state: MessageBarTitleState): MessageBarTitleState => { + const rootBaseStyles = useRootBaseStyles(); + state.root.className = mergeClasses(messageBarTitleClassNames.root, rootBaseStyles, state.root.className); + + return state; +}; diff --git a/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts b/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts new file mode 100644 index 0000000000000..add68734a6292 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts @@ -0,0 +1,13 @@ +import * as React from 'react'; + +export type MessageBarContextValue = { + layout?: 'multiline' | 'singleline'; +}; +const messageBarContext = React.createContext(undefined); + +export const messageBarContextDefaultValue: MessageBarContextValue = { + layout: 'singleline', +}; + +export const MessageBarContextProvider = messageBarContext.Provider; +export const useMessageBarContext = () => React.useContext(messageBarContext) ?? messageBarContextDefaultValue; diff --git a/packages/react-components/react-message-bar-preview/src/index.ts b/packages/react-components/react-message-bar-preview/src/index.ts index cb0ff5c3b541f..e4d035efe8584 100644 --- a/packages/react-components/react-message-bar-preview/src/index.ts +++ b/packages/react-components/react-message-bar-preview/src/index.ts @@ -1 +1,42 @@ -export {}; +export { + MessageBar, + useMessageBarStyles_unstable, + useMessageBar_unstable, + renderMessageBar_unstable, + messageBarClassNames, +} from './MessageBar'; + +export type { MessageBarProps, MessageBarSlots, MessageBarState } from './MessageBar'; + +export { + MessageBarTitle, + useMessageBarTitleStyles_unstable, + useMessageBarTitle_unstable, + renderMessageBarTitle_unstable, + messageBarTitleClassNames, +} from './MessageBarTitle'; + +export type { MessageBarTitleProps, MessageBarTitleSlots, MessageBarTitleState } from './MessageBarTitle'; + +export { + MessageBarActions, + useMessageBarActionsStyles_unstable, + useMessageBarActions_unstable, + renderMessageBarActions_unstable, + messageBarActionsClassNames, +} from './MessageBarActions'; + +export type { MessageBarActionsProps, MessageBarActionsSlots, MessageBarActionsState } from './MessageBarActions'; + +export { + MessageBarBody, + useMessageBarBodyStyles_unstable, + useMessageBarBody_unstable, + renderMessageBarBody_unstable, + messageBarBodyClassNames, +} from './MessageBarBody'; + +export type { MessageBarBodyProps, MessageBarBodySlots, MessageBarBodyState } from './MessageBarBody'; + +export { MessageBarContextProvider, useMessageBarContext } from './contexts/messageBarContext'; +export type { MessageBarContextValue } from './contexts/messageBarContext'; diff --git a/packages/react-components/react-message-bar-preview/stories/MessageBar/Default.stories.tsx b/packages/react-components/react-message-bar-preview/stories/MessageBar/Default.stories.tsx new file mode 100644 index 0000000000000..c6c7034034318 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/stories/MessageBar/Default.stories.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { Button, Link } from '@fluentui/react-components'; +import { DismissRegular } from '@fluentui/react-icons'; +import { MessageBar, MessageBarActions, MessageBarTitle, MessageBarBody } from '@fluentui/react-message-bar-preview'; + +const intents = ['info', 'warning', 'error', 'success'] as const; + +export const Default = () => ( +
+ {intents.map(intent => ( + + + Descriptive title + Message providing information to the user with actionable insights. Link + + } />}> + + + + + ))} +
+); diff --git a/packages/react-components/react-message-bar-preview/stories/MessageBar/MessageBarBestPractices.md b/packages/react-components/react-message-bar-preview/stories/MessageBar/MessageBarBestPractices.md new file mode 100644 index 0000000000000..08ff8ddeeb5f8 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/stories/MessageBar/MessageBarBestPractices.md @@ -0,0 +1,5 @@ +## Best practices + +### Do + +### Don't diff --git a/packages/react-components/react-message-bar-preview/stories/MessageBar/MessageBarDescription.md b/packages/react-components/react-message-bar-preview/stories/MessageBar/MessageBarDescription.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/react-components/react-message-bar-preview/stories/MessageBar/Multiline.stories.tsx b/packages/react-components/react-message-bar-preview/stories/MessageBar/Multiline.stories.tsx new file mode 100644 index 0000000000000..87901e5318582 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/stories/MessageBar/Multiline.stories.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { Button, Link } from '@fluentui/react-components'; +import { DismissRegular } from '@fluentui/react-icons'; +import { MessageBar, MessageBarActions, MessageBarBody, MessageBarTitle } from '@fluentui/react-message-bar-preview'; + +const intents = ['info', 'warning', 'error', 'success'] as const; + +export const Multiline = () => ( +
+ {intents.map(intent => ( + + + Descriptive title + Message providing information to the user with actionable insights. Link + + } />}> + + + + + ))} +
+); diff --git a/packages/react-components/react-message-bar-preview/stories/MessageBar/index.stories.tsx b/packages/react-components/react-message-bar-preview/stories/MessageBar/index.stories.tsx new file mode 100644 index 0000000000000..e5af5995d3206 --- /dev/null +++ b/packages/react-components/react-message-bar-preview/stories/MessageBar/index.stories.tsx @@ -0,0 +1,19 @@ +import { MessageBar } from '@fluentui/react-message-bar-preview'; + +import descriptionMd from './MessageBarDescription.md'; +import bestPracticesMd from './MessageBarBestPractices.md'; + +export { Default } from './Default.stories'; +export { Multiline } from './Multiline.stories'; + +export default { + title: 'Preview Components/MessageBar', + component: MessageBar, + parameters: { + docs: { + description: { + component: [descriptionMd, bestPracticesMd].join('\n'), + }, + }, + }, +};