Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dismissible prop to OuiCallOut #985

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Adds `SchemaItem` as an experimental component ([#974](https://github.com/opensearch-project/oui/pull/974))
- Make `CollapsibleNavGroup` background colors theme-able ([#968](https://github.com/opensearch-project/oui/pull/968))
- Update next light theme primary color to #07827E ([#981](https://github.com/opensearch-project/oui/pull/981))
- Add dismissible prop to OuiCallOut ([#985](https://github.com/opensearch-project/oui/pull/985))

### 🐛 Bug Fixes

Expand Down
8 changes: 8 additions & 0 deletions src-docs/src/views/call_out/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ export default () => (

<OuiSpacer size="m" />

<OuiCallOut
title="Callouts can be dismissed when dismissible is set to true unless the color is danger or warning. "
iconType="wrench"
dismissible
/>

<OuiSpacer size="m" />

<OuiCallOut
size="s"
title="This is a small callout for more unintrusive but constant messages."
Expand Down
92 changes: 92 additions & 0 deletions src/components/call_out/__snapshots__/call_out.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,98 @@ exports[`OuiCallOut props color warning is rendered 1`] = `
/>
`;

exports[`OuiCallOut props dismissible close callout after click 1`] = `
<OuiCallOut
dismissible={true}
title="This is a callout"
>
<div
className="ouiCallOut ouiCallOut--primary"
>
<div
className="ouiCallOutHeader"
>
<span
className="ouiCallOutHeader__title"
>
This is a callout
</span>
</div>
<OuiButtonIcon
aria-label="dismissible_icon"
className="ouiCallOut__closeIcon"
data-test-subj="closeCallOutButton"
iconType="cross"
onClick={[Function]}
>
<button
aria-label="dismissible_icon"
className="ouiButtonIcon ouiButtonIcon--primary ouiButtonIcon--empty ouiButtonIcon--xSmall ouiCallOut__closeIcon"
data-test-subj="closeCallOutButton"
disabled={false}
onClick={[Function]}
type="button"
>
<OuiIcon
aria-hidden="true"
className="ouiButtonIcon__icon"
color="inherit"
size="m"
type="cross"
>
<span
aria-hidden="true"
className="ouiButtonIcon__icon"
color="inherit"
data-ouiicon-type="cross"
size="m"
/>
</OuiIcon>
</button>
</OuiButtonIcon>
</div>
</OuiCallOut>
`;

exports[`OuiCallOut props dismissible close callout after click 2`] = `
<OuiCallOut
dismissible={true}
title="This is a callout"
/>
`;

exports[`OuiCallOut props dismissible is not rendered when in danger color 1`] = `
<div
class="ouiCallOut ouiCallOut--danger"
/>
`;

exports[`OuiCallOut props dismissible is not rendered when in warning color 1`] = `
<div
class="ouiCallOut ouiCallOut--warning"
/>
`;

exports[`OuiCallOut props dismissible is rendered when set to true 1`] = `
<div
class="ouiCallOut ouiCallOut--primary"
>
<button
aria-label="dismissible_icon"
class="ouiButtonIcon ouiButtonIcon--primary ouiButtonIcon--empty ouiButtonIcon--xSmall ouiCallOut__closeIcon"
data-test-subj="closeCallOutButton"
type="button"
>
<span
aria-hidden="true"
class="ouiButtonIcon__icon"
color="inherit"
data-ouiicon-type="cross"
/>
</button>
</div>
`;

exports[`OuiCallOut props heading h1 is rendered 1`] = `
<div
class="ouiCallOut ouiCallOut--primary"
Expand Down
11 changes: 11 additions & 0 deletions src/components/call_out/_call_out.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
.ouiCallOut {
padding: $ouiSize;
border-left: $ouiSizeXS / 2 solid transparent;
position: relative;

&.ouiCallOut--small {
padding: $ouiSizeS;
Expand All @@ -27,6 +28,12 @@
@include ouiCallOutTitle;
margin-bottom: 0; // In case it's nested inside OuiText
}

.ouiCallOut__closeIcon {
position: absolute;
right: 6px;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should use $ouiSize in some way. Maybe calc($ouiSizeM / 2)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to $ouiSizeS / 2

top: 0;
}
}

// smaller font size for headers in small callout
Expand All @@ -47,6 +54,10 @@
.ouiCallOutHeader__title {
color: ouiCallOutColor($name, 'foreground');
}

.ouiCallOut__closeIcon {
fill: ouiCallOutColor($name, 'foreground');
}
}
}

Expand Down
36 changes: 35 additions & 1 deletion src/components/call_out/call_out.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@
*/

import React from 'react';
import { render } from 'enzyme';
import { mount, render } from 'enzyme';
import { requiredProps } from '../../test/required_props';

import { OuiCallOut, COLORS, HEADINGS } from './call_out';
import { findTestSubject } from '../../test';

describe('OuiCallOut', () => {
test('is rendered', () => {
Expand Down Expand Up @@ -81,5 +82,38 @@ describe('OuiCallOut', () => {
});
});
});

describe('dismissible', () => {
it('is rendered when set to true', () => {
const component = render(<OuiCallOut dismissible={true} />);

expect(component).toMatchSnapshot();
});

it('is not rendered when in warning color', () => {
const component = render(
<OuiCallOut dismissible={true} color={'warning'} />
);

expect(component).toMatchSnapshot();
});

it('is not rendered when in danger color', () => {
const component = render(
<OuiCallOut dismissible={true} color={'danger'} />
);

expect(component).toMatchSnapshot();
});

it('close callout after click', () => {
const component = mount(
<OuiCallOut dismissible={true} title="This is a callout" />
);
expect(component).toMatchSnapshot();
findTestSubject(component, 'closeCallOutButton').simulate('click');
expect(component).toMatchSnapshot();
BSFishy marked this conversation as resolved.
Show resolved Hide resolved
});
});
});
});
34 changes: 33 additions & 1 deletion src/components/call_out/call_out.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,21 @@
* under the License.
*/

import React, { forwardRef, Ref, HTMLAttributes, ReactNode } from 'react';
import React, {
forwardRef,
Ref,
HTMLAttributes,
ReactNode,
useState,
} from 'react';

import classNames from 'classnames';

import { CommonProps, keysOf } from '../common';
import { IconType, OuiIcon } from '../icon';

import { OuiText } from '../text';
import { OuiButtonIcon } from '../button';

type Color = 'primary' | 'success' | 'warning' | 'danger';
type Size = 's' | 'm';
Expand All @@ -48,6 +55,7 @@ export type OuiCallOutProps = CommonProps &
color?: Color;
size?: Size;
heading?: Heading;
dismissible?: boolean;
};

const colorToClassNameMap: { [color in Color]: string } = {
Expand Down Expand Up @@ -75,10 +83,12 @@ export const OuiCallOut = forwardRef<HTMLDivElement, OuiCallOutProps>(
children,
className,
heading,
dismissible = false,
...rest
},
ref: Ref<HTMLDivElement>
) => {
const [isCalloutVisible, setIsCalloutVisible] = useState(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we persist this value in session storage?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there is any precedence of using any sort of storage in OUI. In fact, I feel like a more common pattern would be to have a prop like showDismissIcon and onDismiss and let the builder handle the functionality. @KrooshalUX input might be helpful here

const classes = classNames(
'ouiCallOut',
colorToClassNameMap[color],
Expand All @@ -100,6 +110,21 @@ export const OuiCallOut = forwardRef<HTMLDivElement, OuiCallOutProps>(
);
}

const onClose = () => setIsCalloutVisible(false);

let dismissibleIcon;
if (dismissible && color !== 'warning' && color !== 'danger') {
dismissibleIcon = (
<OuiButtonIcon
iconType="cross"
onClick={onClose}
className="ouiCallOut__closeIcon"
aria-label="dismissible_icon"
data-test-subj="closeCallOutButton"
/>
);
}

let optionalChildren;
if (children && size === 's') {
optionalChildren = (
Expand All @@ -126,10 +151,17 @@ export const OuiCallOut = forwardRef<HTMLDivElement, OuiCallOutProps>(
</div>
);
}

if (!isCalloutVisible) {
return null;
}

return (
<div className={classes} ref={ref} {...rest}>
{header}

{dismissibleIcon}

{optionalChildren}
</div>
);
Expand Down