diff --git a/packages/ffe-context-message-react/README.md b/packages/ffe-context-message-react/README.md index f0b4255e59..9e29d2ec84 100644 --- a/packages/ffe-context-message-react/README.md +++ b/packages/ffe-context-message-react/README.md @@ -10,11 +10,6 @@ npm install --save @sb1/ffe-context-message-react Full documentation on context message usage is available at https://design.sparebank1.no/komponenter/meldinger/#contextmessage. -## TypeScript definition files - -This component supports TypeScript - please update `index.d.ts` if you change any -of the external methods or properties in this component. - ## Development To start a local development server, run the following from the designsystem root folder: diff --git a/packages/ffe-context-message-react/package.json b/packages/ffe-context-message-react/package.json index 43418c9d24..4e99614f61 100644 --- a/packages/ffe-context-message-react/package.json +++ b/packages/ffe-context-message-react/package.json @@ -21,19 +21,18 @@ "url": "ssh://git@github.com:SpareBank1/designsystem.git" }, "scripts": { - "build": "ffe-buildtool babel", + "build": "ffe-buildtool tsc", "watch": "ffe-buildtool babel-watch", - "lint": "eslint src", - "lint:fix": "eslint src --fix", + "lint": "eslint src --ext ts,tsx", + "lint:fix": "eslint src --fix --ext ts,tsx", "test": "ffe-buildtool jest", "test:watch": "ffe-buildtool jest --watch" }, "dependencies": { + "@sb1/ffe-collapse-react": "^4.0.2", "@sb1/ffe-context-message": "^8.0.17", "@sb1/ffe-icons-react": "^10.0.1", - "classnames": "^2.3.1", - "prop-types": "^15.7.2", - "uuid": "^9.0.0" + "classnames": "^2.3.1" }, "devDependencies": { "@sb1/ffe-buildtool": "^0.6.1", diff --git a/packages/ffe-context-message-react/src/ContextErrorMessage.js b/packages/ffe-context-message-react/src/ContextErrorMessage.tsx similarity index 60% rename from packages/ffe-context-message-react/src/ContextErrorMessage.js rename to packages/ffe-context-message-react/src/ContextErrorMessage.tsx index 74858c91a9..01ece4e02e 100644 --- a/packages/ffe-context-message-react/src/ContextErrorMessage.js +++ b/packages/ffe-context-message-react/src/ContextErrorMessage.tsx @@ -1,16 +1,17 @@ import React from 'react'; -import { number, node, string, bool, oneOf, func, object } from 'prop-types'; -import acceptedLocales from './locale/accepted-locales'; -import texts from './locale/texts'; -import ContextMessage from './ContextMessage'; +import { texts } from './texts'; +import { ContextMessage, ContextMessageProps } from './ContextMessage'; import { ContextMessageIcon } from './ContextMessageIcon'; -const ContextErrorMessage = ({ - animationLengthMs = 300, - compact = false, - onClose = () => {}, - showCloseButton = false, - style = {}, +export interface ContextErrorMessageProps + extends Omit< + ContextMessageProps, + 'role' | 'messageType' | 'aria-label' | 'icon' + > { + alert?: boolean; +} + +export const ContextErrorMessage: React.FC = ({ alert = true, locale = 'nb', ...rest @@ -24,7 +25,6 @@ const ContextErrorMessage = ({ return ( ); }; - -ContextErrorMessage.propTypes = { - animationLengthMs: number, - /** The content shown in the context box */ - children: node.isRequired, - /** Classes are added in addition to the relevant context message classes */ - className: string, - /** Renders a more compact version of the context message */ - compact: bool, - /** ID for the children container */ - contentElementId: string, - header: string, - /** ID for the header container */ - headerElementId: string, - /** Decides the language */ - locale: oneOf(acceptedLocales), - /** Callback for when the context message has been closed (after the animation) */ - onClose: func, - showCloseButton: bool, - /** Styles applied to the outermost element. `height` will be overridden */ - style: object, - /** When false, role is not set to alert, avoids message from being read up immediately after page load. Default value is true. */ - alert: bool, -}; - -export default ContextErrorMessage; diff --git a/packages/ffe-context-message-react/src/ContextInfoMessage.js b/packages/ffe-context-message-react/src/ContextInfoMessage.tsx similarity index 84% rename from packages/ffe-context-message-react/src/ContextInfoMessage.js rename to packages/ffe-context-message-react/src/ContextInfoMessage.tsx index deadfd78da..c0c22e081c 100644 --- a/packages/ffe-context-message-react/src/ContextInfoMessage.js +++ b/packages/ffe-context-message-react/src/ContextInfoMessage.tsx @@ -1,11 +1,18 @@ import React from 'react'; -import ContextMessage from './ContextMessage'; +import { ContextMessage, ContextMessageProps } from './ContextMessage'; import { ContextMessageIcon } from './ContextMessageIcon'; -import texts from './locale/texts'; -import { oneOf } from 'prop-types'; -import acceptedLocales from './locale/accepted-locales'; +import { texts } from './texts'; -const ContextInfoMessage = ({ locale = 'nb', ...rest }) => { +export interface ContextInfoMessageProps + extends Omit< + ContextMessageProps, + 'role' | 'messageType' | 'aria-label' | 'icon' + > {} + +export const ContextInfoMessage: React.FC = ({ + locale = 'nb', + ...rest +}) => { const infoIconSmall = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik00NzkuNzg4LTY3MnEtMjUuOTQyIDAtNDMuODY0LTE4LjEzNS0xNy45MjMtMTguMTM2LTE3LjkyMy00NC4wNzd0MTguMTM1LTQzLjg2NHExOC4xMzUtMTcuOTIzIDQ0LjA3Ni0xNy45MjMgMjUuOTQyIDAgNDMuODY0IDE4LjEzNiAxNy45MjMgMTguMTM1IDE3LjkyMyA0NC4wNzZ0LTE4LjEzNSA0My44NjRRNTA1LjcyOS02NzIgNDc5Ljc4OC02NzJabS4yNTcgNTA3Ljk5OXEtMjAuODE0IDAtMzUuNDI5LTE0LjU4NC0xNC42MTUtMTQuNTgzLTE0LjYxNS0zNS40MTZ2LTI5Ni42MTRxMC0yMC44MzMgMTQuNTctMzUuNDE2IDE0LjU3LTE0LjU4MyAzNS4zODQtMTQuNTgzdDM1LjQyOSAxNC41ODNxMTQuNjE1IDE0LjU4MyAxNC42MTUgMzUuNDE2djI5Ni42MTRxMCAyMC44MzMtMTQuNTcgMzUuNDE2LTE0LjU3IDE0LjU4NC0zNS4zODQgMTQuNTg0WiIvPjwvc3ZnPg=='; @@ -28,10 +35,3 @@ const ContextInfoMessage = ({ locale = 'nb', ...rest }) => { /> ); }; - -ContextInfoMessage.propTypes = { - /** Decides the language */ - locale: oneOf(acceptedLocales), -}; - -export default ContextInfoMessage; diff --git a/packages/ffe-context-message-react/src/ContextMessage.js b/packages/ffe-context-message-react/src/ContextMessage.js deleted file mode 100644 index 8605eafa0a..0000000000 --- a/packages/ffe-context-message-react/src/ContextMessage.js +++ /dev/null @@ -1,156 +0,0 @@ -import React, { useState, useRef, cloneElement } from 'react'; -import { - number, - node, - string, - bool, - element, - oneOf, - func, - object, -} from 'prop-types'; -import classNames from 'classnames'; -import { Icon } from '@sb1/ffe-icons-react'; -import acceptedLocales from './locale/accepted-locales'; -import texts from './locale/texts'; -import { v4 as uuid } from 'uuid'; - -/** - * Base component for all four types of context messages. - * - * *Should never be used directly!* - * Please use one of the four versions exported from this package. - */ -const ContextMessage = ({ - animationLengthMs = 300, - onClose = () => {}, - icon, - headerText, - compact = false, - messageType, - style = {}, - showCloseButton = false, - headerElement = 'h2', - headerElementId, - contentElementId, - children, - className, - locale = 'nb', - onColoredBg, - ...rest -}) => { - const [isClosed, setIsClosed] = useState(); - const container = useRef(null); - const _headerElementId = useRef(headerElementId || uuid()); - const _contentElementId = useRef(contentElementId || uuid()); - const closeIcon = - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik00ODAtNDQyLjg0NyAzMDkuMDc2LTI3MS45MjRxLTguMzA3IDguMzA4LTE3Ljg4NCA4LTkuNTc2LS4zMDctMTguMjY4LTktOC42OTMtOC42OTItOC42OTMtMTguNTc2dDguNjkzLTE4LjU3Nkw0NDIuODQ3LTQ4MCAyNzEuOTI0LTY1MC45MjRxLTguMzA4LTguMzA3LTgtMTguMzg0LjMwNy0xMC4wNzYgOS0xOC43NjggOC42OTItOC42OTMgMTguNTc2LTguNjkzdDE4LjU3NiA4LjY5M0w0ODAtNTE3LjE1M2wxNzAuOTI0LTE3MC45MjNxOC4zMDctOC4zMDggMTguMzg0LTguNSAxMC4wNzYtLjE5MyAxOC43NjggOC41IDguNjkzIDguNjkyIDguNjkzIDE4LjU3NnQtOC42OTMgMTguNTc2TDUxNy4xNTMtNDgwbDE3MC45MjMgMTcwLjkyNHE4LjMwOCA4LjMwNyA4LjUgMTcuODg0LjE5MyA5LjU3Ni04LjUgMTguMjY4LTguNjkyIDguNjkzLTE4LjU3NiA4LjY5M3QtMTguNTc2LTguNjkzTDQ4MC00NDIuODQ3WiIvPjwvc3ZnPg=='; - const handleClose = event => { - container.current.style.height = `${container.current.offsetHeight}px`; - setTimeout(() => { - container.current.style.height = 0; - }, 0); - setTimeout(() => { - setIsClosed(true); - onClose(event); - }, animationLengthMs); - return false; - }; - - if (isClosed) { - return null; - } - - return ( -
-
- {icon && ( -
- {cloneElement(icon, { - className: classNames( - 'ffe-context-message-content__icon-span', - icon.props.className, - ), - weight: 300, - })} -
- )} -
- {headerText && - React.createElement( - headerElement, - { - id: _headerElementId.current, - className: - 'ffe-context-message-content__header', - }, - headerText, - )} -
- {children} -
-
-
- {showCloseButton && ( - - )} -
- ); -}; - -export default ContextMessage; - -ContextMessage.propTypes = { - animationLengthMs: number, - /** The content shown in the context box */ - children: node.isRequired, - /** Classes are added in addition to the relevant context message classes */ - className: string, - /** Renders a more compact version of the context message */ - compact: bool, - /** ID for the children container */ - contentElementId: string, - headerText: string, - /** ID for the header container */ - headerElementId: string, - /* HTML element for the header */ - headerElement: string, - icon: element.isRequired, - /** Decides the language of the aria-label for the close icon */ - locale: oneOf(acceptedLocales), - /** Provided by the wrapper component */ - messageType: oneOf(['info', 'tips', 'success', 'error']), - /** Callback for when the context message has been closed (after the animation) */ - onClose: func, - showCloseButton: bool, - /** Styles applied to the outermost element. `height` will be overridden */ - style: object, - /* Adds alternative styling for better contrast on certain backgrounds */ - onColoredBg: bool, -}; diff --git a/packages/ffe-context-message-react/src/ContextMessage.spec.js b/packages/ffe-context-message-react/src/ContextMessage.spec.js deleted file mode 100644 index e783635041..0000000000 --- a/packages/ffe-context-message-react/src/ContextMessage.spec.js +++ /dev/null @@ -1,213 +0,0 @@ -import React from 'react'; -import { Icon } from '@sb1/ffe-icons-react'; - -import { - ContextErrorMessage, - ContextInfoMessage, - ContextSuccessMessage, - ContextTipsMessage, -} from '.'; - -import ContextMessage from './ContextMessage'; - -const defaultProps = { - children:

content

, - messageType: 'tips', - role: 'group', - icon: , -}; - -const getShallowWrapper = props => - shallow(); -const getMountedWrapper = props => - mount(); - -describe('', () => { - it('renders with provided content', () => { - const wrapper = getMountedWrapper(); - const content = wrapper.find('.ffe-body-text').find('p'); - expect(content.exists()).toBe(true); - expect(content.text()).toBe('content'); - }); - - it('renders with provided header', () => { - const wrapper = getMountedWrapper({ - headerText: 'header text', - }); - const headerComponent = wrapper.find( - '.ffe-context-message-content__header', - ); - expect(headerComponent.exists()).toBe(true); - expect(headerComponent.text()).toBe('header text'); - expect(headerComponent.type()).toBe('h2'); - }); - - it('renders with provided header as given tag', () => { - const wrapper = getMountedWrapper({ - headerText: 'header text', - headerElement: 'h5', - }); - const headerComponent = wrapper.find( - '.ffe-context-message-content__header', - ); - expect(headerComponent.type()).toBe('h5'); - }); - - it('renders without header', () => { - const wrapper = getMountedWrapper(); - const header = wrapper.find('.ffe-context-message-content__header'); - expect(header.exists()).toBe(false); - }); - it('set role="group" on outer container', () => { - const wrapper = getMountedWrapper(); - const container = wrapper.find('.ffe-context-message'); - expect(container.prop('role')).toBe('group'); - }); - - it('renders provided styles to outermost container', () => { - const wrapper = getShallowWrapper({ - style: { - marginTop: '40px', - }, - }); - expect(wrapper.prop('style').marginTop).toBe('40px'); - }); - - it('renders provided className to outermost container', () => { - const wrapper = getShallowWrapper({ - className: 'special special--mod', - }); - expect(wrapper.hasClass('special')).toBe(true); - expect(wrapper.hasClass('special--mod')).toBe(true); - expect(wrapper.hasClass('ffe-context-message')).toBe(true); - }); - - it('renders --compact modifier if compact prop is true', () => { - const wrapper = getShallowWrapper({ - compact: true, - }); - expect(wrapper.hasClass('ffe-context-message--compact')).toBe(true); - }); - - it('renders with context icon', () => { - const wrapper = getMountedWrapper({}); - expect( - wrapper.find('ffe-context-message-content__icon-span--small'), - ).toBeTruthy(); - expect( - wrapper.find('ffe-context-message-content__icon-span--large'), - ).toBeTruthy(); - }); - - it('lets consumer add extra classes to icon', () => { - const wrapper = getMountedWrapper({ - icon: ( - - ), - }); - - const el = wrapper.find(Icon); - - expect(el.hasClass('ffe-context-message-content__icon-span')).toBe( - true, - ); - expect(el.hasClass('extra-extra-read-all-about-it')).toBe(true); - }); - - it('passes unknown props on to the root DOM node', () => { - const wrapper = getMountedWrapper({ - 'data-foo': 'bar', - }); - - const el = wrapper.find('.ffe-context-message'); - - expect(el.prop('data-foo')).toBe('bar'); - }); - - it('renders without close button by default', () => { - const wrapper = getMountedWrapper({ - icon: , - }); - expect( - wrapper.find('.ffe-context-message-content__close-button').exists(), - ).toBe(false); - }); - - it('closes itself on close click', done => { - const onClickSpy = jest.fn(); - const wrapper = getMountedWrapper({ - animationLengthMs: 10, - showCloseButton: true, - onClose: onClickSpy, - }); - expect(wrapper.find('.ffe-context-message').exists()).toBe(true); - wrapper - .find('.ffe-context-message-content__close-button') - .simulate('click'); - - setTimeout(() => { - wrapper.update(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - expect(wrapper.find('.ffe-context-message').exists()).toBe(false); - done(); - }, 20); - }); - - it('does not have aria-labelledby when header was not specified', () => { - const wrapper = getMountedWrapper(); - const message = wrapper.find('.ffe-context-message'); - expect(message.prop('aria-labelledby')).toBe(undefined); - }); -}); - -describe('', () => { - const wrapper = mount( - -

content

-
, - ); - - it('renders ContextInfoMessage', () => { - const component = wrapper.find('.ffe-context-message'); - expect(component.hasClass('ffe-context-message--info')).toBe(true); - }); -}); - -describe('', () => { - const wrapper = mount( - -

content

-
, - ); - - it('renders ContextTipsMessage', () => { - const component = wrapper.find('.ffe-context-message'); - expect(component.hasClass('ffe-context-message--tips')).toBe(true); - }); -}); - -describe('Test ContextSuccessMessage', () => { - const wrapper = mount( - -

content

-
, - ); - - it('renders ContextInfoMessage', () => { - const component = wrapper.find('.ffe-context-message'); - expect(component.hasClass('ffe-context-message--success')).toBe(true); - }); -}); - -describe('', () => { - const wrapper = mount( - -

content

-
, - ); - - it('renders ContextErrorMessage', () => { - const component = wrapper.find('.ffe-context-message'); - expect(component.hasClass('ffe-context-message--error')).toBe(true); - }); -}); diff --git a/packages/ffe-context-message-react/src/ContextMessage.spec.tsx b/packages/ffe-context-message-react/src/ContextMessage.spec.tsx new file mode 100644 index 0000000000..71a6e0980d --- /dev/null +++ b/packages/ffe-context-message-react/src/ContextMessage.spec.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import { Icon } from '@sb1/ffe-icons-react'; +import { ContextErrorMessage } from './ContextErrorMessage'; +import { ContextInfoMessage } from './ContextInfoMessage'; +import { ContextSuccessMessage } from './ContextSuccessMessage'; +import { ContextTipsMessage } from './ContextTipsMessage'; +import { ContextMessage, ContextMessageProps } from './ContextMessage'; +import { render, screen } from '@testing-library/react'; + +const defaultProps = { + children:

content

, + messageType: 'tips' as const, + role: 'group' as const, + icon: , +}; + +const renderContextMessage = (props?: Partial) => + render(); + +describe('', () => { + it('renders with provided content', () => { + const { container } = renderContextMessage(); + expect(container.querySelector('.ffe-body-text p')?.textContent).toBe( + 'content', + ); + }); + + it('renders with provided header', () => { + renderContextMessage({ + headerText: 'header text', + }); + + const headerComponent = screen.getByRole('heading', { + name: 'header text', + }); + expect(headerComponent.tagName).toBe('H2'); + }); + + it('renders with provided header as given tag', () => { + renderContextMessage({ + headerText: 'header text', + headerElement: 'h5', + }); + const headerComponent = screen.getByRole('heading', { + name: 'header text', + }); + expect(headerComponent.tagName).toBe('H5'); + }); + + it('renders without header', () => { + renderContextMessage(); + expect(screen.queryByRole('heading')).not.toBeInTheDocument(); + }); + it('set role="group" on outer container', () => { + renderContextMessage(); + const group = screen.getByRole('group'); + expect(group.classList.contains('ffe-context-message')).toBeTruthy(); + }); + + it('renders provided className to outermost container', () => { + renderContextMessage({ + className: 'special special--mod', + }); + const group = screen.getByRole('group'); + expect(group.classList.contains('special')).toBeTruthy(); + expect(group.classList.contains('special--mod')).toBeTruthy(); + expect(group.classList.contains('ffe-context-message')).toBeTruthy(); + }); + + it('renders --compact modifier if compact prop is true', () => { + renderContextMessage({ + compact: true, + }); + const group = screen.getByRole('group'); + expect( + group.classList.contains('ffe-context-message--compact'), + ).toBeTruthy(); + }); + + it('renders with context icon', () => { + const { container } = renderContextMessage(); + expect( + container.querySelector( + '.ffe-icons.ffe-context-message-content__icon-span', + ), + ).toBeTruthy(); + }); + + it('lets consumer add extra classes to icon', () => { + const { container } = renderContextMessage({ + icon: ( + + ), + }); + expect( + container.querySelector( + '.ffe-icons.ffe-context-message-content__icon-span.extra-extra-read-all-about-it', + ), + ).toBeTruthy(); + }); + + it('passes unknown props on to the root DOM node', () => { + render(); + const group = screen.getByRole('group'); + expect(group.getAttribute('data-foo')).toBe('bar'); + }); + + it('renders without close button by default', () => { + renderContextMessage(); + expect( + screen.queryByRole('button', { name: 'Lukk' }), + ).not.toBeInTheDocument(); + }); + + it('does not have aria-labelledby when header was not specified', () => { + renderContextMessage(); + const group = screen.getByRole('group'); + expect(group.getAttribute('aria-labelledby')).toBeNull(); + }); +}); + +describe('', () => { + it('renders ContextInfoMessage', () => { + render( + +

content

+
, + ); + const group = screen.getByRole('group'); + expect(group.classList.contains('ffe-context-message')).toBeTruthy(); + expect( + group.classList.contains('ffe-context-message--info'), + ).toBeTruthy(); + }); +}); + +describe('', () => { + it('renders ContextTipsMessage', () => { + render( + +

content

+
, + ); + const group = screen.getByRole('group'); + expect(group.classList.contains('ffe-context-message')).toBeTruthy(); + expect( + group.classList.contains('ffe-context-message--tips'), + ).toBeTruthy(); + }); +}); + +describe('Test ContextSuccessMessage', () => { + it('renders ContextInfoMessage', () => { + render( + +

content

+
, + ); + const group = screen.getByRole('group'); + expect(group.classList.contains('ffe-context-message')).toBeTruthy(); + expect( + group.classList.contains('ffe-context-message--success'), + ).toBeTruthy(); + }); +}); + +describe('', () => { + it('renders ContextErrorMessage', () => { + render( + +

content

+
, + ); + const alert = screen.getByRole('alert'); + + expect(alert.classList.contains('ffe-context-message')).toBeTruthy(); + expect( + alert.classList.contains('ffe-context-message--error'), + ).toBeTruthy(); + }); +}); diff --git a/packages/ffe-context-message-react/src/ContextMessage.tsx b/packages/ffe-context-message-react/src/ContextMessage.tsx new file mode 100644 index 0000000000..0290bc26d9 --- /dev/null +++ b/packages/ffe-context-message-react/src/ContextMessage.tsx @@ -0,0 +1,112 @@ +import React, { useState, cloneElement } from 'react'; +import classNames from 'classnames'; +import { Icon } from '@sb1/ffe-icons-react'; +import { texts } from './texts'; +import { Collapse } from '@sb1/ffe-collapse-react'; + +export interface ContextMessageProps + extends React.ComponentPropsWithoutRef<'div'> { + /** Renders a more compact version of the context message */ + compact?: boolean; + /** ID for the children container */ + contentElementId?: string; + headerText?: string; + /** ID for the header container */ + headerElementId?: string; + /** HTML element for the header */ + headerElement?: string; + icon: React.ReactNode; + /** Decides the language of the aria-label for the close icon */ + locale?: 'nb' | 'nn' | 'en'; + /** Provided by the wrapper component */ + messageType: 'info' | 'tips' | 'success' | 'error'; + /** Callback for when the context message has been closed (after the animation) */ + onCloseRest?: () => void; + showCloseButton?: boolean; + /** Adds alternative styling for better contrast on certain backgrounds */ + onColoredBg?: boolean; +} + +/** + * Base component for all four types of context messages. + * + * *Should never be used directly!* + * Please use one of the four versions exported from this package. + */ +export const ContextMessage: React.FC = ({ + icon, + headerText, + compact = false, + messageType, + style = {}, + showCloseButton = false, + headerElement = 'h2', + headerElementId, + contentElementId, + children, + className, + locale = 'nb', + onColoredBg, + onCloseRest, + ...rest +}) => { + const [isOpen, setIsOpen] = useState(true); + const closeIcon = + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik00ODAtNDQyLjg0NyAzMDkuMDc2LTI3MS45MjRxLTguMzA3IDguMzA4LTE3Ljg4NCA4LTkuNTc2LS4zMDctMTguMjY4LTktOC42OTMtOC42OTItOC42OTMtMTguNTc2dDguNjkzLTE4LjU3Nkw0NDIuODQ3LTQ4MCAyNzEuOTI0LTY1MC45MjRxLTguMzA4LTguMzA3LTgtMTguMzg0LjMwNy0xMC4wNzYgOS0xOC43NjggOC42OTItOC42OTMgMTguNTc2LTguNjkzdDE4LjU3NiA4LjY5M0w0ODAtNTE3LjE1M2wxNzAuOTI0LTE3MC45MjNxOC4zMDctOC4zMDggMTguMzg0LTguNSAxMC4wNzYtLjE5MyAxOC43NjggOC41IDguNjkzIDguNjkyIDguNjkzIDE4LjU3NnQtOC42OTMgMTguNTc2TDUxNy4xNTMtNDgwbDE3MC45MjMgMTcwLjkyNHE4LjMwOCA4LjMwNyA4LjUgMTcuODg0LjE5MyA5LjU3Ni04LjUgMTguMjY4LTguNjkyIDguNjkzLTE4LjU3NiA4LjY5M3QtMTguNTc2LTguNjkzTDQ4MC00NDIuODQ3WiIvPjwvc3ZnPg=='; + + return ( + +
+
+ {React.isValidElement(icon) && ( +
+ {cloneElement(icon, { + ...icon.props, + className: classNames( + 'ffe-context-message-content__icon-span', + icon.props.className, + ), + weight: 300, + })} +
+ )} +
+ {headerText && + React.createElement( + headerElement, + { + className: + 'ffe-context-message-content__header', + }, + headerText, + )} +
{children}
+
+
+ {showCloseButton && ( + + )} +
+
+ ); +}; diff --git a/packages/ffe-context-message-react/src/ContextMessageIcon.js b/packages/ffe-context-message-react/src/ContextMessageIcon.tsx similarity index 71% rename from packages/ffe-context-message-react/src/ContextMessageIcon.js rename to packages/ffe-context-message-react/src/ContextMessageIcon.tsx index 2bc2545d46..23fb5f664f 100644 --- a/packages/ffe-context-message-react/src/ContextMessageIcon.js +++ b/packages/ffe-context-message-react/src/ContextMessageIcon.tsx @@ -1,8 +1,10 @@ import React from 'react'; -import { string } from 'prop-types'; import { Icon } from '@sb1/ffe-icons-react'; -export const ContextMessageIcon = ({ smallIconUrl, largeIconUrl }) => { +export const ContextMessageIcon: React.FC<{ + smallIconUrl: string; + largeIconUrl: string; +}> = ({ smallIconUrl, largeIconUrl }) => { return ( <> { ); }; - -ContextMessageIcon.propTypes = { - smallIconUrl: string.isRequired, - largeIconUrl: string.isRequired, -}; diff --git a/packages/ffe-context-message-react/src/ContextSuccessMessage.js b/packages/ffe-context-message-react/src/ContextSuccessMessage.tsx similarity index 81% rename from packages/ffe-context-message-react/src/ContextSuccessMessage.js rename to packages/ffe-context-message-react/src/ContextSuccessMessage.tsx index fff27ede84..419322bc37 100644 --- a/packages/ffe-context-message-react/src/ContextSuccessMessage.js +++ b/packages/ffe-context-message-react/src/ContextSuccessMessage.tsx @@ -1,11 +1,18 @@ import React from 'react'; -import texts from './locale/texts'; -import ContextMessage from './ContextMessage'; +import { texts } from './texts'; +import { ContextMessage, ContextMessageProps } from './ContextMessage'; import { ContextMessageIcon } from './ContextMessageIcon'; -import { oneOf } from 'prop-types'; -import acceptedLocales from './locale/accepted-locales'; -const ContextSuccessMessage = ({ locale = 'nb', ...rest }) => { +export interface ContextSuccessMessageProps + extends Omit< + ContextMessageProps, + 'role' | 'messageType' | 'aria-label' | 'icon' + > {} + +export const ContextSuccessMessage: React.FC = ({ + locale = 'nb', + ...rest +}) => { const checkIconSmall = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Im0zOTUtMzcyLjM4NCAyNzAuNTM5LTI2OS41MzhxNy45MjMtNy45MjMgMTcuNjkyLTcuNjE2IDkuNzY5LjMwOCAxOC4wNzYgOC42MTYgOC4zMDggOC4zMDcgOC4zMDggMTcuODg0IDAgOS41NzYtOC4zMDggMTcuODg0bC0yODMgMjgyLjk5OXEtOS44NDYgOS44NDYtMjIuODA3IDkuODQ2LTEyLjk2MSAwLTIyLjgwNy05Ljg0NmwtMTE0LTExMy45OTlxLTcuOTIzLTcuOTIzLTguMzA4LTE3LjY5Mi0uMzg0LTkuNzY5IDcuOTIzLTE4LjA3NiA4LjMwOC04LjMwOCAxNy44ODQtOC4zMDggOS41NzcgMCAxNy44ODQgOC4zMDhMMzk1LTM3Mi4zODRaIi8+PC9zdmc+'; @@ -28,10 +35,3 @@ const ContextSuccessMessage = ({ locale = 'nb', ...rest }) => { /> ); }; - -ContextSuccessMessage.propTypes = { - /** Decides the language */ - locale: oneOf(acceptedLocales), -}; - -export default ContextSuccessMessage; diff --git a/packages/ffe-context-message-react/src/ContextTipsMessage.js b/packages/ffe-context-message-react/src/ContextTipsMessage.tsx similarity index 88% rename from packages/ffe-context-message-react/src/ContextTipsMessage.js rename to packages/ffe-context-message-react/src/ContextTipsMessage.tsx index 21cfc039ee..4c89dca90c 100644 --- a/packages/ffe-context-message-react/src/ContextTipsMessage.js +++ b/packages/ffe-context-message-react/src/ContextTipsMessage.tsx @@ -1,11 +1,18 @@ import React from 'react'; -import ContextMessage from './ContextMessage'; +import { ContextMessage, ContextMessageProps } from './ContextMessage'; import { ContextMessageIcon } from './ContextMessageIcon'; -import texts from './locale/texts'; -import { oneOf } from 'prop-types'; -import acceptedLocales from './locale/accepted-locales'; +import { texts } from './texts'; -const ContextTipsMessage = ({ locale = 'nb', ...rest }) => { +export interface ContextTipsMessageProps + extends Omit< + ContextMessageProps, + 'role' | 'messageType' | 'aria-label' | 'icon' + > {} + +export const ContextTipsMessage: React.FC = ({ + locale = 'nb', + ...rest +}) => { const lightbulbIconSmall = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik00NzkuNzg4LTExMi45MjRxLTI3LjA5NSAwLTQ2LjU1Ny0xOC42NS0xOS40NjEtMTguNjUtMjEuMzg1LTQ1LjY1N2gxMzYuMzA4cS0xLjkyNCAyNy4zMDctMjEuNTk3IDQ1LjgwNy0xOS42NzQgMTguNS00Ni43NjkgMTguNVpNMzcyLjI4My0yNDAuNjE3cS0xMS4wNTIgMC0xOC42NjctNy40MDUtNy42MTUtNy40MDQtNy42MTUtMTguMzg0IDAtMTAuOTc5IDcuNDUzLTE4LjU5NSA3LjQ1NC03LjYxNSAxOC41MDYtNy42MTVoMjE1Ljc1N3ExMS4wNTIgMCAxOC42NjcgNy40MDUgNy42MTUgNy40MDUgNy42MTUgMTguMzg0dC03LjQ1MyAxOC41OTVxLTcuNDU0IDcuNjE1LTE4LjUwNiA3LjYxNUgzNzIuMjgzWm0tNDUuMTI5LTExNS4zODRxLTU1Ljg0NS0zNi4wNzctODUuNDk5LTk0LjYxNVEyMTIuMDAxLTUwOS4xNTQgMjEyLjAwMS01NzZxMC0xMTEuOTIyIDc4LjAzOC0xODkuOTYxUTM2OC4wNzgtODQzLjk5OSA0ODAtODQzLjk5OXExMTEuOTIyIDAgMTg5Ljk2MSA3OC4wMzhRNzQ3Ljk5OS02ODcuOTIyIDc0Ny45OTktNTc2cTAgNjYuODQ2LTI5LjY1NCAxMjUuMzg0dC04NS40OTkgOTQuNjE1SDMyNy4xNTRaTTM0NC00MDhoMjcycTM4LTMxIDU5LTc1dDIxLTkzcTAtOTAuMzI3LTYyLjc2OS0xNTMuMTY0UTU3MC40NjItNzkyIDQ4MC4yMzEtNzkyVDMyNy03MjkuMTY0UTI2NC02NjYuMzI3IDI2NC01NzZxMCA0OSAyMSA5M3Q1OSA3NVptMTM2IDBaIi8+PC9zdmc+'; @@ -28,10 +35,3 @@ const ContextTipsMessage = ({ locale = 'nb', ...rest }) => { /> ); }; - -ContextTipsMessage.propTypes = { - /** Decides the language */ - locale: oneOf(acceptedLocales), -}; - -export default ContextTipsMessage; diff --git a/packages/ffe-context-message-react/src/index.d.ts b/packages/ffe-context-message-react/src/index.d.ts deleted file mode 100644 index d1eb320d82..0000000000 --- a/packages/ffe-context-message-react/src/index.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; - -export interface ContextMessageProps extends React.ComponentProps<'div'> { - animationLengthMs?: number; - children: React.ReactNode; - className?: string; - compact?: boolean; - contentElementId?: string; - headerText?: string; - headerElementId?: string; - headerElement?: string; - locale?: 'nb' | 'nn' | 'en'; - onClose?: (event: React.MouseEvent) => void; - showCloseButton?: boolean; - style?: React.CSSProperties; - onColoredBg?: boolean; -} - -export interface ContextErrorMessageProps extends ContextMessageProps { - alert?: boolean; -} - -declare class ContextInfoMessage extends React.Component< - ContextMessageProps, - any -> {} - -declare class ContextTipsMessage extends React.Component< - ContextMessageProps, - any -> {} - -declare class ContextSuccessMessage extends React.Component< - ContextMessageProps, - any -> {} - -declare class ContextErrorMessage extends React.Component< - ContextErrorMessageProps, - any -> {} diff --git a/packages/ffe-context-message-react/src/index.js b/packages/ffe-context-message-react/src/index.js deleted file mode 100644 index f9533db6e2..0000000000 --- a/packages/ffe-context-message-react/src/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import ContextInfoMessage from './ContextInfoMessage'; -import ContextTipsMessage from './ContextTipsMessage'; -import ContextSuccessMessage from './ContextSuccessMessage'; -import ContextErrorMessage from './ContextErrorMessage'; - -export { - ContextInfoMessage, - ContextTipsMessage, - ContextSuccessMessage, - ContextErrorMessage, -}; diff --git a/packages/ffe-context-message-react/src/index.ts b/packages/ffe-context-message-react/src/index.ts new file mode 100644 index 0000000000..5a67bda166 --- /dev/null +++ b/packages/ffe-context-message-react/src/index.ts @@ -0,0 +1,16 @@ +export { + ContextInfoMessage, + ContextInfoMessageProps, +} from './ContextInfoMessage'; +export { + ContextTipsMessage, + ContextTipsMessageProps, +} from './ContextTipsMessage'; +export { + ContextSuccessMessage, + ContextSuccessMessageProps, +} from './ContextSuccessMessage'; +export { + ContextErrorMessage, + ContextErrorMessageProps, +} from './ContextErrorMessage'; diff --git a/packages/ffe-context-message-react/src/locale/accepted-locales.js b/packages/ffe-context-message-react/src/locale/accepted-locales.js deleted file mode 100644 index 432a862ec4..0000000000 --- a/packages/ffe-context-message-react/src/locale/accepted-locales.js +++ /dev/null @@ -1 +0,0 @@ -export default ['nb', 'nn', 'en']; diff --git a/packages/ffe-context-message-react/src/locale/texts.js b/packages/ffe-context-message-react/src/texts.ts similarity index 91% rename from packages/ffe-context-message-react/src/locale/texts.js rename to packages/ffe-context-message-react/src/texts.ts index 63c318bf64..d34b4318f8 100644 --- a/packages/ffe-context-message-react/src/locale/texts.js +++ b/packages/ffe-context-message-react/src/texts.ts @@ -12,7 +12,7 @@ const nb = { tip: { ariaLabel: 'Tipsmelding', }, -}; +} as const; const nn = { close: 'Lukk', error: { @@ -27,7 +27,7 @@ const nn = { tip: { ariaLabel: 'Tipsmelding', }, -}; +} as const; const en = { close: 'Close', error: { @@ -42,6 +42,6 @@ const en = { tip: { ariaLabel: 'Tip message', }, -}; +} as const; -export default { nb, nn, en }; +export const texts = { nb, nn, en }; diff --git a/packages/ffe-context-message-react/tsconfig.cjs.json b/packages/ffe-context-message-react/tsconfig.cjs.json new file mode 100644 index 0000000000..6579fd2246 --- /dev/null +++ b/packages/ffe-context-message-react/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib", + "module": "commonjs" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "src/**/*.spec.ts*"] +} diff --git a/packages/ffe-context-message-react/tsconfig.esm.json b/packages/ffe-context-message-react/tsconfig.esm.json new file mode 100644 index 0000000000..8e577796bf --- /dev/null +++ b/packages/ffe-context-message-react/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./es", + "module": "esnext" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "src/**/*.spec.ts*"] +} diff --git a/packages/ffe-context-message-react/tsconfig.types.json b/packages/ffe-context-message-react/tsconfig.types.json new file mode 100644 index 0000000000..3499c0be03 --- /dev/null +++ b/packages/ffe-context-message-react/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./types", + "declaration": true, + "emitDeclarationOnly": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "src/**/*.spec.ts*"] +}