From 98fa52e78ac1eac753567db77a00d1eb5ade23b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Thu, 11 May 2023 21:28:42 +0200 Subject: [PATCH] feat(HelpButton): remove `modal_props` in favour of `render` (#2333) --- .../releases/eufemia/v10-info.mdx | 2 +- .../components/help-button/properties.mdx | 34 ++++- .../src/components/help-button/HelpButton.tsx | 36 ++--- .../help-button/__tests__/HelpButton.test.tsx | 139 +++++++++++------- .../stories/HelpButton.stories.tsx | 14 +- 5 files changed, 134 insertions(+), 91 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx index c8253ce2bd0..78281837b6b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx @@ -382,7 +382,7 @@ The Anchor was moved form `/elements` to `/components`. ### [HelpButton](/uilib/components/help-button) -1. property `modal_props` does not longer support the `mode` property. From a UX perspective it's only desirable to display a `Dialog` when using `HelpButton`, and not a `Drawer`. +1. The properties `modal_props` and `modal_content` where removed. You may replace these props with the new `render` property. See [this example](/uilib/components/help-button/properties/). ## Element changes diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/help-button/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/help-button/properties.mdx index 41fc7922306..6383821cbc6 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/help-button/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/help-button/properties.mdx @@ -4,11 +4,29 @@ showTabs: true ## Properties -| Properties | Description | -| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| `children` or `modal_content` | _(optional)_ the content to show. | -| `title` | _(optional)_ the content title. Defaults to `Hjelpetekst` (HelpButton.title). | -| `icon` | _(optional)_ the icon defaults to `question`. | -| `modal_content` | _(optional)_ the content which will appear when triggering the modal. | -| `modal_props` | _(optional)_ accepts all [Modal properties](/uilib/components/modal/properties) as an object, if `children` is given. | -| [Button](/uilib/components/button/properties) | _(optional)_ accepts all Button properties, if `children` is not given. | +| Properties | Description | +| --------------------------------------------- | -------------------------------------------------------------------------------------- | +| `children` or `modal_content` | _(optional)_ the content to show. | +| `title` | _(optional)_ the content title. Defaults to `Hjelpetekst` (HelpButton.title). | +| `icon` | _(optional)_ the icon defaults to `question`. | +| `render` | _(optional)_ accepts a function that returns a valid React Element. See example below. | +| [Button](/uilib/components/button/properties) | _(optional)_ accepts all Button properties, if `children` is not given. | + +## How to use `render` + +```jsx +import { HelpButton, Dialog } from '@dnb/eufemia' + +render( + ( + + {children} + + )} + > + Help text + +) +``` diff --git a/packages/dnb-eufemia/src/components/help-button/HelpButton.tsx b/packages/dnb-eufemia/src/components/help-button/HelpButton.tsx index 3bbd22952e3..71bfb3a1865 100644 --- a/packages/dnb-eufemia/src/components/help-button/HelpButton.tsx +++ b/packages/dnb-eufemia/src/components/help-button/HelpButton.tsx @@ -8,7 +8,6 @@ import Context from '../../shared/Context' import Dialog from '../dialog/Dialog' import HelpButtonInstance from './HelpButtonInstance' import type { ButtonProps } from '../button/Button' -import { ModalProps } from '../modal/types' import { extendPropsWithContext } from '../../shared/component-helper' const defaultProps = { @@ -17,45 +16,32 @@ const defaultProps = { } export type HelpButtonProps = { - modal_content?: React.ReactNode - modal_props?: ModalProps + render?: ( + children: React.ReactNode, + props: ButtonProps + ) => React.ReactElement } & ButtonProps export default function HelpButton(localProps: HelpButtonProps) { - const getContent = (props: HelpButtonProps) => { - if (props.modal_content) { - return props.modal_content - } - return typeof props.children === 'function' - ? props.children(props) - : props.children - } - const context = React.useContext(Context) const props = extendPropsWithContext(localProps, defaultProps) - const content = getContent(props) - const { - modal_content, // eslint-disable-line - children, // eslint-disable-line - modal_props, - ...params - } = props + const { children, render, ...params } = props if (params.icon === null) { params.icon = 'question' } - if (content) { + if (children) { if (!params.title) { params.title = context.getTranslation(props).HelpButton.title } - return ( - - {content} - - ) + if (typeof render === 'function') { + return render(children, params) + } + + return {children} } return diff --git a/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButton.test.tsx b/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButton.test.tsx index 601b7ada286..7aed574086f 100644 --- a/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButton.test.tsx +++ b/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButton.test.tsx @@ -5,96 +5,105 @@ import React from 'react' import { - mount, fakeProps, axeComponent, loadScss, } from '../../../core/jest/jestSetup' -import Component, { HelpButtonProps } from '../HelpButton' +import Dialog from '../../dialog/Dialog' +import HelpButton, { HelpButtonProps } from '../HelpButton' import { question as QuestionIcon, information_medium as InformationIcon, } from '../../../icons' -import { ModalProps } from '../../modal/types' +import { fireEvent, render } from '@testing-library/react' const snapshotProps = fakeProps(require.resolve('../HelpButton'), {}) snapshotProps.id = 'help-button' -const modal_props: ModalProps = {} -modal_props.content_id = null -modal_props.no_animation = true - -const props: HelpButtonProps = { modal_props } +const props: HelpButtonProps = {} props.id = 'help-button' -describe('HelpButton component', () => { +describe('HelpButton', () => { it('should have question icon by default', () => { - const Comp = mount() + render() expect( - Comp.find('.dnb-icon').instance().getAttribute('data-testid') + document.querySelector('.dnb-icon').getAttribute('data-testid') ).toBe('question icon') - expect(Comp.find('svg').html()).toBe(mount().html()) - expect(Comp.find('.dnb-button').text().trim()).toBe('‌') + expect(document.querySelector('svg').outerHTML).toBe( + render().container.innerHTML + ) + expect(document.querySelector('.dnb-button').textContent.trim()).toBe( + '‌' + ) }) it('should use "information" icon when set', () => { - const Comp = mount() + render() expect( - Comp.find('.dnb-icon').instance().getAttribute('data-testid') + document.querySelector('.dnb-icon').getAttribute('data-testid') ).toBe('information icon') - expect(Comp.find('svg').html()).toBe(mount().html()) - expect(Comp.find('.dnb-button').text().trim()).toBe('‌') + expect(document.querySelector('svg').outerHTML).toBe( + render().container.innerHTML + ) + expect(document.querySelector('.dnb-button').textContent.trim()).toBe( + '‌' + ) }) it('should use given icon', () => { - const Comp = mount() + render() expect( - Comp.find('.dnb-icon').instance().getAttribute('data-testid') + document.querySelector('.dnb-icon').getAttribute('data-testid') ).toBe('information medium icon') - expect(Comp.find('svg').html()).toBe(mount().html()) - expect(Comp.find('.dnb-button').text().trim()).toBe('‌') + expect(document.querySelector('svg').outerHTML).toBe( + render().container.innerHTML + ) + expect(document.querySelector('.dnb-button').textContent.trim()).toBe( + '‌' + ) }) it('should have correct role description', () => { - const Comp = mount() + render() expect( - Comp.find('.dnb-button') - .instance() + document + .querySelector('.dnb-button') + .getAttribute('aria-roledescription') ).toBe('Hjelp-knapp') }) describe('with bell icon', () => { it('should have correct aria-label', () => { - const Comp = mount() + render() expect( - Comp.find('.dnb-button').instance().getAttribute('aria-label') + document.querySelector('.dnb-button').getAttribute('aria-label') ).toBe('Hjelpetekst') }) it('should have not aria-label if text is given', () => { - const Comp = mount( - - ) + render() expect( - Comp.find('.dnb-button').instance().hasAttribute('aria-label') + document.querySelector('.dnb-button').hasAttribute('aria-label') ).toBe(false) - expect(Comp.find('.dnb-button').text().trim()).toBe('‌button text') + expect( + document.querySelector('.dnb-button').textContent.trim() + ).toBe('‌button text') }) it('should have aria-label if title is given, but no text', () => { - const Comp = mount( - - ) + render() expect( - Comp.find('.dnb-button').instance().getAttribute('aria-label') + document.querySelector('.dnb-button').getAttribute('aria-label') ).toBe('button title') - expect(Comp.find('.dnb-button').text().trim()).toBe('‌') + expect( + document.querySelector('.dnb-button').textContent.trim() + ).toBe('‌') }) it('should use given aria-label if title is given, but no text', () => { - const Comp = mount( - { /> ) expect( - Comp.find('.dnb-button').instance().getAttribute('aria-label') + document.querySelector('.dnb-button').getAttribute('aria-label') ).toBe('custom aria-label') - expect(Comp.find('.dnb-button').text().trim()).toBe('‌') + expect( + document.querySelector('.dnb-button').textContent.trim() + ).toBe('‌') }) it('should validate with ARIA rules', async () => { - const Comp = mount() - expect(await axeComponent(Comp)).toHaveNoViolations() + const Component = render() + expect(await axeComponent(Component)).toHaveNoViolations() }) }) - it('should open a modal if children are given', () => { - const modalContent = 'Modal Content' - const Comp = mount({modalContent}) + it('should open a dialog if children are given', () => { + const dialogContent = 'Dialog Content' + render({dialogContent}) - Comp.find('button.dnb-modal__trigger').simulate('click') + fireEvent.click(document.querySelector('button.dnb-modal__trigger')) const id = `dnb-modal-${props.id}` - const modalElem = document.getElementById(id) - const textContent = String(modalElem.textContent).replace( + const dialogElem = document.getElementById(id) + const textContent = String(dialogElem.textContent).replace( /\u200C/g, '' ) - expect(textContent).toContain(modalContent) + expect(textContent).toContain(dialogContent) + }) + + it('should return given render element', () => { + render( + ( + + {children} + + )} + > + Help text + + ) + + fireEvent.click(document.querySelector('button.dnb-modal__trigger')) + + const dialogElem = document.querySelector('.custom-class') + + expect( + dialogElem.querySelector('.dnb-dialog__header').textContent + ).toBe('Title') + expect( + dialogElem.querySelector('.dnb-dialog__content').textContent + ).toBe('Help text') }) it('should validate with ARIA rules', async () => { - const Comp = mount() - expect(await axeComponent(Comp)).toHaveNoViolations() + const Component = render() + expect(await axeComponent(Component)).toHaveNoViolations() }) }) diff --git a/packages/dnb-eufemia/src/components/help-button/stories/HelpButton.stories.tsx b/packages/dnb-eufemia/src/components/help-button/stories/HelpButton.stories.tsx index e871d1a6114..49fd7f6e6e5 100644 --- a/packages/dnb-eufemia/src/components/help-button/stories/HelpButton.stories.tsx +++ b/packages/dnb-eufemia/src/components/help-button/stories/HelpButton.stories.tsx @@ -6,7 +6,7 @@ import React from 'react' import { Wrapper, Box } from 'storybook-utils/helpers' -import { HelpButton, Dialog, Button, Section, Input } from '../..' +import { HelpButton, Dialog, Drawer, Button, Section, Input } from '../..' export default { title: 'Eufemia/Components/HelpButton', @@ -16,11 +16,13 @@ export const HelpButtonSandbox = () => ( { - console.log(e) - }} - /> + title="Tittel" + render={(children, props) => ( + {children} + )} + > + Helpe text +