diff --git a/packages/ffe-modals-react/package.json b/packages/ffe-modals-react/package.json index 340ca83a30..e3db7d55bd 100644 --- a/packages/ffe-modals-react/package.json +++ b/packages/ffe-modals-react/package.json @@ -25,6 +25,7 @@ "test:watch": "ffe-buildtool jest --watch" }, "dependencies": { + "@sb1/ffe-icons-react": "^10.0.5", "@sb1/ffe-modals": "^0.1.3" }, "devDependencies": { diff --git a/packages/ffe-modals-react/src/CloseButton.tsx b/packages/ffe-modals-react/src/CloseButton.tsx new file mode 100644 index 0000000000..9ad1097695 --- /dev/null +++ b/packages/ffe-modals-react/src/CloseButton.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import classnames from 'classnames'; +import { Icon } from '@sb1/ffe-icons-react'; +import { Locale } from './types'; +import { txt } from './texts'; + +const closeIcon = + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik00ODAtNDM3Ljg0NyAyNzcuMDc2LTIzNC45MjRxLTguMzA3IDguMzA4LTIwLjg4NCA4LjUtMTIuNTc2LjE5My0yMS4yNjgtOC41LTguNjkzLTguNjkyLTguNjkzLTIxLjA3NnQ4LjY5My0yMS4wNzZMNDM3Ljg0Ny00ODAgMjM0LjkyNC02ODIuOTI0cS04LjMwOC04LjMwNy04LjUtMjAuODg0LS4xOTMtMTIuNTc2IDguNS0yMS4yNjggOC42OTItOC42OTMgMjEuMDc2LTguNjkzdDIxLjA3NiA4LjY5M0w0ODAtNTIyLjE1M2wyMDIuOTI0LTIwMi45MjNxOC4zMDctOC4zMDggMjAuODg0LTguNSAxMi41NzYtLjE5MyAyMS4yNjggOC41IDguNjkzIDguNjkyIDguNjkzIDIxLjA3NnQtOC42OTMgMjEuMDc2TDUyMi4xNTMtNDgwbDIwMi45MjMgMjAyLjkyNHE4LjMwOCA4LjMwNyA4LjUgMjAuODg0LjE5MyAxMi41NzYtOC41IDIxLjI2OC04LjY5MiA4LjY5My0yMS4wNzYgOC42OTN0LTIxLjA3Ni04LjY5M0w0ODAtNDM3Ljg0N1oiLz48L3N2Zz4='; + +interface CloseButtonProps + extends Omit, 'type'> { + locale: Locale; +} + +export const CloseButton: React.FC = ({ + className, + locale, + ...rest +}) => { + return ( + + ); +}; diff --git a/packages/ffe-modals-react/src/Modal.spec.tsx b/packages/ffe-modals-react/src/Modal.spec.tsx new file mode 100644 index 0000000000..c87c64bb32 --- /dev/null +++ b/packages/ffe-modals-react/src/Modal.spec.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Modal } from './Modal'; +import { render, screen } from '@testing-library/react'; + +describe('', () => { + it('should render with classes', () => { + render(); + + const modal = screen.getByRole('dialog', { hidden: true }); + + expect(modal.classList.contains('ffe-modal')).toBeTruthy(); + expect(modal.classList.contains('custom-class')).toBeTruthy(); + }); + + it('should have close button', () => { + render(); + + const closeButton = screen.getByRole('button', { + hidden: true, + name: 'Lukk', + }); + + expect(closeButton).toBeInTheDocument(); + }); + + it('should set up heading', () => { + const headingId = 'heading-id'; + + render( + +

heading

+
, + ); + + const modal = screen.getByRole('dialog', { hidden: true }); + const heading = screen.getByRole('heading', { + hidden: true, + name: 'heading', + }); + + expect(modal.getAttribute('aria-labelledby')).toBe( + heading.getAttribute('id'), + ); + }); +}); diff --git a/packages/ffe-modals-react/src/Modal.tsx b/packages/ffe-modals-react/src/Modal.tsx index 8b9161faa2..ce4c8567a5 100644 --- a/packages/ffe-modals-react/src/Modal.tsx +++ b/packages/ffe-modals-react/src/Modal.tsx @@ -1,7 +1,66 @@ -import React from 'react'; +import React, { useImperativeHandle, useRef } from 'react'; +import classnames from 'classnames'; +import { CloseButton } from './CloseButton'; +import { Locale } from './types'; -export interface ModalProps {} +export interface ModalProps extends React.ComponentPropsWithoutRef<'dialog'> { + /** Id of modal heading */ + ariaLabelledby: string; + /** Id of modal heading */ + locale?: Locale; +} -export const Modal: React.FC = () => { - return null; +export type ModalHandle = { + readonly open: () => void; + readonly close: () => void; }; + +export const Modal = React.forwardRef( + ( + { + children, + onClick, + ariaLabelledby, + className, + locale = 'nb', + ...rest + }, + ref, + ) => { + const modalRef = useRef(null); + + useImperativeHandle(ref, () => ({ + open: () => { + modalRef.current?.showModal(); + }, + close: () => { + modalRef.current?.close(); + }, + })); + + return ( + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + { + const target = event.target as HTMLDialogElement; + if (target.nodeName === 'DIALOG') { + target.close(); + } + onClick?.(event); + }} + > +
+ modalRef.current?.close()} + locale={locale} + /> + {children} +
+
+ ); + }, +); diff --git a/packages/ffe-modals-react/src/ModalBlock.tsx b/packages/ffe-modals-react/src/ModalBlock.tsx new file mode 100644 index 0000000000..b990d2cd24 --- /dev/null +++ b/packages/ffe-modals-react/src/ModalBlock.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import classnames from 'classnames'; + +export const ModalBlock: React.FC> = ({ + className, + ...rest +}) => { + return ( +
+ ); +}; diff --git a/packages/ffe-modals-react/src/index.ts b/packages/ffe-modals-react/src/index.ts index 8deb0a3dff..9fef1a6e04 100644 --- a/packages/ffe-modals-react/src/index.ts +++ b/packages/ffe-modals-react/src/index.ts @@ -1 +1,2 @@ export { Modal } from './Modal'; +export { ModalBlock } from './ModalBlock'; diff --git a/packages/ffe-modals-react/src/texts.ts b/packages/ffe-modals-react/src/texts.ts new file mode 100644 index 0000000000..6019f61bbb --- /dev/null +++ b/packages/ffe-modals-react/src/texts.ts @@ -0,0 +1,11 @@ +const nb = { + close: 'Lukk', +} as const; +const nn = { + close: 'Lukk', +} as const; +const en = { + close: 'Close', +} as const; + +export const txt = { nb, nn, en }; diff --git a/packages/ffe-modals-react/src/types.ts b/packages/ffe-modals-react/src/types.ts new file mode 100644 index 0000000000..83a3cfb50a --- /dev/null +++ b/packages/ffe-modals-react/src/types.ts @@ -0,0 +1 @@ +export type Locale = 'nb' | 'nn' | 'en';