diff --git a/src/Nav/Nav.scss b/src/Nav/Nav.scss index 48b8e6680e..af41cbf9fd 100644 --- a/src/Nav/Nav.scss +++ b/src/Nav/Nav.scss @@ -1,4 +1,5 @@ @import "variables"; +@import "mixins"; // Base class // @@ -37,23 +38,126 @@ // Tabs // +.pgn__tabs { + &.nav-pills .nav-link { + border: $nav-pills-link-border-width solid $nav-pills-link-border-color; + + &:focus { + @include nav-tabs-link-focus($nav-tabs-link-focus-border-color); + } + + &:hover { + border-color: $nav-tabs-link-hover-border-color; + background-color: $nav-tabs-link-hover-bg; + } + + &.active, + .nav-item.show .nav-link { + color: $nav-pills-link-active-color; + background-color: $nav-pills-link-active-bg; + border-color: $nav-pills-link-active-bg; + + &:focus { + @include nav-tabs-link-focus($nav-tabs-link-focus-border-color); + } + } + } + + &.nav-inverse-pills { + + .tab-content { + color: $nav-inverse-pills-tab-content-color; + } + + .nav-link { + color: $nav-inverse-pills-link-color; + border: $nav-inverse-pills-link-border-width solid $nav-inverse-pills-link-border-color; + + &:hover { + background-color: $nav-inverse-pills-link-hover-bg; + } + + &:focus { + @include nav-tabs-link-focus($nav-inverse-pills-link-focus-color); + } + + &.disabled { + opacity: .5; + } + } + + .nav-link.active, + .nav-item.show .nav-link { + background-color: $nav-inverse-pills-link-active-bg; + color: $nav-inverse-pills-link-active-color; + border-color: $nav-inverse-pills-link-active-border-color; + + &:hover { + background-color: $nav-inverse-pills-link-active-hover-bg; + color: $nav-inverse-pills-link-active-hover-color; + border-color: $nav-inverse-pills-link-active-hover-border-color; + } + + &:focus:hover { + background-color: $nav-inverse-pills-link-active-focus-hover-bg; + color: $nav-inverse-pills-link-active-focus-color; + border-color: $nav-inverse-pills-link-active-focus-border-color; + } + } + } + + &.nav-inverse-tabs { + + .tab-content { + color: $nav-inverse-tabs-tab-content-color; + } + + .nav-link { + color: $nav-inverse-tabs-link-color; + border-bottom: $nav-tabs-border-width solid $nav-inverse-tabs-link-border-bottom-color; + + &:hover { + background-color: $nav-inverse-tabs-link-hover-bg; + } + + &:focus { + @include nav-tabs-link-focus($nav-inverse-tabs-link-focus-bg); + } + + &.disabled { + opacity: .5; + } + } + + .nav-link.active, + .nav-item.show .nav-link { + border-bottom: $nav-inverse-tabs-link-active-border-bottom-width solid $nav-inverse-tabs-link-active-border-color; + background-color: $nav-inverse-tabs-link-active-bg; + + &:hover { + background-color: $nav-inverse-tabs-link-active-hover-bg; + } + } + } +} + .nav-tabs { border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color; .nav-link { - margin-bottom: -$nav-tabs-border-width; - border-top: $nav-tabs-border-width solid transparent; - border-bottom: $nav-tabs-border-width solid transparent; - border-left: none; - border-right: none; + margin-bottom: calc(#{$nav-tabs-border-width} * -1); + border: $nav-tabs-border-width solid $nav-tabs-link-border-color; + border-bottom-width: $nav-tabs-link-border-bottom-width; @include border-top-radius($nav-tabs-border-radius); - @include hover-focus { + &:hover { border-color: $nav-tabs-link-hover-border-color; background-color: $nav-tabs-link-hover-bg; } + &:focus { + @include nav-tabs-link-focus($nav-tabs-link-focus-border-color); + } + &.disabled { color: $nav-link-disabled-color; background-color: transparent; @@ -66,11 +170,19 @@ color: $nav-tabs-link-active-color; background-color: $nav-tabs-link-active-bg; border-color: $nav-tabs-link-active-border-color; + + &:hover { + background-color: $nav-tabs-link-hover-bg; + } + + &:focus { + @include nav-tabs-link-focus($nav-tabs-link-focus-border-color); + } } .dropdown-menu { // Make dropdown border overlap tab border - margin-top: -$nav-tabs-border-width; + margin-top: calc(#{$nav-tabs-border-width} * -1); // Remove the top rounded corners here since there is a hard edge above the menu @include border-top-radius(0); diff --git a/src/Nav/_mixins.scss b/src/Nav/_mixins.scss new file mode 100644 index 0000000000..9e896eb633 --- /dev/null +++ b/src/Nav/_mixins.scss @@ -0,0 +1,16 @@ +@mixin nav-tabs-link-focus($border-color) { + position: relative; + outline: 0; + z-index: map-get($map: $indexes, $key: 1); + + &::before { + content: ""; + position: absolute; + top: calc(#{$nav-tabs-link-distance-to-border} * -1); + right: calc(#{$nav-tabs-link-distance-to-border} * -1); + bottom: calc(#{$nav-tabs-link-distance-to-border} * -1); + left: calc(#{$nav-tabs-link-distance-to-border} * -1); + border: solid $nav-tabs-border-width $border-color; + border-radius: $nav-tabs-border-radius; + } +} diff --git a/src/Nav/_variables.scss b/src/Nav/_variables.scss index 668909b664..eccd21e5ea 100644 --- a/src/Nav/_variables.scss +++ b/src/Nav/_variables.scss @@ -1,23 +1,52 @@ // Navs -$nav-link-padding-y: .5rem !default; -$nav-link-padding-x: 1rem !default; -$nav-link-color: $gray-700 !default; -$nav-link-disabled-color: $gray-300 !default; -$nav-link-font-weight: 500 !default; +$nav-link-padding-y: .5rem !default; +$nav-link-padding-x: 1rem !default; +$nav-link-color: $gray-700 !default; +$nav-link-disabled-color: $gray-300 !default; +$nav-link-font-weight: 500 !default; -$nav-tabs-border-color: $light-400 !default; -$nav-tabs-border-width: 2px !default; -$nav-tabs-border-radius: 0 !default; -$nav-tabs-link-hover-border-color: transparent transparent $nav-tabs-border-color !default; -$nav-tabs-link-hover-bg: $light-400 !default; -$nav-tabs-link-active-color: $primary-500 !default; -$nav-tabs-link-active-bg: $body-bg !default; -$nav-tabs-link-active-border-color: transparent transparent $primary-500 !default; +$nav-tabs-border-color: $light-400 !default; +$nav-tabs-border-width: 2px !default; +$nav-tabs-border-radius: 0 !default; +$nav-tabs-link-hover-border-color: transparent transparent $nav-tabs-border-color !default; +$nav-tabs-link-hover-bg: $light-400 !default; +$nav-tabs-link-active-color: $primary-500 !default; +$nav-tabs-link-active-bg: transparent !default; +$nav-tabs-link-active-border-color: transparent transparent $primary-500 !default; +$nav-tabs-link-focus-border-color: $nav-tabs-link-active-color !default; +$nav-tabs-link-distance-to-border: .313rem !default; +$nav-tabs-link-border-bottom-width: .188rem !default; +$nav-tabs-link-border-color: transparent !default; -$nav-pills-border-radius: $border-radius !default; -$nav-pills-link-active-color: $component-active-color !default; -$nav-pills-link-active-bg: $component-active-bg !default; +$nav-pills-border-radius: $border-radius !default; +$nav-pills-link-active-color: $component-active-color !default; +$nav-pills-link-active-bg: $component-active-bg !default; +$nav-pills-link-border-color: $nav-tabs-border-color !default; +$nav-pills-link-border-width: 1px !default; -$nav-divider-color: theme-color("gray", "background") !default; -$nav-divider-margin-y: calc($spacer / 2) !default; +$nav-inverse-pills-link-color: $white !default; +$nav-inverse-pills-link-border-color: $dark-300 !default; +$nav-inverse-pills-link-hover-bg: $nav-inverse-pills-link-border-color !default; +$nav-inverse-pills-link-focus-color: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-active-bg: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-active-color: $primary-500 !default; +$nav-inverse-pills-link-active-border-color: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-active-hover-bg: $nav-inverse-pills-link-border-color !default; +$nav-inverse-pills-link-active-hover-color: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-active-hover-border-color: $nav-inverse-pills-link-border-color !default; +$nav-inverse-pills-link-active-focus-hover-bg: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-active-focus-color: $nav-inverse-pills-link-active-color !default; +$nav-inverse-pills-link-active-focus-border-color: $nav-inverse-pills-link-color !default; +$nav-inverse-pills-link-border-width: $nav-pills-link-border-width !default; +$nav-inverse-pills-tab-content-color: $nav-inverse-pills-link-color !default; + +$nav-inverse-tabs-link-color: $white !default; +$nav-inverse-tabs-link-border-bottom-color: $dark-300 !default; +$nav-inverse-tabs-link-hover-bg: $nav-inverse-tabs-link-border-bottom-color !default; +$nav-inverse-tabs-link-active-hover-bg: transparent !default; +$nav-inverse-tabs-link-focus-bg: $nav-inverse-tabs-link-color !default; +$nav-inverse-tabs-link-active-border-color: $nav-inverse-tabs-link-color !default; +$nav-inverse-tabs-link-active-bg: $nav-inverse-tabs-link-hover-bg !default; +$nav-inverse-tabs-link-active-border-bottom-width: $nav-tabs-link-border-bottom-width !default; +$nav-inverse-tabs-tab-content-color: $nav-inverse-tabs-link-color !default; diff --git a/src/Tabs/README.md b/src/Tabs/README.md index 8350a2c293..ec056fd7d9 100644 --- a/src/Tabs/README.md +++ b/src/Tabs/README.md @@ -23,7 +23,11 @@ notes: | ## Uncontrolled usage ```jsx live - + Hello I am the first panel. @@ -102,6 +106,50 @@ notes: | ``` +## Inverse-Pills usage + +```jsx live + + + + Hello I am the first panel. + + + Hello I am the second panel. + + + Hello I am third first panel. + + + +``` + +## Inverse Tabs usage + +```jsx live + + + + Hello I am the first panel. + + + Hello I am the second panel. + + + Hello I am third first panel. + + + +``` + ## With notification ```jsx live @@ -140,7 +188,11 @@ notes: | ### Responsive support ```jsx live - + Hello I am the first panel. diff --git a/src/Tabs/Tabs.scss b/src/Tabs/Tabs.scss index 48214e7d0b..0681d53f4e 100644 --- a/src/Tabs/Tabs.scss +++ b/src/Tabs/Tabs.scss @@ -15,32 +15,90 @@ min-width: $tab-notification-width; font-size: $tab-notification-font-size; } -} -.pgn__tab_invisible { - position: absolute; - left: 0; - pointer-events: none; - visibility: hidden; -} + .pgn__tab_more.nav-link { + margin-bottom: 0; + padding: 0; + + .dropdown .dropdown-toggle { + display: block; + border: none; + } + } -.pgn__tab_more.nav-link { - margin-bottom: 0; - padding: 0; - border-top: none; - border-bottom: none; + // Nav-pills + &.nav-pills { + .pgn__tab_more.nav-link { + margin: 0; - .dropdown .dropdown-toggle { - display: block; - padding: .5rem 1rem; - border-top-width: 4px; - border-bottom-width: 4px; + .dropdown .dropdown-toggle { + padding: $tab-more-link-dropdown-toggle-padding-x $tab-more-link-dropdown-toggle-padding-y; + + &:focus { + background-color: $tab-more-link-dropdown-toggle-focus-bg; + border-color: $tab-more-link-dropdown-toggle-focus-border-color; + color: $tab-more-link-dropdown-toggle-focus-color; + + &.btn.btn-link { + color: $tab-more-link-dropdown-toggle-btn-focus-color; + border-color: $tab-more-link-dropdown-toggle-btn-focus-border-color; + } + } + } + + &.active { + .dropdown .dropdown-toggle { + color: $tab-more-link-dropdown-toggle-active-color; + } + + &:hover { + .dropdown .dropdown-toggle { + background-color: $tab-more-link-dropdown-toggle-hover-color; + } + } + } + } } - &.active { + // Nav inverse pills + &.nav-inverse-pills .pgn__tab_more.nav-link { .dropdown .dropdown-toggle { - // stylelint-disable-next-line - @extend .nav-link, .active; + padding: $tab-inverse-pills-link-dropdown-toggle-padding-x $tab-inverse-pills-link-dropdown-toggle-padding-y; + + &:focus { + background-color: $tab-inverse-pills-link-dropdown-toggle-focus-bg; + color: $tab-inverse-pills-link-dropdown-toggle-focus-color; + } } + + &.active .dropdown .dropdown-toggle { + color: $tab-inverse-pills-link-dropdown-toggle-active-color; + + &:hover { + color: $tab-inverse-pills-link-dropdown-toggle-active-hover-color; + background-color: $tab-inverse-pills-link-dropdown-toggle-active-hover-bg; + } + } + } + + // Nav tabs + &.nav-tabs .pgn__tab_more.nav-link .dropdown .dropdown-toggle { + padding: .563rem 1rem; + } + + // Nav inverse tabs + &.nav-inverse-tabs .pgn__tab_more.nav-link .dropdown .dropdown-toggle { + padding: $tab-inverse-tabs-link-dropdown-toggle-padding-x $tab-inverse-tabs-link-dropdown-toggle-padding-y; + + &:hover { + background-color: $tab-inverse-tabs-link-dropdown-toggle-hover-bg; + } + } + + .pgn__tab_invisible { + position: absolute; + left: 0; + pointer-events: none; + visibility: hidden; } } diff --git a/src/Tabs/Tabs.test.jsx b/src/Tabs/Tabs.test.jsx index 84eb2f9895..7372fcd113 100644 --- a/src/Tabs/Tabs.test.jsx +++ b/src/Tabs/Tabs.test.jsx @@ -82,6 +82,12 @@ describe('', () => { wrapper.find('.dropdown-item').at(0).simulate('click'); expect(wrapper.find('[data-rb-event-key="tab_2"]').at(0).hasClass('active')).toEqual(true); }); + it('select dropdown item after pressing Enter', () => { + const wrapper = mount(); + wrapper.find('#pgn__tab-toggle').at(0).at(0).simulate('click'); + wrapper.find('.dropdown-item').at(0).simulate('keyPress', { key: 'Enter' }); + expect(wrapper.find('[data-rb-event-key="tab_2"]').at(0).hasClass('active')).toEqual(true); + }); it('invalid child does not render', () => { const wrapper = mount(( diff --git a/src/Tabs/__snapshots__/Tabs.test.jsx.snap b/src/Tabs/__snapshots__/Tabs.test.jsx.snap index dd7c0b7868..89993edd79 100644 --- a/src/Tabs/__snapshots__/Tabs.test.jsx.snap +++ b/src/Tabs/__snapshots__/Tabs.test.jsx.snap @@ -18,6 +18,7 @@ exports[` correct rendering renders successfully 1`] = ` > Tab 1
diff --git a/src/Tabs/_variables.scss b/src/Tabs/_variables.scss index 1aa61d194a..831cc67b0b 100644 --- a/src/Tabs/_variables.scss +++ b/src/Tabs/_variables.scss @@ -1,3 +1,22 @@ -$tab-notification-height: 1rem !default; -$tab-notification-width: 1rem !default; -$tab-notification-font-size: $font-size-xs !default; +$tab-notification-height: 1rem !default; +$tab-notification-width: 1rem !default; +$tab-notification-font-size: $font-size-xs !default; +$tab-more-link-dropdown-toggle-padding-x: .7rem !default; +$tab-more-link-dropdown-toggle-padding-y: $spacer !default; +$tab-more-link-dropdown-toggle-focus-bg: $primary-500 !default; +$tab-more-link-dropdown-toggle-focus-border-color: $tab-more-link-dropdown-toggle-focus-bg !default; +$tab-more-link-dropdown-toggle-focus-color: $white !default; +$tab-more-link-dropdown-toggle-btn-focus-color: $tab-more-link-dropdown-toggle-focus-color !default; +$tab-more-link-dropdown-toggle-btn-focus-border-color: $tab-more-link-dropdown-toggle-focus-bg !default; +$tab-more-link-dropdown-toggle-active-color: $tab-more-link-dropdown-toggle-focus-color !default; +$tab-more-link-dropdown-toggle-hover-color: $tab-more-link-dropdown-toggle-focus-bg !default; +$tab-inverse-pills-link-dropdown-toggle-padding-x: .625rem !default; +$tab-inverse-pills-link-dropdown-toggle-padding-y: $spacer !default; +$tab-inverse-pills-link-dropdown-toggle-focus-bg: $white !default; +$tab-inverse-pills-link-dropdown-toggle-focus-color: $primary-500 !default; +$tab-inverse-pills-link-dropdown-toggle-active-color: $tab-inverse-pills-link-dropdown-toggle-focus-color !default; +$tab-inverse-pills-link-dropdown-toggle-active-hover-color: $tab-inverse-pills-link-dropdown-toggle-focus-bg !default; +$tab-inverse-pills-link-dropdown-toggle-active-hover-bg: $primary-300 !default; +$tab-inverse-tabs-link-dropdown-toggle-padding-x: .625rem !default; +$tab-inverse-tabs-link-dropdown-toggle-padding-y: $spacer !default; +$tab-inverse-tabs-link-dropdown-toggle-hover-bg: transparent !default; diff --git a/src/Tabs/index.jsx b/src/Tabs/index.jsx index 1b57b456d2..b11bae59bd 100644 --- a/src/Tabs/index.jsx +++ b/src/Tabs/index.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useRef } from 'react'; -import BaseTabs from 'react-bootstrap/Tabs'; import classNames from 'classnames'; import PropTypes from 'prop-types'; +import BaseTabs from 'react-bootstrap/Tabs'; import TabsDeprecated from './deprecated'; import Bubble from '../Bubble'; import Dropdown from '../Dropdown'; @@ -71,6 +71,11 @@ function Tabs({ const tabsChildren = useMemo(() => { const indexOfOverflowStart = indexOfLastVisibleChild + 1; + const handleDropdownKeyPress = (e, eventKey) => { + if (e.key === 'Enter') { + handleDropdownTabClick(eventKey); + } + }; const childrenList = React.Children.map(children, (child, index) => { if (child?.type?.name !== 'Tab' && process.env.NODE_ENV === 'development') { // eslint-disable-next-line no-console @@ -93,6 +98,7 @@ function Tabs({ variant="error" role="status" className="pgn__tab-notification" + aria-live="polite" expandable > {notification} @@ -118,8 +124,11 @@ function Tabs({ } return ( handleDropdownTabClick(overflowChild.props.eventKey)} + onKeyPress={(e) => handleDropdownKeyPress(e, overflowChild.props.eventKey)} disabled={overflowChild.props.disabled} datakey={overflowChild.props.eventKey} className={classNames({ @@ -150,7 +159,7 @@ function Tabs({ /> )} - {overflowChildren} + {overflowChildren} )} /> @@ -173,6 +182,7 @@ function Tabs({ } Tabs.propTypes = { + variant: PropTypes.oneOf(['tabs', 'pills', 'inverse-tabs', 'inverse-pills', 'button-group']), /** Specifies elements that is processed to create tabs. */ children: PropTypes.node.isRequired, /** Specifies class name to append to the base element. */ @@ -186,6 +196,7 @@ Tabs.propTypes = { }; Tabs.defaultProps = { + variant: 'tabs', className: undefined, moreTabText: MORE_TAB_TEXT, defaultActiveKey: undefined,