diff --git a/packages/carbon-react/src/components/Notification/Notification.stories.js b/packages/carbon-react/src/components/Notification/Notification.stories.js new file mode 100644 index 000000000000..97a5e3a6eb4b --- /dev/null +++ b/packages/carbon-react/src/components/Notification/Notification.stories.js @@ -0,0 +1,45 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { + ToastNotification, + InlineNotification, + NotificationActionButton, +} from 'carbon-components-react'; +import React from 'react'; + +const notificationProps = () => ({ + kind: 'info', + role: 'alert', + title: 'Notification title', + subtitle: 'Subtitle text goes here.', +}); + +const toastNotificationProps = () => ({ + ...notificationProps(), +}); + +export default { + title: 'Components/Notifications', +}; + +export const Toast = () => ( + +); + +export const Inline = () => ( + {'Action'}} + /> +); + +Inline.storyName = 'Inline'; diff --git a/packages/carbon-react/src/components/Notification/index.js b/packages/carbon-react/src/components/Notification/index.js new file mode 100644 index 000000000000..0daee3af6fd9 --- /dev/null +++ b/packages/carbon-react/src/components/Notification/index.js @@ -0,0 +1,12 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +export { + ToastNotification, + InlineNotification, + NotificationActionButton, +} from 'carbon-components-react'; diff --git a/packages/styles/scss/components/__tests__/inline-notification-test.js b/packages/styles/scss/components/__tests__/inline-notification-test.js new file mode 100644 index 000000000000..6426c4832a84 --- /dev/null +++ b/packages/styles/scss/components/__tests__/inline-notification-test.js @@ -0,0 +1,27 @@ +/** + * Copyright IBM Corp. 2018, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + * + * @jest-environment node + */ + +'use strict'; + +const { SassRenderer } = require('@carbon/test-utils/scss'); + +const { render } = SassRenderer.create(__dirname); + +describe('scss/components/notification', () => { + test('Public API', async () => { + const { unwrap } = await render(` + @use 'sass:map'; + @use 'sass:meta'; + @use '../notification'; + + $_: get('mixin', meta.mixin-exists('inline-notification', 'notification')); + `); + expect(unwrap('mixin')).toBe(true); + }); +}); diff --git a/packages/styles/scss/components/__tests__/toast-notification-test.js b/packages/styles/scss/components/__tests__/toast-notification-test.js new file mode 100644 index 000000000000..f688a3ad9bf7 --- /dev/null +++ b/packages/styles/scss/components/__tests__/toast-notification-test.js @@ -0,0 +1,27 @@ +/** + * Copyright IBM Corp. 2018, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + * + * @jest-environment node + */ + +'use strict'; + +const { SassRenderer } = require('@carbon/test-utils/scss'); + +const { render } = SassRenderer.create(__dirname); + +describe('scss/components/notification', () => { + test('Public API', async () => { + const { unwrap } = await render(` + @use 'sass:map'; + @use 'sass:meta'; + @use '../notification'; + + $_: get('mixin', meta.mixin-exists('toast-notification', 'notification')); + `); + expect(unwrap('mixin')).toBe(true); + }); +}); diff --git a/packages/styles/scss/components/_index.scss b/packages/styles/scss/components/_index.scss index 86119c4127ba..b1e927c8f1f6 100644 --- a/packages/styles/scss/components/_index.scss +++ b/packages/styles/scss/components/_index.scss @@ -21,6 +21,7 @@ @use 'tooltip'; @use 'radio-button'; @use 'modal'; +@use 'notification'; @use 'search'; @use 'data-table'; @use 'data-table/action'; diff --git a/packages/styles/scss/components/notification/_index.scss b/packages/styles/scss/components/notification/_index.scss index b4622b04e767..d281b0723465 100644 --- a/packages/styles/scss/components/notification/_index.scss +++ b/packages/styles/scss/components/notification/_index.scss @@ -5,4 +5,12 @@ // LICENSE file in the root directory of this source tree. // -@forward 'tokens'; +@forward './tokens'; +@forward 'inline-notification'; +@forward 'toast-notification'; + +@use 'inline-notification'; +@use 'toast-notification'; + +@include inline-notification.inline-notification; +@include toast-notification.toast-notification; diff --git a/packages/styles/scss/components/notification/_inline-notification.scss b/packages/styles/scss/components/notification/_inline-notification.scss new file mode 100644 index 000000000000..fc57355905a3 --- /dev/null +++ b/packages/styles/scss/components/notification/_inline-notification.scss @@ -0,0 +1,325 @@ +// +// Copyright IBM Corp. 2016, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// +@use 'mixins' as *; +@use '../../breakpoint' as *; +@use '../../colors' as *; +@use '../../config' as *; +@use '../../feature-flags' as *; +@use '../../motion' as *; +@use '../../spacing' as *; +@use '../../theme' as *; +@use '../../themes' as *; +@use '../../type' as *; +@use '../../utilities/convert' as *; +@use '../../utilities/component-tokens' as *; +@use '../../utilities/high-contrast-mode' as *; +@use '../../utilities/focus-outline' as *; + +/// Inline notification styles +/// @access public +/// @group notification +@mixin inline-notification { + .#{$prefix}--inline-notification { + @include reset; + + position: relative; + display: flex; + width: 100%; + min-width: rem(288px); + max-width: rem(288px); + height: auto; + min-height: rem(48px); + flex-wrap: wrap; + margin-top: $spacing-05; + margin-bottom: $spacing-05; + color: $text-inverse; + + @include breakpoint(md) { + max-width: rem(608px); + flex-wrap: nowrap; + } + + @include breakpoint(lg) { + max-width: rem(736px); + } + + @include breakpoint(max) { + max-width: rem(832px); + } + } + + .#{$prefix}--inline-notification:not(.#{$prefix}--inline-notification--low-contrast) + a { + color: $link-inverse; + } + + .#{$prefix}--inline-notification a { + text-decoration: none; + } + + .#{$prefix}--inline-notification a:hover { + text-decoration: underline; + } + + .#{$prefix}--inline-notification a:focus { + outline: 1px solid $link-inverse; + } + + .#{$prefix}--inline-notification.#{$prefix}--inline-notification--low-contrast + a:focus { + @include focus-outline; + } + + .#{$prefix}--inline-notification--low-contrast { + color: $text-primary; + + &::before { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + border-width: 1px 1px 1px 0; + border-style: solid; + content: ''; + filter: opacity(0.4); + pointer-events: none; + } + } + + .#{$prefix}--inline-notification--error { + @include notification--experimental( + $support-error-inverse, + $background-inverse + ); + } + + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--error { + @include notification--experimental( + $support-error, + get-token(var(--cds-background-error)) + ); + + &::before { + border-color: $support-error; + } + } + + .#{$prefix}--inline-notification--success { + @include notification--experimental( + $support-success-inverse, + $background-inverse + ); + } + + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--success { + @include notification--experimental( + $support-success, + get-token-value(var(--cds-background-success)) + ); + + &::before { + border-color: $support-success; + } + } + + .#{$prefix}--inline-notification--info, + .#{$prefix}--inline-notification--info-square { + @include notification--experimental( + $support-info-inverse, + $background-inverse + ); + } + + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--info, + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--info-square { + @include notification--experimental( + $support-info, + get-token-value(var(--cds-background-info)) + ); + + &::before { + border-color: $support-info; + } + } + + .#{$prefix}--inline-notification--warning, + .#{$prefix}--inline-notification--warning-alt { + @include notification--experimental( + $support-warning-inverse, + $background-inverse + ); + } + + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--warning, + .#{$prefix}--inline-notification--low-contrast.#{$prefix}--inline-notification--warning-alt { + @include notification--experimental( + $support-warning, + get-token-value(var(--cds-background-warning)) + ); + + &::before { + border-color: $support-warning; + } + } + + .#{$prefix}--inline-notification--warning + .#{$prefix}--inline-notification__icon + path[opacity='0'] { + fill: $black-100; + opacity: 1; + } + + .#{$prefix}--inline-notification__details { + display: flex; + flex-grow: 1; + margin: 0 $spacing-09 0 $spacing-05; + + @include breakpoint(md) { + margin: 0 $spacing-05; + } + } + + .#{$prefix}--inline-notification__icon { + flex-shrink: 0; + margin-top: rem(14px); + margin-right: $spacing-05; + } + + .#{$prefix}--inline-notification__text-wrapper { + display: flex; + flex-wrap: wrap; + padding: rem(15px) 0; + } + + @if enabled('enable-v11-release') { + .#{$prefix}--inline-notification__content { + @include type-style('body-short-01'); + + word-break: break-word; + } + } @else { + .#{$prefix}--inline-notification__title { + @include type-style('productive-heading-01'); + + margin: 0 $spacing-02 0 0; + } + + .#{$prefix}--inline-notification__subtitle { + @include type-style('body-short-01'); + + word-break: break-word; + } + } + + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost { + height: rem(32px); + margin-bottom: $spacing-03; + margin-left: $spacing-08; + + @include breakpoint(md) { + margin: $spacing-03 0; + } + } + + .#{$prefix}--inline-notification:not(.#{$prefix}--inline-notification--low-contrast) + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost { + color: $link-inverse; + } + + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:active, + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:hover { + background-color: $background-inverse-hover; + } + + .#{$prefix}--inline-notification--low-contrast + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:active, + .#{$prefix}--inline-notification--low-contrast + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:hover { + background-color: get-token-value(var(--cds-action-hover)); + } + + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:focus { + border-color: transparent; + box-shadow: none; + outline: 2px solid $focus-inverse; + outline-offset: -2px; + } + + .#{$prefix}--inline-notification--low-contrast + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost:focus { + outline-color: $focus; + } + + .#{$prefix}--inline-notification--hide-close-button + .#{$prefix}--inline-notification__action-button.#{$prefix}--btn--ghost { + margin-right: $spacing-03; + } + + .#{$prefix}--inline-notification__close-button { + @include focus-outline('reset'); + + position: absolute; + top: 0; + right: 0; + display: flex; + width: rem(48px); + min-width: rem(48px); + max-width: rem(48px); + height: rem(48px); + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + transition: outline $duration-fast-02 motion(standard, productive), + background-color $duration-fast-02 motion(standard, productive); + + &:focus { + outline: 2px solid $focus-inverse; + outline-offset: -2px; + } + + .#{$prefix}--inline-notification__close-icon { + fill: $icon-inverse; + } + + @include breakpoint(md) { + position: static; + } + } + + .#{$prefix}--inline-notification--low-contrast + .#{$prefix}--inline-notification__close-button:focus { + @include focus-outline('outline'); + } + + .#{$prefix}--inline-notification--low-contrast + .#{$prefix}--inline-notification__close-button + .#{$prefix}--inline-notification__close-icon { + fill: $icon-primary; + } + + // Windows HCM fix + /* stylelint-disable */ + .#{$prefix}--inline-notification { + @include high-contrast-mode('outline'); + } + + .#{$prefix}--inline-notification__close-button:focus, + .#{$prefix}--btn.#{$prefix}--btn--ghost.#{$prefix}--inline-notification__action-button:focus { + @include high-contrast-mode('focus'); + } + + .#{$prefix}--inline-notification__icon { + @include high-contrast-mode('icon-fill'); + } + /* stylelint-enable */ +} diff --git a/packages/styles/scss/components/notification/_mixins.scss b/packages/styles/scss/components/notification/_mixins.scss new file mode 100644 index 000000000000..3e6930e063e8 --- /dev/null +++ b/packages/styles/scss/components/notification/_mixins.scss @@ -0,0 +1,41 @@ +// +// Copyright IBM Corp. 2016, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@use '../../config' as *; + +/// @access public +/// @group notification +@mixin inline-notification--color($color) { + border: 1px solid $color; + border-left: 6px solid $color; + + .#{$prefix}--inline-notification__icon { + fill: $color; + } +} + +//---------------------------------------------- +// Toast Notification +// --------------------------------------------- + +/// @access private +/// @group notification +@mixin notification--color($color) { + border-left: 6px solid $color; +} + +/// @access private +/// @group notification +@mixin notification--experimental($color, $background-color) { + border-left: 3px solid $color; + background: $background-color; + + .#{$prefix}--inline-notification__icon, + .#{$prefix}--toast-notification__icon { + fill: $color; + } +} diff --git a/packages/styles/scss/components/notification/_toast-notification.scss b/packages/styles/scss/components/notification/_toast-notification.scss new file mode 100644 index 000000000000..552a79879cda --- /dev/null +++ b/packages/styles/scss/components/notification/_toast-notification.scss @@ -0,0 +1,256 @@ +// +// Copyright IBM Corp. 2016, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@use 'mixins' as*; +@use 'tokens' as *; +@use '../../breakpoint' as *; +@use '../../colors' as *; +@use '../../config' as *; +@use '../../feature-flags' as *; +@use '../../motion' as *; +@use '../../spacing' as *; +@use '../../theme' as *; +@use '../../themes' as *; +@use '../../type' as *; +@use '../../utilities/convert' as *; +@use '../../utilities/high-contrast-mode' as *; +@use '../../utilities/focus-outline' as *; + +/// Toast notification styles +/// @access public +/// @group notification +@mixin toast-notification { + .#{$prefix}--toast-notification { + @include reset; + + display: flex; + width: rem(288px); + height: auto; + padding-left: $spacing-05; + margin-top: $spacing-03; + margin-right: $spacing-05; + margin-bottom: $spacing-03; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2); + color: $text-inverse; + + &:first-child { + margin-top: $spacing-05; + } + + @include breakpoint(max) { + width: rem(352px); + } + } + + .#{$prefix}--toast-notification:not(.#{$prefix}--toast-notification--low-contrast) + a { + color: $link-inverse; + } + + .#{$prefix}--toast-notification a { + text-decoration: none; + } + + .#{$prefix}--toast-notification a:hover { + text-decoration: underline; + } + + .#{$prefix}--toast-notification a:focus { + outline: 1px solid $link-inverse; + } + + .#{$prefix}--toast-notification.#{$prefix}--toast-notification--low-contrast + a:focus { + @include focus-outline; + } + + .#{$prefix}--toast-notification--low-contrast { + color: $text-primary; + } + + .#{$prefix}--toast-notification--error { + @include notification--experimental( + $support-error-inverse, + $background-inverse + ); + } + + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--error { + @include notification--experimental( + $support-error, + get-token(var(--cds-background-error)) + ); + } + + .#{$prefix}--toast-notification--success { + @include notification--experimental( + $support-success-inverse, + $background-inverse + ); + } + + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--success { + @include notification--experimental( + $support-success, + get-token(var(--cds-background-success)) + ); + } + + .#{$prefix}--toast-notification--info, + .#{$prefix}--toast-notification--info-square { + @include notification--experimental( + $support-info-inverse, + $background-inverse + ); + } + + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--info, + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--info-square { + @include notification--experimental( + $support-info, + get-token(var(--cds-background-info)) + ); + } + + .#{$prefix}--toast-notification--warning, + .#{$prefix}--toast-notification--warning-alt { + @include notification--experimental( + $support-warning-inverse, + $background-inverse + ); + } + + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--warning, + .#{$prefix}--toast-notification--low-contrast.#{$prefix}--toast-notification--warning-alt { + @include notification--experimental( + $support-warning, + get-token(var(--cds-background-warning)) + ); + } + + .#{$prefix}--toast-notification--warning + .#{$prefix}--toast-notification__icon + path[opacity='0'] { + fill: $black-100; + opacity: 1; + } + + .#{$prefix}--toast-notification__icon { + flex-shrink: 0; + margin-top: $spacing-05; + margin-right: $spacing-05; + } + + @if enabled('enable-v11-release') { + .#{$prefix}--toast-notification__content { + @include type-style('body-short-01'); + + margin-top: $spacing-05; + margin-right: $spacing-05; + margin-bottom: $spacing-05; + word-break: break-word; + } + + .#{$prefix}--toast-notification--low-contrast + .#{$prefix}--toast-notification__content { + color: $text-primary; + } + } @else { + .#{$prefix}--toast-notification__details { + margin-right: $spacing-05; + } + } + + .#{$prefix}--toast-notification__close-button { + @include focus-outline('reset'); + + display: flex; + width: rem(48px); + min-width: rem(48px); + height: rem(48px); + min-height: rem(48px); + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0; + border: none; + margin-left: auto; + background-color: transparent; + cursor: pointer; + transition: outline $transition--base, background-color $transition--base; + + &:focus { + outline: 2px solid $focus-inverse; + outline-offset: -2px; + } + + .#{$prefix}--toast-notification__close-icon { + fill: $icon-inverse; + } + } + + .#{$prefix}--toast-notification--low-contrast + .#{$prefix}--toast-notification__close-button:focus { + @include focus-outline('outline'); + } + + .#{$prefix}--toast-notification--low-contrast + .#{$prefix}--toast-notification__close-button + .#{$prefix}--toast-notification__close-icon { + fill: $icon-primary; + } + + @if not enabled('enable-v11-release') { + .#{$prefix}--toast-notification__title { + @include type-style('productive-heading-01'); + + margin-top: $spacing-05; + font-weight: 600; + word-break: break-word; + } + + .#{$prefix}--toast-notification__subtitle { + @include type-style('body-short-01'); + + margin-top: 0; + margin-bottom: $spacing-05; + color: $text-inverse; + word-break: break-word; + } + + .#{$prefix}--toast-notification--low-contrast + .#{$prefix}--toast-notification__subtitle { + color: $text-primary; + } + + .#{$prefix}--toast-notification__caption { + @include type-style('body-short-01'); + + padding-top: $spacing-03; + margin-bottom: $spacing-05; + color: $text-inverse; + } + + .#{$prefix}--toast-notification--low-contrast + .#{$prefix}--toast-notification__caption { + color: $text-primary; + } + } + + // Windows HCM fix + /* stylelint-disable */ + .#{$prefix}--toast-notification { + @include high-contrast-mode('outline'); + } + .#{$prefix}--toast-notification__close-button:focus { + @include high-contrast-mode('focus'); + } + .#{$prefix}--toast-notification__icon { + @include high-contrast-mode('icon-fill'); + } + /* stylelint-enable */ +}