Skip to content

Commit

Permalink
[EuiModalHeaderTitle] Remove automatic detection of title contents in…
Browse files Browse the repository at this point in the history
… favor of always wrapping a `h1`, configurable via new `component` prop (#6530)

* Use a `component` prop for EuiModalHeaderTitle tag wrapper instead of trying to detect child types

- this is because Kibana's `<FormattedMessage>` component (which only outputs a string) is incorrectly not getting styles applied to it

* Add prop unit tests

+ convert tests to RTL while we're here

* Allow `EuiConfirmModal`s to override title component tag as well if necessary

* Changelog

* tweak changelog copy a bit more
  • Loading branch information
cee-chen authored Jan 18, 2023
1 parent f1110bf commit 0a86e46
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiModalHeaderTitle component 1`] = `
<div
class="euiTitle euiModalHeader__title emotion-euiTitle-m"
>
children
</div>
`;

exports[`EuiModalHeaderTitle is rendered 1`] = `
<h1
aria-label="aria-label"
Expand Down
18 changes: 17 additions & 1 deletion src/components/modal/confirm_modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React from 'react';
import { mount } from 'enzyme';
import { render } from '../../test/rtl';

import {
findTestSubject,
Expand All @@ -33,7 +34,10 @@ beforeEach(() => {

describe('EuiConfirmModal', () => {
shouldRenderCustomStyles(
<EuiConfirmModal onCancel={() => {}}>children</EuiConfirmModal>
<EuiConfirmModal title="Test" onCancel={() => {}}>
children
</EuiConfirmModal>,
{ childProps: ['titleProps'] }
);

test('renders EuiConfirmModal', () => {
Expand Down Expand Up @@ -196,4 +200,16 @@ describe('EuiConfirmModal', () => {
});
});
});

test('titleProps', () => {
const { baseElement } = render(
<EuiConfirmModal
title="A confirmation modal"
titleProps={{ component: 'div', className: 'titlePropsTest' }}
onCancel={() => {}}
/>
);
const title = baseElement.querySelector('.titlePropsTest');
expect(title?.tagName.toLowerCase()).toEqual('div');
});
});
13 changes: 11 additions & 2 deletions src/components/modal/confirm_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React, {
FunctionComponent,
ComponentProps,
ReactNode,
useEffect,
useState,
Expand All @@ -17,7 +18,10 @@ import classnames from 'classnames';
import { EuiModal, EuiModalProps } from './modal';
import { EuiModalFooter } from './modal_footer';
import { EuiModalHeader } from './modal_header';
import { EuiModalHeaderTitle } from './modal_header_title';
import {
EuiModalHeaderTitle,
EuiModalHeaderTitleProps,
} from './modal_header_title';
import { EuiModalBody } from './modal_body';

import { useEuiTheme } from '../../services';
Expand All @@ -34,6 +38,7 @@ export interface EuiConfirmModalProps
*/
children?: ReactNode;
title?: ReactNode;
titleProps?: ComponentProps<EuiModalHeaderTitleProps>;
cancelButtonText?: ReactNode;
confirmButtonText?: ReactNode;
onCancel: (
Expand Down Expand Up @@ -71,6 +76,7 @@ export const CANCEL_BUTTON = 'cancel';
export const EuiConfirmModal: FunctionComponent<EuiConfirmModalProps> = ({
children,
title,
titleProps,
onCancel,
onConfirm,
cancelButtonText,
Expand Down Expand Up @@ -118,7 +124,10 @@ export const EuiConfirmModal: FunctionComponent<EuiConfirmModalProps> = ({
if (title) {
modalTitle = (
<EuiModalHeader>
<EuiModalHeaderTitle data-test-subj="confirmModalTitleText">
<EuiModalHeaderTitle
data-test-subj="confirmModalTitleText"
{...titleProps}
>
{title}
</EuiModalHeaderTitle>
</EuiModalHeader>
Expand Down
13 changes: 10 additions & 3 deletions src/components/modal/modal_header_title.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import { render } from 'enzyme';
import { render } from '../../test/rtl';
import { requiredProps } from '../../test/required_props';
import { shouldRenderCustomStyles } from '../../test/internal';

Expand All @@ -17,9 +17,16 @@ describe('EuiModalHeaderTitle', () => {
shouldRenderCustomStyles(<EuiModalHeaderTitle>children</EuiModalHeaderTitle>);

test('is rendered', () => {
const component = (
const { container } = render(
<EuiModalHeaderTitle {...requiredProps}>children</EuiModalHeaderTitle>
);
expect(render(component)).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});

test('component', () => {
const { container } = render(
<EuiModalHeaderTitle component="div">children</EuiModalHeaderTitle>
);
expect(container.firstChild).toMatchSnapshot();
});
});
14 changes: 11 additions & 3 deletions src/components/modal/modal_header_title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,34 @@
* Side Public License, v 1.
*/

import React, { FunctionComponent, HTMLAttributes } from 'react';
import React, { FunctionComponent, HTMLAttributes, ElementType } from 'react';
import classnames from 'classnames';
import { CommonProps } from '../common';

import { EuiTitle } from '../title';

export type EuiModalHeaderTitleProps = FunctionComponent<
HTMLAttributes<HTMLDivElement> & CommonProps
HTMLAttributes<HTMLHeadingElement> &
CommonProps & {
/**
* The tag to render
* @default h1
*/
component?: ElementType;
}
>;

export const EuiModalHeaderTitle: EuiModalHeaderTitleProps = ({
className,
children,
component: Component = 'h1',
...rest
}) => {
const classes = classnames('euiModalHeader__title', className);

return (
<EuiTitle size="m" className={classes} {...rest}>
{React.isValidElement(children) ? children : <h1>{children}</h1>}
<Component>{children}</Component>
</EuiTitle>
);
};
6 changes: 6 additions & 0 deletions upcoming_changelogs/6530.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Added the `component` prop to `EuiModalHeaderTitle`, which allows overriding the default `h1` tag
- Added the `titleProps` prop to `EuiConfirmModal`, which allows overriding the default `h1` tag

**Breaking changes**

- `EuiModalHeaderTitle` now **always** wraps its children in a `h1` tag (previously attempted to conditionally detect whether its children were raw strings or not). To change this tag type to, e.g. a more generic `div`, use the new `component` prop.

0 comments on commit 0a86e46

Please sign in to comment.