Skip to content

Commit

Permalink
feat(ffe-modals-react): basic modal
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Hellstrand committed Jun 21, 2024
1 parent 8104c91 commit 15e6909
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/ffe-modals-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
30 changes: 30 additions & 0 deletions packages/ffe-modals-react/src/CloseButton.tsx
Original file line number Diff line number Diff line change
@@ -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<React.ComponentPropsWithoutRef<'button'>, 'type'> {
locale: Locale;
}

export const CloseButton: React.FC<CloseButtonProps> = ({
className,
locale,
...rest
}) => {
return (
<button
type="button"
className={classnames('ffe-modal__close')}
aria-label={txt[locale].close}
{...rest}
>
<Icon fileUrl={closeIcon} size="md" />
</button>
);
};
45 changes: 45 additions & 0 deletions packages/ffe-modals-react/src/Modal.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { Modal } from './Modal';
import { render, screen } from '@testing-library/react';

describe('<SystemMessage />', () => {
it('should render with classes', () => {
render(<Modal ariaLabelledby="heading-id" className="custom-class" />);

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(<Modal ariaLabelledby="heading-id" className="custom-class" />);

const closeButton = screen.getByRole('button', {
hidden: true,
name: 'Lukk',
});

expect(closeButton).toBeInTheDocument();
});

it('should set up heading', () => {
const headingId = 'heading-id';

render(
<Modal ariaLabelledby={headingId} className="custom-class">
<h2 id={headingId}>heading</h2>
</Modal>,
);

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'),
);
});
});
67 changes: 63 additions & 4 deletions packages/ffe-modals-react/src/Modal.tsx
Original file line number Diff line number Diff line change
@@ -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<ModalProps> = () => {
return null;
export type ModalHandle = {
readonly open: () => void;
readonly close: () => void;
};

export const Modal = React.forwardRef<ModalHandle, ModalProps>(
(
{
children,
onClick,
ariaLabelledby,
className,
locale = 'nb',
...rest
},
ref,
) => {
const modalRef = useRef<HTMLDialogElement>(null);

useImperativeHandle(ref, () => ({
open: () => {
modalRef.current?.showModal();
},
close: () => {
modalRef.current?.close();
},
}));

return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<dialog
{...rest}
ref={modalRef}
className={classnames('ffe-modal', className)}
aria-labelledby={ariaLabelledby}
onClick={event => {
const target = event.target as HTMLDialogElement;
if (target.nodeName === 'DIALOG') {
target.close();
}
onClick?.(event);
}}
>
<div className="ffe-modal__body">
<CloseButton
onClick={() => modalRef.current?.close()}
locale={locale}
/>
{children}
</div>
</dialog>
);
},
);
11 changes: 11 additions & 0 deletions packages/ffe-modals-react/src/ModalBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import classnames from 'classnames';

export const ModalBlock: React.FC<React.ComponentPropsWithoutRef<'div'>> = ({
className,
...rest
}) => {
return (
<div className={classnames('ffe-modal__block', className)} {...rest} />
);
};
1 change: 1 addition & 0 deletions packages/ffe-modals-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { Modal } from './Modal';
export { ModalBlock } from './ModalBlock';
11 changes: 11 additions & 0 deletions packages/ffe-modals-react/src/texts.ts
Original file line number Diff line number Diff line change
@@ -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 };
1 change: 1 addition & 0 deletions packages/ffe-modals-react/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Locale = 'nb' | 'nn' | 'en';

0 comments on commit 15e6909

Please sign in to comment.