Skip to content

Commit

Permalink
[core] feat(Callout): compact modifier prop (#6592)
Browse files Browse the repository at this point in the history
Co-authored-by: Eugene Chan <echan@palantir.com>
Co-authored-by: Adi Dahiya <adi.dahiya14@gmail.com>
  • Loading branch information
3 people authored Jan 31, 2024
1 parent 13866c7 commit b3bb648
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 64 deletions.
14 changes: 14 additions & 0 deletions packages/core/src/components/callout/_callout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Styleguide callout

$callout-padding: $pt-grid-size * 1.5;
$callout-header-margin-top: $pt-grid-size * 0.2;
$callout-padding-compact: $pt-grid-size;

.#{$ns}-callout {
@include running-typography();
Expand Down Expand Up @@ -73,6 +74,19 @@ $callout-header-margin-top: $pt-grid-size * 0.2;
}
}

&.#{$ns}-compact {
padding: $callout-padding-compact;

&.#{$ns}-callout-icon {
padding-left: $callout-padding-compact + $pt-icon-size-standard + ($pt-grid-size * 0.7);

> .#{$ns}-icon:first-child {
left: $callout-padding-compact;
top: $callout-padding-compact + $callout-header-margin-top;
}
}
}

.#{$ns}-dark & {
background-color: rgba($gray3, 0.2);

Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/components/callout/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export interface CalloutProps extends IntentProps, Props, HTMLDivProps {
/** Callout contents. */
children?: React.ReactNode;

/**
* Whether to use a compact appearance, which reduces the visual padding around callout content.
*/
compact?: boolean;

/**
* Name of a Blueprint UI icon (or an icon element) to render on the left side.
*
Expand Down Expand Up @@ -73,11 +78,12 @@ export class Callout extends AbstractPureComponent<CalloutProps> {
public static displayName = `${DISPLAYNAME_PREFIX}.Callout`;

public render() {
const { className, children, icon, intent, title, ...htmlProps } = this.props;
const { className, children, icon, intent, title, compact, ...htmlProps } = this.props;
const iconElement = this.renderIcon(icon, intent);
const classes = classNames(Classes.CALLOUT, Classes.intentClass(intent), className, {
[Classes.CALLOUT_HAS_BODY_CONTENT]: !Utils.isReactNodeEmpty(children),
[Classes.CALLOUT_ICON]: iconElement != null,
[Classes.COMPACT]: compact,
});

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/callout/calloutTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("<Callout>", () => {

it("renders optional title element", () => {
const wrapper = mount(<Callout title="title" />, { attachTo: containerElement });
assert.isTrue(wrapper.find(H5).exists());
assert.isTrue(wrapper.find(`.${Classes.HEADING}`).exists());
// NOTE: JSX cannot be passed through `title` prop due to conflict with HTML props
// @ts-expect-error
mount(<Callout title={<em>typings fail</em>} />);
Expand Down
107 changes: 45 additions & 62 deletions packages/docs-app/src/examples/core-examples/calloutExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,68 +23,51 @@ import type { IconName } from "@blueprintjs/icons";
import { IconSelect } from "./common/iconSelect";
import { IntentSelect } from "./common/intentSelect";

interface CalloutExampleState {
contentIndex?: number;
icon?: IconName;
intent?: Intent;
showTitle: boolean;
}
export const CalloutExample: React.FC<DocsExampleProps> = props => {
const [compact, setCompact] = React.useState(false);
const [contentIndex, setContentIndex] = React.useState(0);
const [showTitle, setShowTitle] = React.useState(true);
const [icon, setIcon] = React.useState<IconName>();
const [intent, setIntent] = React.useState<Intent>();

export class CalloutExample extends React.PureComponent<DocsExampleProps, CalloutExampleState> {
public state: CalloutExampleState = {
contentIndex: 0,
showTitle: true,
};

private handleContentIndexChange = handleNumberChange(contentIndex => this.setState({ contentIndex }));

private handleIconNameChange = (icon: IconName) => this.setState({ icon });

private handleIntentChange = (intent: Intent) => this.setState({ intent });

private handleShowTitleChange = handleBooleanChange((showTitle: boolean) => this.setState({ showTitle }));

public render() {
const { contentIndex, showTitle, ...calloutProps } = this.state;
const options = (
<>
<H5>Props</H5>
<Switch checked={showTitle} label="Title" onChange={this.handleShowTitleChange} />
<IntentSelect intent={calloutProps.intent} onChange={this.handleIntentChange} showClearButton={true} />
<IconSelect iconName={calloutProps.icon} onChange={this.handleIconNameChange} />
<H5>Children</H5>
<Label>
Example content
<HTMLSelect value={contentIndex} onChange={this.handleContentIndexChange}>
<option value="0">Text with formatting</option>
<option value="1">Simple text string</option>
<option value="2">Button</option>
<option value="3">Empty</option>
</HTMLSelect>
</Label>
</>
);
/* eslint-disable react/jsx-key */
const children = [
<React.Fragment>
Long-form information about the important content. This text is styled as{" "}
<a href="#core/typography.running-text">"Running text"</a>, so it may contain things like headers, links,
lists, <Code>code</Code> etc.
</React.Fragment>,
"Long-form information about the important content",
<Button text="Example button" intent="primary" />,
undefined,
][contentIndex];
/* eslint-enable react/jsx-key */

return (
<Example options={options} {...this.props}>
<Callout {...calloutProps} title={showTitle ? "Visually important content" : undefined}>
{this.renderChildren(contentIndex)}
</Callout>
</Example>
);
}
const options = (
<>
<H5>Props</H5>
<Switch checked={showTitle} label="Title" onChange={handleBooleanChange(setShowTitle)} />
<Switch checked={compact} label="Compact" onChange={handleBooleanChange(setCompact)} />
<IntentSelect intent={intent} onChange={setIntent} showClearButton={true} />
<IconSelect iconName={icon} onChange={setIcon} />
<H5>Children</H5>
<Label>
Example content
<HTMLSelect value={contentIndex} onChange={handleNumberChange(setContentIndex)}>
<option value={0}>Text with formatting</option>
<option value={1}>Simple text string</option>
<option value={2}>Button</option>
<option value={3}>Empty</option>
</HTMLSelect>
</Label>
</>
);

/* eslint-disable react/jsx-key */
private renderChildren(contentIndex: number) {
return [
<React.Fragment>
Long-form information about the important content. This text is styled as{" "}
<a href="#core/typography.running-text">"Running text"</a>, so it may contain things like headers,
links, lists, <Code>code</Code> etc.
</React.Fragment>,
"Long-form information about the important content",
<Button text="Example button" intent="primary" />,
undefined,
][contentIndex];
}
}
return (
<Example options={options} {...props}>
<Callout {...{ compact, intent, icon }} title={showTitle ? "Visually important content" : undefined}>
{children}
</Callout>
</Example>
);
};

1 comment on commit b3bb648

@adidahiya
Copy link
Contributor

Choose a reason for hiding this comment

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

[core] feat(Callout): `compact` modifier prop (#6592)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.