Skip to content

Commit b90ef52

Browse files
[core] feat(NonIdealState): new iconMuted prop (#6611)
Co-authored-by: Adi Dahiya <[email protected]>
1 parent b80ca1d commit b90ef52

File tree

7 files changed

+54
-34
lines changed

7 files changed

+54
-34
lines changed

packages/core/src/common/classes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ export const TREE_ROOT = `${NS}-tree-root`;
359359
export const ICON = `${NS}-icon`;
360360
export const ICON_STANDARD = `${ICON}-standard`;
361361
export const ICON_LARGE = `${ICON}-large`;
362+
export const ICON_MUTED = `${ICON}-muted`;
362363

363364
/**
364365
* Returns the namespace prefix for all Blueprint CSS classes.

packages/core/src/components/forms/controlProps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export interface ControlProps
6868
label?: string;
6969

7070
/**
71-
* JSX Element label for the control.
71+
* JSX element label for the control.
7272
*
7373
* This prop is a workaround for TypeScript consumers as the type definition for `label` only
7474
* accepts strings. JavaScript consumers can provide a JSX element directly to `label`.

packages/core/src/components/icon/_icon.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@
3333
fill: currentcolor;
3434
}
3535
}
36+
37+
// muted icon style used by NonIdealState
38+
&.#{$ns}-icon-muted svg {
39+
fill-opacity: 15%;
40+
// need to show overflow for some strokes on paths that reach the edge of the icon bounding box
41+
overflow: visible;
42+
43+
path {
44+
stroke: $gray3;
45+
stroke-opacity: 50%;
46+
stroke-width: 0.5px;
47+
}
48+
}
49+
50+
.#{$ns}-dark & {
51+
.#{$ns}-icon-muted svg {
52+
fill-opacity: 20%;
53+
}
54+
}
3655
}
3756

3857
// intent colors for both SVG and font icons are set in _typography-colors.scss

packages/core/src/components/non-ideal-state/_non-ideal-state.scss

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Non-ideal state
1010
Markup:
1111
<div class="#{$ns}-non-ideal-state">
1212
<div class="#{$ns}-non-ideal-state-visual" style="font-size: 48px; line-height: 48px;">
13-
<span class="#{$ns}-icon #{$ns}-icon-folder-open"></span>
13+
<span class="#{$ns}-icon #{$ns}-icon-muted #{$ns}-icon-folder-open"></span>
1414
</div>
1515
<div class="#{$ns}-non-ideal-state-text">
1616
<h4 class="#{$ns}-heading">This folder is empty</h4>
@@ -68,22 +68,4 @@ Styleguide non-ideal-state
6868

6969
.#{$ns}-non-ideal-state-visual {
7070
color: $gray3;
71-
72-
.#{$ns}-icon svg {
73-
fill-opacity: 15%;
74-
// need to show overflow for some strokes on paths that reach the edge of the icon bounding box
75-
overflow: visible;
76-
77-
path {
78-
stroke: $gray3;
79-
stroke-opacity: 50%;
80-
stroke-width: 0.5px;
81-
}
82-
}
83-
84-
.#{$ns}-dark & {
85-
.#{$ns}-icon svg {
86-
fill-opacity: 20%;
87-
}
88-
}
8971
}

packages/core/src/components/non-ideal-state/non-ideal-state.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ parent: components
77
Non-ideal UI states inform the user that some content is unavailable. There are several types of non-ideal states,
88
including:
99

10-
* **Empty state:** a container has just been created and has no data in it yet, or a container's contents have been
11-
intentionally removed.
12-
* **Loading state:** a container is awaiting data. A good practice is to show a spinner for this state with optional
13-
explanatory text below the spinner.
14-
* **Error state:** something went wrong (for instance, 404 and 500 HTTP errors). In this case, a good practice is to
15-
add a call to action directing the user what to do next.
10+
* **Empty state:** a container has just been created and has no data in it yet, or a container's contents have been
11+
intentionally removed.
12+
* **Loading state:** a container is awaiting data. A good practice is to show a spinner for this state with optional
13+
explanatory text below the spinner.
14+
* **Error state:** something went wrong (for instance, 404 and 500 HTTP errors). In this case, a good practice is to
15+
add a call to action directing the user what to do next.
1616

1717
@reactExample NonIdealStateExample
1818

@@ -27,8 +27,7 @@ __NonIdealState__ component props are rendered in this order in the DOM, with co
2727

2828
By default, a vertical layout is used, but you can make it horizontal with `layout="horizontal"`.
2929

30-
Icons take on a muted appearance inside this component, but their shape contrast is preserved by adding a small stroke
31-
to the SVG paths.
30+
Icons will also take on a muted appearance inside this component, with their shape contrast preserved by adding a small stroke to the SVG paths. This behavior can be disabled by setting `iconMuted={false}`.
3231

3332
@## Props interface
3433

packages/core/src/components/non-ideal-state/nonIdealState.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export interface NonIdealStateProps extends Props {
4646
*/
4747
description?: React.ReactChild;
4848

49-
/** The name of a Blueprint icon or a JSX Element (such as `<Spinner/>`) to render above the title. */
49+
/** The name of a Blueprint icon or a JSX element (such as `<Spinner/>`) to render above the title. */
5050
icon?: IconName | MaybeElement;
5151

5252
/**
@@ -56,6 +56,13 @@ export interface NonIdealStateProps extends Props {
5656
*/
5757
iconSize?: NonIdealStateIconSize;
5858

59+
/**
60+
* Whether the icon should use a muted style.
61+
*
62+
* @default true
63+
*/
64+
iconMuted?: boolean;
65+
5966
/**
6067
* Component layout, either vertical or horizontal.
6168
*
@@ -76,6 +83,7 @@ export class NonIdealState extends AbstractPureComponent<NonIdealStateProps> {
7683
public static displayName = `${DISPLAYNAME_PREFIX}.NonIdealState`;
7784

7885
public static defaultProps: Partial<NonIdealStateProps> = {
86+
iconMuted: true,
7987
iconSize: NonIdealStateIconSize.STANDARD,
8088
layout: "vertical",
8189
};
@@ -94,7 +102,7 @@ export class NonIdealState extends AbstractPureComponent<NonIdealStateProps> {
94102
}
95103

96104
private maybeRenderVisual() {
97-
const { icon, iconSize } = this.props;
105+
const { icon, iconMuted, iconSize } = this.props;
98106
if (icon == null) {
99107
return undefined;
100108
} else {
@@ -103,7 +111,13 @@ export class NonIdealState extends AbstractPureComponent<NonIdealStateProps> {
103111
className={Classes.NON_IDEAL_STATE_VISUAL}
104112
style={{ fontSize: `${iconSize}px`, lineHeight: `${iconSize}px` }}
105113
>
106-
<Icon icon={icon} size={iconSize} aria-hidden={true} tabIndex={-1} />
114+
<Icon
115+
className={classNames({ [Classes.ICON_MUTED]: iconMuted })}
116+
icon={icon}
117+
size={iconSize}
118+
aria-hidden={true}
119+
tabIndex={-1}
120+
/>
107121
</div>
108122
);
109123
}

packages/core/test/non-ideal-state/nonIdealStateTests.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,22 @@ describe("<NonIdealState>", () => {
2525
const wrapper = shallow(
2626
<NonIdealState
2727
action={<p>More text!</p>}
28-
description="An error occured."
28+
description="An error occurred."
2929
title="ERROR"
3030
icon="folder-close"
3131
/>,
3232
);
3333
assert.exists(wrapper.find(H4), "missing H4");
34-
[Classes.NON_IDEAL_STATE_VISUAL, Classes.NON_IDEAL_STATE].forEach(className => {
35-
assert.isTrue(wrapper.find(`.${className}`).exists(), `missing ${className}`);
34+
[Classes.NON_IDEAL_STATE_VISUAL, Classes.ICON_MUTED, Classes.NON_IDEAL_STATE].forEach(className => {
35+
assert.exists(wrapper.find(`.${className}`), `missing ${className}`);
3636
});
3737
});
3838

39+
it("does not apply icon muted style", () => {
40+
const wrapper = shallow(<NonIdealState title="ERROR" icon="folder-close" iconMuted={false} />);
41+
assert.isFalse(wrapper.find(`.${Classes.ICON_MUTED}`).exists(), `unexpected ${Classes.ICON_MUTED}`);
42+
});
43+
3944
it("ensures description is wrapped in an element", () => {
4045
const wrapper = shallow(<NonIdealState action={<strong />} description="foo" />);
4146
const div = wrapper.find(`.${Classes.NON_IDEAL_STATE_TEXT}`).children().find("div");

0 commit comments

Comments
 (0)