diff --git a/CHANGELOG.md b/CHANGELOG.md index e0853eb59..99e0a6fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ### Added +- New Pageheader Organism-level component, that encapsulates what was previously an example component built using utility classes. + +- Updates the layout of the Sidebar component, improvements to the scrolling behavior and uses the invisible button variant. + - Badge with Label, added an example showing a text label rendered next to a badge component, to the badge docs. - A new layout component at `atoms/switcher`, that lays out its children in a horizontal row with consistent spacing between children. The layout switches to a vertical stack once the width of the component passes below a threshold, or the number of children goes over a limit. - A new layout component at `atoms/stack`, that lays out its children vertically, with consistent spacing between children. diff --git a/scss/bitstyles/organisms/_index.scss b/scss/bitstyles/organisms/_index.scss index 66503c46b..1f7451062 100644 --- a/scss/bitstyles/organisms/_index.scss +++ b/scss/bitstyles/organisms/_index.scss @@ -4,3 +4,4 @@ @forward './notification-center' as notification-center-*; @forward './table' as table-*; @forward './joined-ui' as joined-ui-*; +@forward './page-header' as page-header-*; diff --git a/scss/bitstyles/organisms/page-header/PageHeader.js b/scss/bitstyles/organisms/page-header/PageHeader.js new file mode 100644 index 000000000..4dc7380ba --- /dev/null +++ b/scss/bitstyles/organisms/page-header/PageHeader.js @@ -0,0 +1,157 @@ +import icons from '../../../../assets/images/icons.svg'; +import Button from '../../atoms/button/Button'; + +const PageHeader = ({ + topLeft, + topRight, + centerLeft, + centerRight, + bottomLeft, + bottomRight, +}) => { + const pageHeader = document.createElement('header'); + pageHeader.classList.add('o-page-header'); + const content = document.createElement('div'); + content.classList.add('a-content', 'o-page-header__content'); + + pageHeader.appendChild(content); + + const contentPositions = [ + { position: 'top-left', content: topLeft }, + { position: 'top-right', content: topRight }, + { position: 'center-left', content: centerLeft }, + { position: 'center-right', content: centerRight }, + { position: 'bottom-left', content: bottomLeft }, + { position: 'bottom-right', content: bottomRight }, + ]; + + contentPositions.forEach((item) => { + if (item.content) { + const contentElement = document.createElement('div'); + contentElement.classList.add(`o-page-header__${item.position}`); + contentElement.appendChild(item.content); + content.appendChild(contentElement); + } + }); + + return pageHeader; +}; + +export default PageHeader; + +export const breadCrumbsMenu = document.createElement('ol'); +breadCrumbsMenu.classList.add( + 'u-h7', + 'u-list-none', + 'u-flex', + 'u-flex-wrap', + 'u-items-center' +); +breadCrumbsMenu.innerHTML = ` +
  • + Main section + +
  • +
  • + Sub section + +
  • +
  • + Sub page + +
  • `; + +export const header = document.createElement('header'); +header.innerHTML = ` +
    +
    +

    Title Header

    +
    + Online +
    +
    +
    `; + +export const tabs = document.createElement('ul'); +tabs.classList.add( + 'u-list-none', + 'u-flex', + 'u-overflow-x-auto', + 'u-items-end', + 'a-button--tab-container', + 'u-margin-m-bottom' +); +tabs.setAttribute('role', 'tablist'); +tabs.setAttribute('aria-label', 'Data'); + +const tabsListItem = document.createElement('li'); +tabsListItem.classList.add('u-margin-s2-right'); + +const tabsButton = ({ label, ariaSelected, ariaControls }) => + Button({ + classname: ['a-button', 'a-button--tab'], + children: label, + role: 'tab', + ariaSelected, + ariaControls, + }); + +const tabsLabels = [ + { + label: 'Tab1', + ariaSelected: true, + ariaControls: 'tab-panel-1', + }, + { + label: 'Tab2', + ariaSelected: false, + ariaControls: 'tab-panel-2', + }, + { + label: 'Tab3', + ariaSelected: false, + ariaControls: 'tab-panel-3', + }, +]; + +tabsLabels.forEach((item) => + tabs.appendChild( + tabsListItem.appendChild( + tabsButton({ + label: item.label, + ariaSelected: item.ariaSelected, + ariaControls: item.ariaControls, + }) + ) + ) +); diff --git a/scss/bitstyles/organisms/page-header/_index.scss b/scss/bitstyles/organisms/page-header/_index.scss new file mode 100644 index 000000000..02470bfca --- /dev/null +++ b/scss/bitstyles/organisms/page-header/_index.scss @@ -0,0 +1,50 @@ +@forward 'settings'; +@use './settings'; +@use '../../tools/media-query'; +@use '../../tools/classname'; + +#{classname.get($classname-items: 'page-header', $layer: 'organism')} { + padding-top: settings.$padding-top; + background-color: settings.$background-color; + + @include media-query.get('m') { + &__content { + display: grid; + grid-template-areas: + 'top-left top-right' + 'center-left center-right' + 'bottom-left bottom-right'; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + &__top-left { + grid-area: top-left; + } + + &__top-right { + display: flex; + grid-area: top-right; + justify-content: flex-end; + } + + &__center-left { + grid-area: center-left; + } + + &__center-right { + display: flex; + grid-area: center-right; + justify-content: flex-end; + } + + &__bottom-left { + grid-area: bottom-left; + } + + &__bottom-right { + display: flex; + grid-area: bottom-right; + justify-content: flex-end; + } + } +} diff --git a/scss/bitstyles/organisms/page-header/_settings.scss b/scss/bitstyles/organisms/page-header/_settings.scss new file mode 100644 index 000000000..d0b694dc7 --- /dev/null +++ b/scss/bitstyles/organisms/page-header/_settings.scss @@ -0,0 +1,6 @@ +@use '../../tools/design-token'; + +$padding-top: var(design-token.get('size', 'm')) !default; +$background-color: var( + design-token.get('color', 'grayscale', 'light-3') +) !default; diff --git a/scss/bitstyles/organisms/page-header/page-header.stories.js b/scss/bitstyles/organisms/page-header/page-header.stories.js new file mode 100644 index 000000000..029d84296 --- /dev/null +++ b/scss/bitstyles/organisms/page-header/page-header.stories.js @@ -0,0 +1,56 @@ +import PageHeader, { breadCrumbsMenu, header, tabs } from './PageHeader'; + +export default { + title: 'Organisms/Page Header', + component: PageHeader, + argTypes: { + topLeft: { + description: 'The top left area of the header', + }, + topRight: { + description: 'The top right area of the header', + }, + centerLeft: { + description: 'The center left of the header', + }, + centerRight: { + description: 'The center right area of the header', + }, + bottomLeft: { + description: 'The bottom left area of the header', + }, + bottomRight: { + description: 'The bottom right area of the header', + }, + }, +}; + +const Template = (args) => PageHeader(args); + +export const Default = Template.bind({}); +Default.args = { + topLeft: breadCrumbsMenu, + topRight: breadCrumbsMenu.cloneNode(true), + centerLeft: header, + bottomLeft: tabs, +}; +Default.parameters = { + zeplinLink: [ + { + name: 'default', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6407627ec0507f42b9eec653', + }, + ], +}; + +export const NoBottomSlot = Template.bind({}); +NoBottomSlot.args = { + topLeft: breadCrumbsMenu.cloneNode(true), + centerLeft: header.cloneNode(true), +}; + +export const OnlyTopSlot = Template.bind({}); +OnlyTopSlot.args = { + topLeft: header.cloneNode(true), + topRight: breadCrumbsMenu.cloneNode(true), +}; diff --git a/scss/bitstyles/organisms/page-header/page-header.stories.mdx b/scss/bitstyles/organisms/page-header/page-header.stories.mdx new file mode 100644 index 000000000..4ce0ab419 --- /dev/null +++ b/scss/bitstyles/organisms/page-header/page-header.stories.mdx @@ -0,0 +1,92 @@ +import { Canvas, Meta, Story } from '@storybook/addon-docs'; +import icons from '../../../../assets/images/icons.svg'; +import PageHeader from './PageHeader.js'; + + + +# Page header + +The intro to every page, containing slots for: + +- [breadcrumbs](/docs/ui-navigation-breadcrumbs--breadcrumbs) +- page title +- status [badge](/docs/atoms-badge--badge) +- action [buttons](/docs/ui-buttons-buttons--page) +- [tabs](/docs/ui-navigation-tabs--tabs) for organising the content (note that no tabpanels are shown here) + +
    + Examples use + +
    + +This first example shows a header with all rows filled: + + + + + +Only using the top and central slots: + + + + + +This is a minimal example, with only the top slot: + + + + diff --git a/scss/bitstyles/ui/extending-bitstyles.stories.mdx b/scss/bitstyles/ui/extending-bitstyles.stories.mdx index f96f8d08f..47690851b 100644 --- a/scss/bitstyles/ui/extending-bitstyles.stories.mdx +++ b/scss/bitstyles/ui/extending-bitstyles.stories.mdx @@ -45,6 +45,7 @@ Add your own components at the end of the `@use` statements importing from `bits @use 'bitstyles/organisms/notification-center'; @use 'bitstyles/organisms/table'; @use 'bitstyles/organisms/joined-ui'; +@use 'bitstyles/organisms/page-header'; // … diff --git a/scss/bitstyles/ui/page-header.stories.mdx b/scss/bitstyles/ui/page-header.stories.mdx deleted file mode 100644 index 300f8ae4c..000000000 --- a/scss/bitstyles/ui/page-header.stories.mdx +++ /dev/null @@ -1,360 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; -import icons from '../../../assets/images/icons.svg'; - - - -# Page header - -The intro to every page, containing slots for: - -- [breadcrumbs](/docs/ui-navigation-breadcrumbs--breadcrumbs) -- page title -- status [badge](/docs/atoms-badge--badge) -- action [buttons](/docs/ui-buttons-buttons--page) -- [tabs](/docs/ui-navigation-tabs--tabs) for organising the content (note that no tabpanels are shown here) - -
    -

    - These examples use AlpineJS for the behavior. If you’re using a different JS - package, replace the blocks of html attributes starting with `x-`, and with - `@click`. -

    -
    - -
    - Examples use - -
    - -This first example shows a header with all these slots filled: - - - - {` - - `} - - - -It’s also common to not need tabs — if the content is not large and varied enough, it’s beter to use a simpler layout: - - - - {` - - `} - - - -This is a minimal example, with only breadcrumbs and a heading. - - - - {` - - `} - - diff --git a/test/scss/fixtures/bitstyles-overrides.css b/test/scss/fixtures/bitstyles-overrides.css index 48ee464a3..fcd0a9687 100644 --- a/test/scss/fixtures/bitstyles-overrides.css +++ b/test/scss/fixtures/bitstyles-overrides.css @@ -2250,6 +2250,41 @@ table { --bscpn-button-border-bottom-left-radius: 0; margin-right: calc(var(--bscpn-button-border-width) * -1); } +.bs-or-page-header { + background-color: var(--bscpn-color-grayscale-light-3); + padding-top: 10rem; + } + @media screen and (min-width: 30em) { + .bs-or-page-header__content { + display: grid; + grid-template-areas: 'top-left top-right' 'center-left center-right' 'bottom-left bottom-right'; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .bs-or-page-header__top-left { + grid-area: top-left; + } + .bs-or-page-header__top-right { + display: flex; + grid-area: top-right; + justify-content: flex-end; + } + .bs-or-page-header__center-left { + grid-area: center-left; + } + .bs-or-page-header__center-right { + display: flex; + grid-area: center-right; + justify-content: flex-end; + } + .bs-or-page-header__bottom-left { + grid-area: bottom-left; + } + .bs-or-page-header__bottom-right { + display: flex; + grid-area: bottom-right; + justify-content: flex-end; + } + } .bs-aspect-ratio-10-10 { aspect-ratio: 1/1; } diff --git a/test/scss/fixtures/bitstyles.css b/test/scss/fixtures/bitstyles.css index a1422fb79..1c6f6013e 100644 --- a/test/scss/fixtures/bitstyles.css +++ b/test/scss/fixtures/bitstyles.css @@ -2677,6 +2677,41 @@ table { --bs-button-border-bottom-left-radius: 0; margin-right: calc(var(--bs-button-border-width) * -1); } +.o-page-header { + background-color: var(--bs-color-grayscale-light-3); + padding-top: var(--bs-size-m); + } +@media screen and (min-width: 30em) { + .o-page-header__content { + display: grid; + grid-template-areas: 'top-left top-right' 'center-left center-right' 'bottom-left bottom-right'; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .o-page-header__top-left { + grid-area: top-left; + } + .o-page-header__top-right { + display: flex; + grid-area: top-right; + justify-content: flex-end; + } + .o-page-header__center-left { + grid-area: center-left; + } + .o-page-header__center-right { + display: flex; + grid-area: center-right; + justify-content: flex-end; + } + .o-page-header__bottom-left { + grid-area: bottom-left; + } + .o-page-header__bottom-right { + display: flex; + grid-area: bottom-right; + justify-content: flex-end; + } +} .u-aspect-ratio-1-1 { aspect-ratio: 1/1; } diff --git a/test/scss/test-use-all.scss b/test/scss/test-use-all.scss index 002b139e2..1f072feed 100644 --- a/test/scss/test-use-all.scss +++ b/test/scss/test-use-all.scss @@ -175,6 +175,7 @@ $sidebar-small-location: right, $table-color-th: #f00, $joined-ui-border-radius: 10rem, + $page-header-padding-top: 10rem, // utilities $aspect-ratio-values: ( diff --git a/test/scss/test-use-each.scss b/test/scss/test-use-each.scss index 3950b6aac..f0bfd7389 100644 --- a/test/scss/test-use-each.scss +++ b/test/scss/test-use-each.scss @@ -286,7 +286,9 @@ @use '../../scss/bitstyles/organisms/joined-ui' with ( $border-radius: 10rem ); - +@use '../../scss/bitstyles/organisms/page-header' with ( + $padding-top: 10rem +); // // Utilities //////////////////////////////////// diff --git a/test/scss/test-use-layers.scss b/test/scss/test-use-layers.scss index b5c5466e1..5e279a1a7 100644 --- a/test/scss/test-use-layers.scss +++ b/test/scss/test-use-layers.scss @@ -189,7 +189,8 @@ $sidebar-large-width: 1rem, $sidebar-small-location: right, $table-color-th: #f00, - $joined-ui-border-radius: 10rem + $joined-ui-border-radius: 10rem, + $page-header-padding-top: 10rem ); @use '../../scss/bitstyles/utilities' with ( $aspect-ratio-values: (