From 2bd44b559e3b77387bc886480136cb9c7d7d4167 Mon Sep 17 00:00:00 2001 From: Chris Fisher Date: Mon, 24 Sep 2018 15:31:51 -0500 Subject: [PATCH 01/10] feat(accordion): Add experimental accordion (#1080) * feat: adding duo styles for experimental-accordion * feat: added flex styles to match duo-styling * feat: update styles based on design sketch file * chore: remove commented styles * chore: add typescale includes * feat: updated styles based on design review * chore: remove commented code * chore: remove unneeded comments * feat: updated section header styles * feat: updated styles for accordion based on design review --- src/components/accordion/_accordion.scss | 148 ++++++++++++++++++++++- src/globals/scss/_theme.scss | 6 +- 2 files changed, 150 insertions(+), 4 deletions(-) diff --git a/src/components/accordion/_accordion.scss b/src/components/accordion/_accordion.scss index a8ae34eea4b5..8470570efd66 100644 --- a/src/components/accordion/_accordion.scss +++ b/src/components/accordion/_accordion.scss @@ -4,11 +4,12 @@ @import '../../globals/scss/typography'; @import '../../globals/scss/spacing'; @import '../../globals/scss/helper-mixins'; +@import '../../globals/scss/functions'; @import '../../globals/scss/import-once'; @import '../../globals/scss/css--reset'; @import '../../globals/scss/css--typography'; -@include exports('accordion') { +@mixin accordion { .#{$prefix}--accordion { @include reset; @include font-family; @@ -137,3 +138,148 @@ margin-bottom: 0; } } + +@mixin accordion--x { + .#{$prefix}--accordion { + @include reset; + @include font-family; + list-style: none; + width: 100%; + } + + .#{$prefix}--accordion__item { + transition: all $transition--base $carbon--standard-easing; + border-top: 1px solid $ui-03; + overflow: hidden; + + &:focus { + outline: none; + + .#{$prefix}--accordion__arrow { + @include focus-outline('border'); + overflow: visible; // safari fix + outline-offset: -0.5px; // safari fix + } + } + + &:last-child { + border-bottom: 1px solid $ui-03; + } + } + + .#{$prefix}--accordion__heading { + @include button-reset; + color: $text-01; + display: flex; + align-items: center; + justify-content: $accordion-justify-content; + cursor: pointer; + padding: rem(7px) 0; + flex-direction: $accordion-flex-direction; + width: 100%; + margin: 0; // safari fix + + &:focus { + outline: none; + + .#{$prefix}--accordion__arrow { + @include focus-outline('border'); + overflow: visible; // safari fix + outline-offset: -0.5px; // safari fix + } + } + &:hover { + background-color: $field-01; + } + } + + .#{$prefix}--accordion__arrow { + transition: all $transition--base $carbon--standard-easing; + height: 1.25rem; + width: 1.25rem; + padding: $spacing-2xs $spacing-3xs $spacing-2xs $spacing-2xs; + margin: $accordion-arrow-margin; + fill: $ui-05; + transform: rotate(90deg) /*rtl:rotate(180deg)*/; + } + + .#{$prefix}--accordion__title { + @include typescale('zeta'); + @include line-height('body'); + margin: $accordion-title-margin; + width: 100%; + text-align: left; + font-weight: 400; + } + + .#{$prefix}--accordion__content { + transition: all $transition--expansion $carbon--ease-out; + padding-left: $spacing-md; + height: 0; + visibility: hidden; + opacity: 0; + + p { + @include typescale('zeta'); + padding-right: 20%; + } + } + + .#{$prefix}--accordion__item--active { + overflow: visible; + border-top: 1px solid transparent; + + .#{$prefix}--accordion__content { + padding-bottom: $spacing-lg; + padding-top: $spacing-xs; + height: auto; + visibility: visible; + opacity: 1; + transition: all $transition--expansion $carbon--ease-in; + } + + .#{$prefix}--accordion__arrow { + /*rtl:ignore*/ + transform: rotate(-90deg); + fill: $ui-05; + } + } + + // Skeleton state + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__heading, + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__button { + cursor: default; + } + + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__arrow { + pointer-events: none; + fill: $ui-05; + cursor: default; + + &:hover, + &:focus, + &:active { + border: none; + outline: none; + cursor: default; + } + } + + .#{$prefix}--skeleton .#{$prefix}--accordion__heading:focus .#{$prefix}--accordion__arrow { + border: none; + outline: none; + cursor: default; + } + + .#{$prefix}--accordion__title.#{$prefix}--skeleton__text { + margin-bottom: 0; + } +} + +@include exports('accordion') { + @if feature-flag-enabled('components-x') { + @include accordion--x; + } @else { + @include accordion; + } +} diff --git a/src/globals/scss/_theme.scss b/src/globals/scss/_theme.scss index fcb7c3880998..6ca274177ed5 100644 --- a/src/globals/scss/_theme.scss +++ b/src/globals/scss/_theme.scss @@ -161,11 +161,11 @@ $button-outline: 1px solid $ibm-colors__white !default !global; // Accordion (Reverse) - $accordion-flex-direction: row !default !global; + $accordion-flex-direction: row-reverse !default !global; $accordion-justify-content: flex-start !default !global; - $accordion-arrow-margin: 0 0 0 $spacing-2xs !default !global; + $accordion-arrow-margin: 0 $spacing-md 0 0 !default !global; $accordion-title-margin: 0 0 0 $spacing-md !default !global; - $accordion-content-padding: 0 $spacing-md 0 $spacing-2xl !default !global; + $accordion-content-padding: 0 0 0 $spacing-md !default !global; // Card $card-text-align: center !default !global; From 59fbdb34a9d99a90691f6c2171837cb53e975211 Mon Sep 17 00:00:00 2001 From: Akira Sudoh Date: Wed, 26 Sep 2018 00:25:25 +0900 Subject: [PATCH 02/10] fix(devenv): fix theme switcher for FF (#1139) Fixes #1136 --- demo/js/components/RootPage.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/demo/js/components/RootPage.js b/demo/js/components/RootPage.js index 379d39a95bbf..3e9140f8b19f 100644 --- a/demo/js/components/RootPage.js +++ b/demo/js/components/RootPage.js @@ -237,11 +237,18 @@ class RootPage extends Component { clearTimeout(this.hTimeoutDetectExperimental); this.hTimeoutDetectExperimental = null; } - // For IE11 - if (link.sheet.cssRules.length === 0) { + let rulesLength = 0; + try { + // https://bugzilla.mozilla.org/show_bug.cgi?id=761236#c1 suggests that `NS_ERROR_DOM_INVALID_ACCESS_ERR` is thrown + // if we try to read `.cssRules` on a stylesheet that's not done being parsed yet + rulesLength = link.sheet.cssRules.length; + } catch (err) {} // eslint-disable-line no-empty + // For IE11/FF + if (rulesLength === 0) { this.hTimeoutDetectExperimental = setTimeout(() => { this._detectComponentsX(link); }, 100); + return; } const isComponentsX = Array.prototype.some.call( link.sheet.cssRules, From 4373805131c6a927e18c6b2ac8e06ed6251d3dab Mon Sep 17 00:00:00 2001 From: Akira Sudoh Date: Wed, 26 Sep 2018 00:34:32 +0900 Subject: [PATCH 03/10] feat(components): complete parameterizing selectors (#1140) Fixes #1138. --- src/components/checkbox/README.md | 26 +++++---- src/components/checkbox/checkbox.js | 63 +++++++++++++-------- src/components/code-snippet/code-snippet.js | 24 ++++---- src/components/date-picker/date-picker.js | 2 +- tests/spec/checkbox_spec.js | 6 ++ 5 files changed, 76 insertions(+), 45 deletions(-) diff --git a/src/components/checkbox/README.md b/src/components/checkbox/README.md index 336a2f048e90..7408b63ac3ab 100644 --- a/src/components/checkbox/README.md +++ b/src/components/checkbox/README.md @@ -2,16 +2,22 @@ #### Public Methods -| Name | Params | Description | -|-----------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------| -| setState | state: `String` ['true', 'false', 'mixed'] | Can be used to set the checkbox to `true`(checked), `false`(unchecked) or `mixed` (indeterminate) | -| setDisabled | state: `Boolean` | Can be used to set the checkbox to disabled, needed for the `label > input` | +| Name | Params | Description | +| ----------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| setState | state: `String` ['true', 'false', 'mixed'] | Can be used to set the checkbox to `true`(checked), `false`(unchecked) or `mixed` (indeterminate) | +| setDisabled | state: `Boolean` | Can be used to set the checkbox to disabled, needed for the `label > input` | #### Options -| Option | Default Selector | Description | -|---------------------|------------------------------------------------|--| -| selectorInit | .bx--checkbox | The CSS selector to find checkbox | +| Option | Default Selector | Description | +| --------------------------------- | ---------------------------------- | -------------------------------------------------------------------------- | +| selectorInit | .bx--checkbox | The CSS selector to find checkbox | +| selectorContainedCheckboxState | [data-contained-checkbox-state] | The CSS selector to find a container of checkbox preserving checked state | +| selectorContainedCheckboxDisabled | [data-contained-checkbox-disabled] | The CSS selector to find a container of checkbox preserving disabled state | +| classLabel | .bx--checkbox-label | The CSS class for the label | +| classLabelFocused | .bx--checkbox-label\_\_focus | The CSS class for the focused label | +| attribContainedCheckboxState | data-contained-checkbox-state | The attribute name for the checked state of contained checkbox | +| attribContainedCheckboxDisabled | data-contained-checkbox-disabled | The attribute name for the disabled state of contained checkbox | ### FAQ @@ -41,14 +47,12 @@ With `label` wrapping `input` ``` -Note: You no longer need to include a SVG for the checkmark to render. +Note: You no longer need to include a SVG for the checkmark to render. #### Fieldset and Legend As a best practice, groups of checkboxes should make use of `
` and `` (see Form for details). This is especially true for forms submitting data. -But, there are exceptions to the rule. For example, Data Tables make use of checkboxes as a way to select rows of data. +But, there are exceptions to the rule. For example, Data Tables make use of checkboxes as a way to select rows of data. Checkboxes in this context would represent an entire row of data in its associated table row. - - diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js index 9c01116431c1..0743431eddc7 100644 --- a/src/components/checkbox/checkbox.js +++ b/src/components/checkbox/checkbox.js @@ -1,3 +1,4 @@ +import settings from '../../globals/js/settings'; import mixin from '../../globals/js/misc/mixin'; import createComponent from '../../globals/js/mixins/create-component'; import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; @@ -48,8 +49,8 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.checked = true; // nested checkboxes inside labels - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'true'); } } else if (this.element.checked === false) { this.element.removeAttribute('checked'); @@ -57,21 +58,21 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.checked = false; // nested checkboxes inside labels - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'false'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'false'); } } } _handleFocus() { - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.classList.add('bx--checkbox-label__focus'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.classList.add(this.options.classLabelFocused); } } _handleBlur() { - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.classList.remove('bx--checkbox-label__focus'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.classList.remove(this.options.classLabelFocused); } } @@ -90,9 +91,9 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.indeterminate = state === stateChangeTypes.mixed; this.element.checked = state === stateChangeTypes.true; - const container = this.element.closest('[data-contained-checkbox-state]'); + const container = this.element.closest(this.options.selectorContainedCheckboxState); if (container) { - container.setAttribute('data-contained-checkbox-state', state); + container.setAttribute(this.options.attribContainedCheckboxState, state); } } @@ -105,9 +106,9 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { } else if (value === false) { this.element.removeAttribute('disabled'); } - const container = this.element.closest('[data-contained-checkbox-disabled]'); + const container = this.element.closest(this.options.selectorContainedCheckboxDisabled); if (container) { - container.setAttribute('data-contained-checkbox-disabled', value); + container.setAttribute(this.options.attribContainedCheckboxDisabled, value); } } @@ -118,8 +119,8 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { if (this.element.indeterminate === true) { this.element.setAttribute('aria-checked', 'mixed'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.indeterminate === true) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'mixed'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.indeterminate === true) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'mixed'); } } @@ -127,14 +128,14 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { if (this.element.checked === true) { this.element.setAttribute('aria-checked', 'true'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.checked) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.checked) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'true'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-disabled', 'false'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxDisabled, 'false'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.disabled) { - this.element.parentElement.setAttribute('data-contained-checkbox-disabled', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.disabled) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxDisabled, 'true'); } } @@ -152,10 +153,26 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { * @member Checkbox.options * @type {Object} * @property {string} selectorInit The data attribute to find copy button UIs. + * @property {string} selectorContainedCheckboxState The CSS selector to find a container of checkbox preserving checked state. + * @property {string} selectorContainedCheckboxDisabled + * The CSS selector to find a container of checkbox preserving disabled state. + * @property {string} classLabel The CSS class for the label. + * @property {string} classLabelFocused The CSS class for the focused label. + * @property {string} attribContainedCheckboxState The attribute name for the checked state of contained checkbox. + * @property {string} attribContainedCheckboxDisabled The attribute name for the disabled state of contained checkbox. */ - static options = { - selectorInit: '.bx--checkbox', - }; + static get options() { + const { prefix } = settings; + return { + selectorInit: `.${prefix}--checkbox`, + selectorContainedCheckboxState: '[data-contained-checkbox-state]', + selectorContainedCheckboxDisabled: '[data-contained-checkbox-disabled]', + classLabel: `${prefix}--checkbox-label`, + classLabelFocused: `${prefix}--checkbox-label__focus`, + attribContainedCheckboxState: 'data-contained-checkbox-state', + attribContainedCheckboxDisabled: 'data-contained-checkbox-disabled', + }; + } static stateChangeTypes = stateChangeTypes; } diff --git a/src/components/code-snippet/code-snippet.js b/src/components/code-snippet/code-snippet.js index c3f02943a593..f64ca9cca3a2 100644 --- a/src/components/code-snippet/code-snippet.js +++ b/src/components/code-snippet/code-snippet.js @@ -1,3 +1,4 @@ +import settings from '../../globals/js/settings'; import mixin from '../../globals/js/misc/mixin'; import createComponent from '../../globals/js/mixins/create-component'; import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; @@ -60,16 +61,19 @@ class CodeSnippet extends mixin(createComponent, initComponentBySearch, handles) * @type {Object} * @property {string} selectorInit The data attribute to find code snippet UIs. */ - static options = { - selectorInit: '[data-code-snippet]', - attribShowMoreText: 'data-show-more-text', - attribShowLessText: 'data-show-less-text', - minHeight: 288, - classExpanded: 'bx--snippet--expand', - classExpandBtn: '.bx--snippet-btn--expand', - classExpandText: '.bx--snippet-btn--text', - classHideExpand: 'bx--snippet-btn--expand--hide', - }; + static get options() { + const { prefix } = settings; + return { + selectorInit: '[data-code-snippet]', + attribShowMoreText: 'data-show-more-text', + attribShowLessText: 'data-show-less-text', + minHeight: 288, + classExpanded: `${prefix}--snippet--expand`, + classExpandBtn: `.${prefix}--snippet-btn--expand`, + classExpandText: `.${prefix}--snippet-btn--text`, + classHideExpand: `${prefix}--snippet-btn--expand--hide`, + }; + } } export default CodeSnippet; diff --git a/src/components/date-picker/date-picker.js b/src/components/date-picker/date-picker.js index 2696c349bdfc..b1f9a8254e76 100644 --- a/src/components/date-picker/date-picker.js +++ b/src/components/date-picker/date-picker.js @@ -335,7 +335,7 @@ class DatePicker extends mixin(createComponent, initComponentBySearch, handles) classWeekday: `${prefix}--date-picker__weekday`, classDay: `${prefix}--date-picker__day`, classFocused: `${prefix}--focused`, - classVisuallyHidden: 'bx--visually-hidden', + classVisuallyHidden: `${prefix}--visually-hidden`, attribType: 'data-date-picker-type', dateFormat: 'm/d/Y', }; diff --git a/tests/spec/checkbox_spec.js b/tests/spec/checkbox_spec.js index d019b06ce62e..f9c6b91e88ec 100644 --- a/tests/spec/checkbox_spec.js +++ b/tests/spec/checkbox_spec.js @@ -31,6 +31,12 @@ describe('Test Checkbox', function() { it('should set default options', function() { expect(flattenOptions(instance.options)).toEqual({ selectorInit: '.bx--checkbox', + selectorContainedCheckboxState: '[data-contained-checkbox-state]', + selectorContainedCheckboxDisabled: '[data-contained-checkbox-disabled]', + classLabel: 'bx--checkbox-label', + classLabelFocused: 'bx--checkbox-label__focus', + attribContainedCheckboxState: 'data-contained-checkbox-state', + attribContainedCheckboxDisabled: 'data-contained-checkbox-disabled', }); }); From f0be7e1a00af750ccd788dd98093074823550748 Mon Sep 17 00:00:00 2001 From: Alessandra Davila Date: Wed, 26 Sep 2018 09:32:11 -0500 Subject: [PATCH 04/10] feat(shell): add app switcher styles (#1057) * feat(shell): add initial app switcher panel styles * chore(shell-switcher): add variable styles * chore(switcher): change feature flag to false * fix(switcher): change accessibility lable * chore(shell-switcher): unnest css * chore(shell-switcher): class name change * feat(shell): add switcher expanded and show all views * chore(shell): add styles to all apps view * chore(shell): add ibm colors * chore(shell-switcher): change units and unnest selectors * chore(shell-switcher): ie11 support * chore(shell-switcher): fix ie11 tabbing and alignment * chore(shell-switcher): small style updates * chore(shell-switcher): add switcher variables * chore(shell-switcher): product name alignment * chore(product-switcher): modified naming * chore(product-switcher): make overflow sibiling to link --- .../ui-shell/_product-switcher.scss | 174 ++++++++++++++++++ src/components/ui-shell/_theme.scss | 11 +- src/components/ui-shell/_ui-shell.scss | 1 + src/components/ui-shell/product-switcher.hbs | 103 +++++++++++ src/components/ui-shell/ui-shell.config.js | 62 +++++++ src/components/ui-shell/ui-shell.hbs | 1 + 6 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/components/ui-shell/_product-switcher.scss create mode 100644 src/components/ui-shell/product-switcher.hbs diff --git a/src/components/ui-shell/_product-switcher.scss b/src/components/ui-shell/_product-switcher.scss new file mode 100644 index 000000000000..7eca0d973de9 --- /dev/null +++ b/src/components/ui-shell/_product-switcher.scss @@ -0,0 +1,174 @@ +@import '../../globals/scss/functions'; +@import '../../globals/scss/helper-mixins'; +@import '../../globals/scss/vars'; +@import '../../globals/scss/typography'; +@import 'theme'; +@import 'functions'; + +@mixin product-switcher { + //-------------------------------------------------------------------------- + // Platform Global Panel + //-------------------------------------------------------------------------- + .#{$prefix}--panel--overlay { + position: fixed; + top: units(6); + right: 0; + bottom: 0; + width: units(32); + will-change: transform; + transform: translate3d(100%, 0, 0); + padding: 1rem 0; + overflow-y: auto; + z-index: 1000; + background-color: $shell-header-bg-02; + height: 100%; + overflow-x: hidden; + } + + .#{$prefix}--panel--expanded { + box-shadow: 0 8px 16px 0 rgba($shell-ui-03, 0.25); + transform: translate3d(0, 0, 0); + transition: transform 0.11s cubic-bezier(0.2, 0, 0.38, 0.9); + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Search + //-------------------------------------------------------------------------- + .#{$prefix}--product-switcher__search { + padding: 0 units(2); + margin-bottom: units(3); + } + + .#{$prefix}--search--shell input { + background-color: $shell-header-bg-05; + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Buttons + //-------------------------------------------------------------------------- + .#{$prefix}--product-switcher__subheader, + .#{$prefix}--product-switcher__all-btn { + @include typescale('omega'); + padding: units(1); + color: $shell-header-text-03; + } + + .#{$prefix}--product-switcher__subheader { + padding-left: units(7); + } + + .#{$prefix}--product-switcher__all-btn { + padding-left: units(7); + } + + .#{$prefix}--product-switcher__all-btn, + .#{$prefix}--product-switcher__back-btn { + display: inline-block; + background: transparent; + width: 100%; + border: none; + color: $shell-header-link; + cursor: pointer; + text-align: left; + } + + .#{$prefix}--product-switcher__all-btn:hover, + .#{$prefix}--product-switcher__back-btn:hover { + text-decoration: underline; + } + + .#{$prefix}--product-switcher__all-btn:focus, + .#{$prefix}--product-switcher__back-btn:focus { + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--product-switcher__back-btn { + display: flex; + align-items: center; + @include typescale('omega'); + padding: units(1) units(2); + } + + .#{$prefix}--product-switcher__back-arrow { + fill: $shell-header-link; + margin-right: units(2); + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Product List + //-------------------------------------------------------------------------- + .#{$prefix}--product-list__item { + cursor: pointer; + display: flex; + justify-content: space-between; + } + + .#{$prefix}--product-list__item:hover { + background: $shell-header-bg-03; + } + + .#{$prefix}--product-link { + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + padding: units(1) units(2); + text-decoration: none; + } + + .#{$prefix}--product-link:focus { + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--product-switcher__icon { + margin-right: units(2); + } + + .#{$prefix}--product-link__name { + @include typescale('zeta'); + margin-left: 0.25rem; + font-weight: 400; + color: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu { + display: none; + justify-content: center; + align-items: center; + width: units(5); + } + + .#{$prefix}--overflow-menu > svg { + fill: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu:hover { + background: $shell-header-bg-04; + } + + .#{$prefix}--overflow-menu:hover > svg { + fill: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu:focus { + display: flex; + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--overflow-menu-options__option:hover { + background: $shell-header-bg-03; + } + + .#{$prefix}--product-list__item:hover .#{$prefix}--overflow-menu { + display: flex; + } +} + +@include exports('product-switcher') { + @if feature-flag-enabled('ui-shell') { + @include product-switcher; + } +} diff --git a/src/components/ui-shell/_theme.scss b/src/components/ui-shell/_theme.scss index 634707eb9a59..e1390caacd65 100644 --- a/src/components/ui-shell/_theme.scss +++ b/src/components/ui-shell/_theme.scss @@ -7,14 +7,20 @@ $shell-header-bg-02: #f3f3f3; // Panel item hover $shell-header-bg-03: #dcdcdc; +// Panel overflow hover +$shell-header-bg-04: #bebebe; + +// Panel input background +$shell-header-bg-05: #fff; + // Primary text in header-panel, Item text $shell-header-text-01: #f3f3f3; // Secondary text in header-panel, Item text -$shell-header-text-02: #252525; +$shell-header-text-02: #171717; // Secondary text in header-panel, Category label -$shell-header-text-03: #565656; +$shell-header-text-03: #8c8c8c; // Header bar icons $shell-header-icon-01: #f3f3f3; @@ -31,3 +37,4 @@ $shell-header-icon-selected: #0062ff; // Temporary token $shell-brand-01: #0062ff; $shell-ui-02: #3d3d3d; +$shell-ui-03: #000; diff --git a/src/components/ui-shell/_ui-shell.scss b/src/components/ui-shell/_ui-shell.scss index a887f91c0ff2..84998ebcb28d 100644 --- a/src/components/ui-shell/_ui-shell.scss +++ b/src/components/ui-shell/_ui-shell.scss @@ -2,5 +2,6 @@ @import 'variables'; @import 'functions'; @import 'platform-header'; +@import 'product-switcher'; @import 'platform-side-nav'; @import 'platform-nav'; diff --git a/src/components/ui-shell/product-switcher.hbs b/src/components/ui-shell/product-switcher.hbs new file mode 100644 index 000000000000..6457c3bc1b7d --- /dev/null +++ b/src/components/ui-shell/product-switcher.hbs @@ -0,0 +1,103 @@ +{{#if switcher.state.expanded}} +