diff --git a/i18n/keys.json b/i18n/keys.json index 77f3a35c..8eac17c0 100644 --- a/i18n/keys.json +++ b/i18n/keys.json @@ -1,4 +1,8 @@ { + "inline.message.dismiss": { + "defaultMessage": "Dismiss", + "description": "ARIA-label for the dismiss button at the top of the Modal." + }, "loading": { "defaultMessage": "Loading...", "description": "aria-label text, to indicate that there is a spinner rotating in this place" @@ -23,6 +27,21 @@ "defaultMessage": "SonarQube Server", "description": "Alternative text for the SonarQube Server logo" }, + "message.prefix.danger": { + "defaultMessage": "Error:" + }, + "message.prefix.discover": { + "defaultMessage": "Hint:" + }, + "message.prefix.info": { + "defaultMessage": "Information:" + }, + "message.prefix.success": { + "defaultMessage": "Success:" + }, + "message.prefix.warning": { + "defaultMessage": "Warning:" + }, "modal.close": { "defaultMessage": "Close", "description": "ARIA-label for the close button at the top of the Modal." diff --git a/src/components/index.ts b/src/components/index.ts index 21bf361d..a63d5313 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -25,6 +25,7 @@ export * from './echoes-provider'; export * from './icons'; export * from './links'; export * from './logos'; +export * from './messages'; export * from './modals'; export * from './popover'; export * from './radio-button-group'; diff --git a/src/components/messages/MessageCallout.tsx b/src/components/messages/MessageCallout.tsx new file mode 100644 index 00000000..a63e0643 --- /dev/null +++ b/src/components/messages/MessageCallout.tsx @@ -0,0 +1,85 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { forwardRef, ReactNode, useMemo } from 'react'; +import { useIntl } from 'react-intl'; +import { isDefined } from '~common/helpers/types'; +import { MessageDismissButton } from './MessageDismissButton'; +import { MessageScreenReaderPrefix } from './MessageScreenReaderPrefix'; +import { + MESSAGE_CALLOUT_TYPE_STYLE, + MESSAGE_TYPE_ICON, + MessageCalloutContainer, + MessageCalloutFooter, + MessageCalloutIconWrapper, + MessageCalloutMainContent, + MessageCalloutTextWrapper, + MessageCalloutTitleWrapper, +} from './MessageStyles'; +import { MessageType } from './MessageTypes'; + +export interface MessageProps { + action?: ReactNode; + className?: string; + onDismiss?: VoidFunction; + screenReaderPrefix?: string; + text: ReactNode; + title?: string; + type: MessageType; +} + +export const MessageCallout = forwardRef((props, ref) => { + const { action, className, onDismiss, screenReaderPrefix, text, title, type, ...radixProps } = + props; + const isDismissable = isDefined(onDismiss); + + const intl = useIntl(); + + return ( + MESSAGE_CALLOUT_TYPE_STYLE[type], [type])} + ref={ref} + {...radixProps}> + + + {MESSAGE_TYPE_ICON[type]} + + + + {isDefined(title) && {title}} +
{text}
+
+ + {isDismissable && ( + + )} +
+ {isDefined(action) && {action}} +
+ ); +}); +MessageCallout.displayName = 'MessageCallout'; diff --git a/src/components/messages/MessageDismissButton.tsx b/src/components/messages/MessageDismissButton.tsx new file mode 100644 index 00000000..43218064 --- /dev/null +++ b/src/components/messages/MessageDismissButton.tsx @@ -0,0 +1,64 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import styled from '@emotion/styled'; +import { forwardRef } from 'react'; +import { useButtonClickHandler } from '../buttons/Button'; +import { ButtonStyled } from '../buttons/ButtonStyles'; +import { ButtonCommonProps } from '../buttons/ButtonTypes'; +import { IconX } from '../icons'; +import { Tooltip } from '../tooltip'; + +interface MessageDismissButtonProps extends Pick { + ariaLabel: string; +} + +export const MessageDismissButton = forwardRef( + (props, ref) => { + const { ariaLabel, onClick, ...htmlProps } = props; + + const handleClick = useButtonClickHandler(props); + + return ( + + + + + + ); + }, +); +MessageDismissButton.displayName = 'MessageDismissButton'; + +const MessageDismissButtonStyled = styled(ButtonStyled)` + flex: 0 0 auto; + + height: var(--echoes-dimension-height-600); + width: var(--echoes-dimension-width-300); + + justify-content: center; + + background-color: var(--echoes-color-background-transparent); + border-radius: var(--echoes-border-radius-200); +`; diff --git a/src/components/messages/MessageInline.tsx b/src/components/messages/MessageInline.tsx new file mode 100644 index 00000000..b554b187 --- /dev/null +++ b/src/components/messages/MessageInline.tsx @@ -0,0 +1,84 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import styled from '@emotion/styled'; +import { forwardRef, PropsWithChildren, useMemo } from 'react'; +import { MessageScreenReaderPrefix } from './MessageScreenReaderPrefix'; +import { MESSAGE_TYPE_ICON } from './MessageStyles'; +import { MessageInlineSize, MessageType } from './MessageTypes'; + +interface Props { + className?: string; + screenReaderPrefix?: string; + size?: MessageInlineSize; + type: MessageType; +} + +export const MessageInline = forwardRef>((props, ref) => { + const { children, className, screenReaderPrefix, size, type, ...radixProps } = props; + return ( + MESSAGE_INLINE_TYPE_STYLE[type], [type])} + ref={ref}> + {MESSAGE_TYPE_ICON[type]} + + + {children} + + + ); +}); +MessageInline.displayName = 'MessageInline'; + +const MESSAGE_INLINE_TYPE_STYLE = { + [MessageType.Info]: { + '--message-text-color': 'var(--echoes-color-text-info)', + }, + [MessageType.Danger]: { + '--message-text-color': 'var(--echoes-color-text-danger)', + }, + [MessageType.Warning]: { + '--message-text-color': 'var(--echoes-color-text-warning)', + }, + [MessageType.Success]: { + '--message-text-color': 'var(--echoes-color-text-success)', + }, + [MessageType.Discover]: { + '--message-text-color': 'var(--echoes-color-text-accent)', + }, +}; + +const MESSAGE_INLINE_FONT = { + [MessageInlineSize.Small]: 'var(--echoes-typography-text-small-medium)', + [MessageInlineSize.Default]: 'var(--echoes-typography-text-default-regular)', +}; + +const MessageInlineContainer = styled.span<{ size?: MessageInlineSize }>` + ${({ size }) => (size ? `font: ${MESSAGE_INLINE_FONT[size]};` : '')} +`; +MessageInlineContainer.displayName = 'MessageInlineContainer'; + +const MessageInlineTextWrapper = styled.span` + padding-left: var(--echoes-dimension-space-50); + color: var(--message-text-color); +`; +MessageInlineTextWrapper.displayName = 'MessageInlineTextWrapper'; diff --git a/src/components/messages/MessageScreenReaderPrefix.tsx b/src/components/messages/MessageScreenReaderPrefix.tsx new file mode 100644 index 00000000..457ac08b --- /dev/null +++ b/src/components/messages/MessageScreenReaderPrefix.tsx @@ -0,0 +1,76 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import styled from '@emotion/styled'; +import { forwardRef, useMemo } from 'react'; +import { useIntl } from 'react-intl'; +import { screenReaderOnly } from '~common/helpers/styles'; +import { MessageType } from './MessageTypes'; + +interface Props { + screenReaderPrefix?: string; + type: MessageType; +} + +export const MessageScreenReaderPrefix = forwardRef((props, ref) => { + const { screenReaderPrefix, type, ...radixProps } = props; + return ( + + {screenReaderPrefix ?? } + + ); +}); +MessageScreenReaderPrefix.displayName = 'MessageScreenReaderPrefix'; + +function MessagePrefix({ type }: { type: MessageType }) { + const intl = useIntl(); + + const messages: { [type in MessageType]: string } = useMemo( + () => ({ + [MessageType.Info]: intl.formatMessage({ + id: 'message.prefix.info', + defaultMessage: 'Information:', + }), + [MessageType.Danger]: intl.formatMessage({ + id: 'message.prefix.danger', + defaultMessage: 'Error:', + }), + [MessageType.Warning]: intl.formatMessage({ + id: 'message.prefix.warning', + defaultMessage: 'Warning:', + }), + [MessageType.Discover]: intl.formatMessage({ + id: 'message.prefix.discover', + defaultMessage: 'Hint:', + }), + [MessageType.Success]: intl.formatMessage({ + id: 'message.prefix.success', + defaultMessage: 'Success:', + }), + }), + [intl], + ); + + return messages[type]; +} + +const ScreenReaderPrefix = styled.span` + ${screenReaderOnly} +`; +ScreenReaderPrefix.displayName = 'ScreenReaderPrefix'; diff --git a/src/components/messages/MessageStyles.tsx b/src/components/messages/MessageStyles.tsx new file mode 100644 index 00000000..d0a23273 --- /dev/null +++ b/src/components/messages/MessageStyles.tsx @@ -0,0 +1,105 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import styled from '@emotion/styled'; +import { IconCheckCircle, IconError, IconInfo, IconQuestionMark, IconWarning } from '../icons'; +import { MessageType } from './MessageTypes'; + +export const MESSAGE_TYPE_ICON = { + [MessageType.Info]: , + [MessageType.Danger]: , + [MessageType.Warning]: , + [MessageType.Success]: , + [MessageType.Discover]: , +}; + +export const MESSAGE_CALLOUT_TYPE_STYLE = { + [MessageType.Info]: { + '--message-background': 'var(--echoes-color-background-info-weak)', + '--message-title-color': 'var(--echoes-color-text-info-bold)', + }, + [MessageType.Danger]: { + '--message-background': 'var(--echoes-color-background-danger-weak-default)', + '--message-title-color': 'var(--echoes-color-text-danger-bold)', + }, + [MessageType.Warning]: { + '--message-background': 'var(--echoes-color-background-warning-weak)', + '--message-title-color': 'var(--echoes-color-text-warning-bold)', + }, + [MessageType.Success]: { + '--message-background': 'var(--echoes-color-background-success-weak)', + '--message-title-color': 'var(--echoes-color-text-success-bold)', + }, + [MessageType.Discover]: { + '--message-background': 'var(--echoes-color-background-accent-weak-default)', + '--message-title-color': 'var(--echoes-color-text-accent-bold)', + }, +}; + +export const MessageCalloutContainer = styled.div` + display: flex; + flex-direction: column; + gap: var(--echoes-dimension-space-100); + border-radius: var(--echoes-border-radius-400); + + color: var(--echoes-color-text-default); + + background-color: var(--message-background); + padding: var(--echoes-dimension-space-200); +`; +MessageCalloutContainer.displayName = 'MessageCalloutContainer'; + +export const MessageCalloutMainContent = styled.div` + display: flex; + flex-direction: row; + gap: var(--echoes-dimension-space-100); + align-items: start; +`; +MessageCalloutMainContent.displayName = 'MessageCalloutMainContent'; + +export const MessageCalloutIconWrapper = styled.div<{ addMargin: boolean }>` + flex: 0 0 auto; + + font: var(--echoes-typography-text-default-regular); + ${({ addMargin: offset }) => (offset ? 'margin-top: var(--echoes-dimension-space-25);' : '')} +`; +MessageCalloutIconWrapper.displayName = 'MessageCalloutIconWrapper'; + +export const MessageCalloutTitleWrapper = styled.div` + flex: 1 0 auto; + font: var(--echoes-typography-heading-medium); + color: var(--message-title-color); +`; +MessageCalloutTitleWrapper.displayName = 'MessageCalloutTitleWrapper'; + +export const MessageCalloutTextWrapper = styled.div` + flex: 1 0 auto; + + display: flex; + flex-direction: column; + gap: var(--echoes-dimension-space-100); + + font: var(--echoes-typography-text-default-regular); +`; +MessageCalloutTextWrapper.displayName = 'MessageCalloutTextWrapper'; + +export const MessageCalloutFooter = styled.div` + margin-left: var(--echoes-dimension-space-200); +`; +MessageCalloutFooter.displayName = 'MessageCalloutFooter'; diff --git a/src/components/messages/MessageTypes.tsx b/src/components/messages/MessageTypes.tsx new file mode 100644 index 00000000..1af88eef --- /dev/null +++ b/src/components/messages/MessageTypes.tsx @@ -0,0 +1,32 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +export enum MessageType { + Info = 'info', + Danger = 'danger', + Warning = 'warning', + Success = 'success', + Discover = 'discover', +} + +export enum MessageInlineSize { + Small = 'small', + Default = 'default', +} diff --git a/src/components/messages/__tests__/MessageCallout-test.tsx b/src/components/messages/__tests__/MessageCallout-test.tsx new file mode 100644 index 00000000..8e0062b7 --- /dev/null +++ b/src/components/messages/__tests__/MessageCallout-test.tsx @@ -0,0 +1,92 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { screen } from '@testing-library/react'; +import { ComponentProps } from 'react'; +import { render } from '~common/helpers/test-utils'; +import { Button } from '../../buttons'; +import { Tooltip } from '../../tooltip'; +import { MessageCallout } from '../MessageCallout'; +import { MessageType } from '../MessageTypes'; + +it('should display a message', async () => { + const { container } = setupMessageCallout({ text: 'Fancy Content' }); + + expect(screen.getByText('Fancy Content')).toBeInTheDocument(); + await expect(container).toHaveNoA11yViolations(); +}); + +it.each([ + [MessageType.Danger, 'Error:'], + [MessageType.Discover, 'Hint:'], + [MessageType.Info, 'Information:'], + [MessageType.Success, 'Success:'], + [MessageType.Warning, 'Warning:'], +])('should indicate the message is of type %s', (type, expectedPrefix) => { + setupMessageCallout({ type }); + + expect(screen.getByText(expectedPrefix)).toBeInTheDocument(); +}); + +it('should display a title', async () => { + const { container } = setupMessageCallout({ title: 'Fancy Title' }); + + expect(screen.getByText('Fancy Title')).toBeInTheDocument(); + await expect(container).toHaveNoA11yViolations(); +}); + +it('should display an action', async () => { + const { container } = setupMessageCallout({ + action: , + }); + + expect(screen.getByRole('button', { name: 'Nice button' })).toBeInTheDocument(); + await expect(container).toHaveNoA11yViolations(); +}); + +it('should be dismissable', async () => { + const onDismiss = jest.fn(); + const { container, user } = setupMessageCallout({ + onDismiss, + }); + + await expect(container).toHaveNoA11yViolations(); + + const dismissButton = screen.getByRole('button', { name: 'Dismiss' }); + expect(dismissButton).toBeInTheDocument(); + await user.click(dismissButton); + + expect(onDismiss).toHaveBeenCalled(); +}); + +it('should correclty support tooltips', async () => { + const { user } = render( + + + , + ); + + await user.hover(screen.getByText('I got a tooltip')); + expect(screen.getByRole('tooltip', { name: 'my tooltip' })).toBeInTheDocument(); +}); + +function setupMessageCallout(props: Partial>) { + return render(); +} diff --git a/src/components/messages/__tests__/MessageInline-test.tsx b/src/components/messages/__tests__/MessageInline-test.tsx new file mode 100644 index 00000000..0cea30c6 --- /dev/null +++ b/src/components/messages/__tests__/MessageInline-test.tsx @@ -0,0 +1,67 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { screen } from '@testing-library/react'; +import { ComponentProps } from 'react'; +import { render } from '~common/helpers/test-utils'; +import { Tooltip } from '../../tooltip'; +import { MessageInline } from '../MessageInline'; +import { MessageType } from '../MessageTypes'; + +it('should show a message', async () => { + const { container } = setupMessageInline({ children: 'Fancy Content' }); + + expect(screen.getByText('Fancy Content')).toBeInTheDocument(); + await expect(container).toHaveNoA11yViolations(); +}); + +it.each([ + [MessageType.Danger, 'Error:'], + [MessageType.Discover, 'Hint:'], + [MessageType.Info, 'Information:'], + [MessageType.Success, 'Success:'], + [MessageType.Warning, 'Warning:'], +])('should indicate the message is of type %s', (type, expectedPrefix) => { + setupMessageInline({ type }); + + expect(screen.getByText(expectedPrefix)).toBeInTheDocument(); +}); + +it('should correclty support tooltips', async () => { + const { user } = render( + + Look at my tooltip + , + ); + + await user.hover(screen.getByText('Look at my tooltip')); + expect(screen.getByRole('tooltip', { name: 'my tooltip' })).toBeInTheDocument(); +}); + +function setupMessageInline({ + children = 'text', + ...props +}: Partial>) { + return render( + + {children} + , + ); +} diff --git a/src/components/messages/index.ts b/src/components/messages/index.ts new file mode 100644 index 00000000..ff74a011 --- /dev/null +++ b/src/components/messages/index.ts @@ -0,0 +1,23 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +export { MessageCallout } from './MessageCallout'; +export { MessageInline } from './MessageInline'; +export { MessageInlineSize, MessageType } from './MessageTypes'; diff --git a/stories/Icons-stories.tsx b/stories/Icons-stories.tsx index f1942ccf..e633541c 100644 --- a/stories/Icons-stories.tsx +++ b/stories/Icons-stories.tsx @@ -132,16 +132,16 @@ const IconTile = styled.div` export const AutoSizing: StoryObj = { render: () => (
-

+

Warning warning!

-

+

Pinned!

-

+

This is pretty cool , innit?

-

+

Tiny text

diff --git a/stories/message/MessageCallout-stories.tsx b/stories/message/MessageCallout-stories.tsx new file mode 100644 index 00000000..51cc7cc5 --- /dev/null +++ b/stories/message/MessageCallout-stories.tsx @@ -0,0 +1,99 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* eslint-disable no-console */ +import type { Meta, StoryObj } from '@storybook/react'; +import { useCallback, useState } from 'react'; +import { Button, ButtonVariety, MessageCallout, MessageType } from '../../src'; +import { basicWrapperDecorator } from '../helpers/BasicWrapper'; + +const meta: Meta = { + component: MessageCallout, + title: 'Echoes/Messages/MessageCallout', + argTypes: { + type: { control: { type: 'select' }, options: Object.values(MessageType) }, + }, + parameters: { + controls: { exclude: ['onDismiss'] }, + }, + decorators: [basicWrapperDecorator], +}; + +export default meta; + +export type Story = StoryObj; + +export const Default: Story = { + args: { + type: MessageType.Info, + text: 'This is a MessageCallout!', + }, +}; + +export const Dismissable: Story = { + args: { + type: MessageType.Info, + text: 'This is a dismissable MessageCallout', + }, + render: (args) => { + return ; + }, +}; + +export const DismissableWithAction: Story = { + args: { + action: ( + + ), + type: MessageType.Info, + title: 'Hello!', + text: 'This is a MessageCallout!', + }, + render: (args) => { + return ; + }, +}; + +function DismissingContainer(args: Story['args']) { + const [show, setShow] = useState(true); + + const dismiss = useCallback(() => { + setShow(false); + }, [setShow]); + + const toggle = useCallback(() => { + setShow((v) => !v); + }, [setShow]); + + return ( + <> + + {show && ( + + )} + + ); +} diff --git a/stories/message/MessageInline-stories.tsx b/stories/message/MessageInline-stories.tsx new file mode 100644 index 00000000..177fb09d --- /dev/null +++ b/stories/message/MessageInline-stories.tsx @@ -0,0 +1,60 @@ +/* + * Echoes React + * Copyright (C) 2023-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* eslint-disable no-console */ +import type { Meta, StoryObj } from '@storybook/react'; +import { MessageInline, MessageInlineSize, MessageType } from '../../src'; +import { basicWrapperDecorator } from '../helpers/BasicWrapper'; + +const meta: Meta = { + component: MessageInline, + title: 'Echoes/Messages/MessageInline', + argTypes: { + size: { control: { type: 'select' }, options: Object.values(MessageInlineSize) }, + type: { control: { type: 'select' }, options: Object.values(MessageType) }, + }, + decorators: [basicWrapperDecorator], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Hi there', + type: MessageType.Info, + }, +}; + +export const InAParagraph: Story = { + args: { + children: "Yes, that's me we're talking about! Look how beautifully I flow within this text.", + type: MessageType.Info, + }, + render: ({ size, ...args }) => { + return ( +

+ Hi, this is a paragraph of text to showcase the fact that the following{' '} + should flow flawlessly! +

+ ); + }, +};