diff --git a/docs/app/views/examples/components/empty_state/_preview.html.erb b/docs/app/views/examples/components/empty_state/_preview.html.erb index b1c7f56bc4..cf18c50378 100644 --- a/docs/app/views/examples/components/empty_state/_preview.html.erb +++ b/docs/app/views/examples/components/empty_state/_preview.html.erb @@ -8,12 +8,12 @@ <% end %>

With Icon and Compact

-

Compact variants, with less top and bottom padding, are useful for smaller contexts.

+

Compact variants, with a smaller icon, are useful for smaller contexts.

<%= sage_component SageEmptyState, { icon: "bold", title: "Title for state, compact variety", text: "Text to appear below. Lorem ipsum dolor sit amet consectituor.", - scope: "compact", + size: "compact", } do %>

Other stuff such as buttons...

<% end %> @@ -26,61 +26,3 @@ } do %>

Other stuff such as buttons...

<% end %> - -

Page Scope and Graphic

-<%= sage_component SageEmptyState, { - title: "Create your first Email Campaign", - graphic: image_tag("empty-state-lg.svg", alt: ""), - scope: "page", -} do %> - <% content_for :sage_empty_state_text do %> -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Elit arcu volutpat cursus ultricies ac, ultricies. - Platea sed nibh molestie ut. -

- <% end %> - <% content_for :sage_empty_state_actions do %> - <%= sage_component SageButtonGroup, { gap: :sm } do %> - <%= sage_component SageButton, { - attributes: { href: "#" }, - style: "primary", - value: "Create Email Campaigns", - } %> - <%= sage_component SageButton, { - attributes: { href: "#" }, - style: "secondary", - subtle: true, - value: "Learn More", - } %> - <% end %> - <% end %> -<% end %> - -

Page Scope and Graphic with Video

-<%= sage_component SageEmptyState, { - title: "Create your first Email Campaign", - graphic: image_tag("empty-state-lg.svg", alt: ""), - scope: "page", -} do %> - <% content_for :sage_empty_state_text do %> -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Elit arcu volutpat cursus ultricies ac, ultricies. - Platea sed nibh molestie ut. -

- <% end %> - <% content_for :sage_empty_state_actions do %> - <%= sage_component SageButton, { - attributes: { href: "#" }, - style: "primary", - value: "Create Email Campaigns", - } %> - <%= sage_component SageButton, { - attributes: { href: "#" }, - style: "secondary", - subtle: true, - value: "Learn More", - } %> - <% end %> -<% end %> diff --git a/docs/app/views/examples/components/empty_state/_props.html.erb b/docs/app/views/examples/components/empty_state/_props.html.erb index 78d94b1012..8ea699b93d 100644 --- a/docs/app/views/examples/components/empty_state/_props.html.erb +++ b/docs/app/views/examples/components/empty_state/_props.html.erb @@ -1,6 +1,6 @@ <%= md('`center_vertical`') %> - <%= md('If true, the empty state will be adjust to be visually centered inside the entire page context. Meant to be used in pairing with `scope: "page"`.') %> + <%= md('If true, the empty state will be visually centered inside the entire page context.') %> <%= md('Boolean') %> <%= md('`false`') %> @@ -17,14 +17,13 @@ <%= md('`nil`') %> - <%= md('`scope`') %> - <%= md('The layout of the component adjusts depending on this context setting: - -- `nil` (default) sets up a "feature"-level layout for use within panels or cards for showing empty state for particular features. + <%= md('`size`') %> + <%= md('The size of the icon adjusts depending on this context setting: + +- `nil` (default) sets up a "feature"-level layout for use on whole page empty states and is intended to fill the stage. - `"compact"` similar to the default above, this compacts the spacing a little more for smaller contexts. -- `"page"` is for use on whole page empty states and is intended to fill the stage. ') %> - <%= md('`nil` | `"compact"` | `"page"`') %> + <%= md('`nil` | `"compact"`') %> <%= md('`nil`') %> @@ -48,11 +47,28 @@ Content Slots + + <%= md('`:sage_empty_state_actions`') %> + <%= md(' + Slot into which buttons or other actions can be placed. + ') %> + + + + + <%= md('`:sage_empty_state_text`') %> + <%= md(' + Slot into which text can be placed. + ') %> + + + <%= md('`:sage_empty_state_video`') %> <%= md(' - Slot into which video cards or other media can be placed. + Slot into which video cards or other media can be placed. ') %> + diff --git a/docs/lib/sage_rails/app/sage_components/sage_empty_state.rb b/docs/lib/sage_rails/app/sage_components/sage_empty_state.rb index 79a7fc2112..6c29f7d92d 100644 --- a/docs/lib/sage_rails/app/sage_components/sage_empty_state.rb +++ b/docs/lib/sage_rails/app/sage_components/sage_empty_state.rb @@ -3,10 +3,11 @@ class SageEmptyState < SageComponent center_vertical: [:optional, NilClass, TrueClass], graphic: [:optional, NilClass, String], icon: [:optional, NilClass, String], - scope: [:optional, NilClass, Set.new(["page", "compact", nil])], + icon_background: [:optional, NilClass, String], + size: [:optional, NilClass, Set.new(["compact", nil])], text: [:optional, NilClass, String], title: [:optional, NilClass, String], - title_tag: [:optional, NilClass, Set.new(["h1", "h2", "h3", "h4", "h5", "h5", "h6"])], + title_tag: [:optional, NilClass, Set.new(["h1", "h2", "h3", "h4", "h5", "h6"])], }) def sections %w(empty_state_actions empty_state_text empty_state_video) diff --git a/docs/lib/sage_rails/app/views/sage_components/_sage_empty_state.html.erb b/docs/lib/sage_rails/app/views/sage_components/_sage_empty_state.html.erb index f2d9c8b4ea..7f4070f492 100644 --- a/docs/lib/sage_rails/app/views/sage_components/_sage_empty_state.html.erb +++ b/docs/lib/sage_rails/app/views/sage_components/_sage_empty_state.html.erb @@ -1,6 +1,6 @@ <% -variant = component.scope.present? ? component.scope : false -title_tag = variant == "feature" ? "h1" : "h2" +variant = component.size.present? ? component.size : false +title_tag = variant == "compact" ? "h2" : "h1" if component.title_tag.present? title_tag = component.title_tag end @@ -20,11 +20,19 @@ end <% end %> - <%= sage_component SageIcon, { - icon: component.icon, - size: "3xl", - css_classes: "sage-empty-state__icon" - } if component.icon %> + <% if component.icon.present? %> +
+ <%= sage_component SageIcon, { + color: "white", + icon: component.icon, + size: variant == 'compact' ? 'xl' : '3xl', + css_classes: "sage-empty-state__icon" + }%> +
+ <% end %>
<% if component.title.present? %> diff --git a/packages/sage-assets/lib/stylesheets/components/_empty_state.scss b/packages/sage-assets/lib/stylesheets/components/_empty_state.scss index effb269696..e2c15fb632 100644 --- a/packages/sage-assets/lib/stylesheets/components/_empty_state.scss +++ b/packages/sage-assets/lib/stylesheets/components/_empty_state.scss @@ -4,28 +4,23 @@ /// @group sage //// -$-empty-state-graphic-feature-width: 100%; -$-empty-state-graphic-feature-height: rem(142px); -$-empty-state-graphic-page-width: 100%; -$-empty-state-graphic-page-height: rem(336px); -$-empty-state-page-max-width: rem(1064px); +$-empty-state-graphic-default-width: 100%; +$-empty-state-graphic-default-height: rem(104px); +$-empty-state-graphic-compact-width: 100%; +$-empty-state-graphic-compact-height: rem(56px); +$-empty-state-icon-background-color: var(--color-background-icon); +$-empty-state-icon-size: rem(104px); +$-empty-state-icon-compact-size: rem(56px); .sage-empty-state { - margin-left: auto; - margin-right: auto; - - &:not(.sage-empty-state--page) { - @include sage-grid-panel(); - - max-width: sage-container(md); - text-align: center; - justify-items: center; - } - - &:not(.sage-empty-state--compact):not(.sage-empty-state--page) { - gap: sage-spacing(); - padding: sage-spacing(2xl) sage-spacing(); - } + @include sage-grid-panel(); + + gap: sage-spacing(sm); + margin-inline-start: auto; + margin-inline-end: auto; + max-width: sage-container(md); + text-align: center; + justify-items: center; } .sage-empty-state--center { @@ -34,48 +29,12 @@ $-empty-state-page-max-width: rem(1064px); top: 50%; transform: translate(-50%, -50%); padding: sage-spacing(); - - @media screen and (min-width: sage-breakpoint(lg-max)) { - padding: 0; - } -} - -.sage-empty-state--compact { - padding: sage-spacing(xs) sage-spacing(); -} - -.sage-empty-state--page { - display: flex; - align-items: center; - justify-content: center; - gap: sage-spacing(); - width: 100%; - padding: sage-spacing(lg); - - @media screen and (max-width: sage-breakpoint(lg-max)) { - flex-direction: column; - } - - @media screen and (min-width: sage-breakpoint(xl-min)) { - flex-direction: row; - max-width: $-empty-state-page-max-width; - } } .sage-empty-state__actions, .sage-empty-state__custom-content, .sage-empty-state__video { - margin-top: sage-spacing(); -} - -.sage-empty-state__content { - .sage-empty-state--page & { - max-width: $-empty-state-graphic-page-width; - - @media screen and (min-width: sage-breakpoint(xl-min)) { - order: 1; - } - } + margin-block-start: sage-spacing(); } .sage-empty-state__graphic { @@ -83,8 +42,8 @@ $-empty-state-page-max-width: rem(1064px); align-items: center; justify-content: center; overflow: hidden; - width: $-empty-state-graphic-feature-width; - height: $-empty-state-graphic-feature-height; + inline-size: $-empty-state-graphic-default-width; + block-size: $-empty-state-graphic-default-height; > img { display: block; @@ -93,39 +52,47 @@ $-empty-state-page-max-width: rem(1064px); border-radius: sage-border(radius); } - .sage-empty-state--page & { - max-width: $-empty-state-graphic-page-width; - height: $-empty-state-graphic-page-height; + .sage-empty-state--compact & { + block-size: $-empty-state-graphic-compact-height; + inline-size: $-empty-state-graphic-compact-width; + } +} + +.sage-empty-state__icon-container { + display: flex; + padding: sage-spacing(lg); + background-color: $-empty-state-icon-background-color; + border-radius: sage-border(radius-round); - @media screen and (min-width: sage-breakpoint(xl-min)) { - order: 2; - } + .sage-empty-state--compact & { + padding: sage-spacing(sm); } } .sage-empty-state__icon { display: inline-flex; - color: sage-color(charcoal, 100); + inline-size: $-empty-state-icon-size; + block-size: $-empty-state-icon-size; } .sage-empty-state__title { - @extend %t-sage-heading-4; + @extend %t-sage-heading-3; - color: sage-color(charcoal, 500); + color: sage-color(grey, 90); word-wrap: break-word; - .sage-empty-state--page & { - @extend %t-sage-heading-1; - } - &:not(:only-child) { - margin-bottom: sage-spacing(sm); + margin-block-end: sage-spacing(sm); } } .sage-empty-state__text { @extend %t-sage-body; - color: sage-color(charcoal, 300); word-wrap: break-word; } + +.sage-empty-state__text, +.sage-empty-state__custom-content { + color: sage-color(grey, 70); +} diff --git a/packages/sage-assets/lib/stylesheets/components/_icon_card.scss b/packages/sage-assets/lib/stylesheets/components/_icon_card.scss index 04ede8922a..ece871d7fd 100644 --- a/packages/sage-assets/lib/stylesheets/components/_icon_card.scss +++ b/packages/sage-assets/lib/stylesheets/components/_icon_card.scss @@ -29,6 +29,7 @@ --background-color: #{sage-color-combo($-color, default, background)}; } } + .sage-icon-card--round { border-radius: sage-border(radius-round); } diff --git a/packages/sage-react/lib/EmptyState/EmptyState.jsx b/packages/sage-react/lib/EmptyState/EmptyState.jsx index e98bb81e1f..eb56246d4b 100644 --- a/packages/sage-react/lib/EmptyState/EmptyState.jsx +++ b/packages/sage-react/lib/EmptyState/EmptyState.jsx @@ -4,7 +4,7 @@ import classnames from 'classnames'; import { SageTokens } from '../configs'; import { Button } from '../Button'; import { Icon } from '../Icon'; -import { EMPTY_STATE_SCOPES } from './configs'; +import { EMPTY_STATE_SIZES } from './configs'; export const EmptyState = ({ actions, @@ -12,7 +12,8 @@ export const EmptyState = ({ children, graphic, icon, - scope, + backgroundColor, + size, text, title, titleTag, @@ -23,7 +24,7 @@ export const EmptyState = ({ 'sage-empty-state', { 'sage-empty-state--center': centerVertical, - [`sage-empty-state--${scope}`]: scope, + [`sage-empty-state--${size}`]: size, }, ); @@ -39,7 +40,19 @@ export const EmptyState = ({ {graphic}
)} - {icon && ()} + {icon && ( +
+ +
+ )}
{title && ( @@ -59,7 +72,10 @@ export const EmptyState = ({ )} {actions && (
- + {actions}
@@ -75,7 +91,7 @@ export const EmptyState = ({ ); }; -EmptyState.SCOPES = EMPTY_STATE_SCOPES; +EmptyState.SIZES = EMPTY_STATE_SIZES; EmptyState.defaultProps = { actions: null, @@ -83,7 +99,8 @@ EmptyState.defaultProps = { children: null, graphic: null, icon: null, - scope: EmptyState.SCOPES.DEFAULT, + backgroundColor: null, + size: EmptyState.SIZES.DEFAULT, text: null, title: null, titleTag: 'h2', @@ -91,14 +108,48 @@ EmptyState.defaultProps = { }; EmptyState.propTypes = { + /** + * Slot into which buttons or other actions can be placed. + */ actions: PropTypes.node, + /** + * If true, the Empty State will be visually centered inside the entire page context. + */ centerVertical: PropTypes.bool, + /** + * The content to be rendered within the Empty State. + */ children: PropTypes.node, + /** + * Adds a graphic above the content. + */ graphic: PropTypes.node, + /** + * Adds an icon above the content. + */ icon: PropTypes.oneOf(Object.values(SageTokens.ICONS)), - scope: PropTypes.oneOf(Object.values(EmptyState.SCOPES)), + /** + * Sets the background color of the icon container. Defaults to Mercury 30 + */ + backgroundColor: PropTypes.string, + /** + * The size and context of the Empty State. + */ + size: PropTypes.oneOf(Object.values(EmptyState.SIZES)), + /** + * Sets the text for the Empty State. + */ text: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + /** + * Sets the title for the Empty State. + */ title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + /** + * Sets which HTML heading tag to use on the title. + */ titleTag: PropTypes.oneOf(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']), + /** + * Slot into which video cards or other media can be placed. + */ video: PropTypes.node, }; diff --git a/packages/sage-react/lib/EmptyState/EmptyState.story.jsx b/packages/sage-react/lib/EmptyState/EmptyState.story.jsx deleted file mode 100644 index 23c7bd7b41..0000000000 --- a/packages/sage-react/lib/EmptyState/EmptyState.story.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { selectArgs } from '../story-support/helpers'; -import { Button } from '../Button'; -import { SageTokens } from '../configs'; -import { EmptyState } from './EmptyState'; - -export default { - title: 'Sage/EmptyState', - component: EmptyState, - // displays description on Docs tab - parameters: { - docs: { - description: { - component: 'The Empty State is displayed for main application features that have never been interacted with before. The Empty State is also used for smaller features in the app that primarily focus on data entry and have no data to show.' - }, - }, - }, - argTypes: { - ...selectArgs({ - icon: SageTokens.ICONS, - scope: EmptyState.SCOPES - }) - }, - args: { - actions: ( - <> - - - - - ), - icon: SageTokens.ICONS.GEAR, - text: 'Text Here', - title: 'Title Here' - } -}; - -const Template = (args) => ; - -export const Default = Template.bind({}); - -export const PageScope = Template.bind({}); -PageScope.args = { - icon: null, - graphic: (), - scope: EmptyState.SCOPES.PAGE, - text: ( -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Elit arcu volutpat cursus ultricies ac, ultricies. - Platea sed nibh molestie ut. -

- ), - title: 'Create your first Email Campaign', - titleTag: 'h1', - children: null, - actions: ( - <> - - - - ) -}; diff --git a/packages/sage-react/lib/EmptyState/EmptyState.story.mdx b/packages/sage-react/lib/EmptyState/EmptyState.story.mdx new file mode 100644 index 0000000000..4bf2bfcc59 --- /dev/null +++ b/packages/sage-react/lib/EmptyState/EmptyState.story.mdx @@ -0,0 +1,92 @@ +import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks'; +import { Button } from '../Button'; +import { SageTokens } from '../configs'; +import { EmptyState } from './EmptyState'; + + + +# Empty State + +The Empty State is displayed for main application features that have never been interacted with before. The Empty State is also used for smaller features in the app that primarily focus on data entry and have no data to show. + +## Accessibility + +Ensure images or graphics that are used in the Empty State component *do not* include `alt` text so that the image remains decorative and invisible to screen readers. + +## Properties + + + +## Size + +The Empty State component has two sizes: default and `compact`. The default size is meant to be used for whole-page empty states and is intended to fill the stage. The compact size features a smaller icon and is intended to fill empty states for smaller contexts. + +### Default + + + + + + + + )} + icon={SageTokens.ICONS.PEN} + title="Create your first Email Campaign" + titleTag="h1" + text={(

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Elit arcu volutpat cursus ultricies ac, ultricies. + Platea sed nibh molestie ut. +

)} + /> +
+
+ +### Compact + + + + + + + + )} + icon={SageTokens.ICONS.PEN} + size={EmptyState.SIZES.COMPACT} + title="Create your first Email Campaign" + titleTag="h1" + text={(

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Elit arcu volutpat cursus ultricies ac, ultricies. + Platea sed nibh molestie ut. +

)} + /> +
+
+ +### With Graphic + + + + + + + + )} + graphic={()} + title="Create your first Email Campaign" + titleTag="h1" + text={(

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Elit arcu volutpat cursus ultricies ac, ultricies. + Platea sed nibh molestie ut. +

)} + /> +
+
\ No newline at end of file diff --git a/packages/sage-react/lib/EmptyState/configs.js b/packages/sage-react/lib/EmptyState/configs.js index 6550148d23..75df4b370d 100644 --- a/packages/sage-react/lib/EmptyState/configs.js +++ b/packages/sage-react/lib/EmptyState/configs.js @@ -1,5 +1,4 @@ -export const EMPTY_STATE_SCOPES = { +export const EMPTY_STATE_SIZES = { DEFAULT: null, - PAGE: 'page', COMPACT: 'compact', };