diff --git a/packages/kronos-crm-icons/src/eye-slave.svg b/packages/kronos-crm-icons/src/eye-slave.svg index 7edf288e62..617610ed4f 100644 --- a/packages/kronos-crm-icons/src/eye-slave.svg +++ b/packages/kronos-crm-icons/src/eye-slave.svg @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/packages/kronos-crm-icons/src/recur-slave.svg b/packages/kronos-crm-icons/src/recur-slave.svg index a8269e0c74..f1aca78240 100644 --- a/packages/kronos-crm-icons/src/recur-slave.svg +++ b/packages/kronos-crm-icons/src/recur-slave.svg @@ -28,4 +28,4 @@ - \ No newline at end of file + diff --git a/packages/kronos-crm-icons/src/sort-alpha-desc.svg b/packages/kronos-crm-icons/src/sort-alpha-desc.svg index 363505b3c6..c28ed09de0 100644 --- a/packages/kronos-crm-icons/src/sort-alpha-desc.svg +++ b/packages/kronos-crm-icons/src/sort-alpha-desc.svg @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/packages/kronos-fna-icons/src/print.svg b/packages/kronos-fna-icons/src/print.svg index 9dcc53c259..9d4d08884a 100755 --- a/packages/kronos-fna-icons/src/print.svg +++ b/packages/kronos-fna-icons/src/print.svg @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/packages/kronos-fna-icons/src/synchro.svg b/packages/kronos-fna-icons/src/synchro.svg index 88f4537b6d..14605cc6a5 100755 --- a/packages/kronos-fna-icons/src/synchro.svg +++ b/packages/kronos-fna-icons/src/synchro.svg @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/packages/react/src/components/avatar/avatar.tsx b/packages/react/src/components/avatar/avatar.tsx index 4124e76de6..57a618a449 100644 --- a/packages/react/src/components/avatar/avatar.tsx +++ b/packages/react/src/components/avatar/avatar.tsx @@ -8,7 +8,7 @@ import { Icon } from '../icon/icon'; export type AvatarSize = 'xsmall' | 'small' | 'medium' | 'large' -interface AvatarProps { +export interface AvatarProps { className?: string; username?: string; bgColor?: string; diff --git a/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx new file mode 100644 index 0000000000..858c57cfb7 --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx @@ -0,0 +1,81 @@ +import React, { ReactElement } from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { getByTestId } from '../../test-utils/enzyme-selectors'; +import { mountWithTheme, mountWithProviders, renderWithTheme } from '../../test-utils/renderer'; +import { BentoMenuButton } from './bento-menu-button'; +import { ExternalItemProps, NavItemProps } from '../dropdown-menu/list-items'; + +jest.mock('../../utils/uuid'); + +function setup(children: ReactElement): ReactElement { + return ( + + {children} + + ); +} + +const products = [ + { + id: 'optiona', + label: 'Option A', + value: 'optionA', + to: '/testa', + }, + { + id: 'optionB', + label: 'Option B', + value: 'optionB', + to: '/testb', + }, + { + id: 'optionC', + label: 'Option C', + value: 'optionC', + to: '/testc', + }, + { + id: 'optionD', + label: 'Option D', + value: 'optionD', + to: '/testd', + }, +] as NavItemProps[]; + +const externals = [ + { + id: 'optionA', + label: 'Option A', + href: '/testa', + }, +] as ExternalItemProps[]; + +describe('BentoMenuButton', () => { + test('Opens bento-menu when menu-button is clicked', () => { + const wrapper = mountWithProviders( + , + ); + + getByTestId(wrapper, 'menu-button').simulate('click'); + + expect(getByTestId(wrapper, 'menu-dropdownMenu').prop('hidden')).toBe(false); + }); + + test('Should close bento-menu when escape key is pressed in nav-menu', () => { + const wrapper = mountWithTheme(setup( + , + )); + + getByTestId(wrapper, 'listitem-optionA').simulate('keydown', { key: 'Escape' }); + + expect(getByTestId(wrapper, 'menu-dropdownMenu').prop('hidden')).toBe(true); + }); + + test('Matches Snapshot', () => { + const tree = renderWithTheme(setup( + , + )); + + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap new file mode 100644 index 0000000000..d45d646277 --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap @@ -0,0 +1,468 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BentoMenuButton Matches Snapshot 1`] = ` +.c2 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: inherit; + border: 1px solid; + border-radius: 1.5rem; + box-sizing: border-box; + color: inherit; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + font-family: inherit; + font-size: 0.75rem; + font-weight: var(--font-bold); + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-letter-spacing: 0.4px; + -moz-letter-spacing: 0.4px; + -ms-letter-spacing: 0.4px; + letter-spacing: 0.4px; + line-height: 1rem; + min-height: 32px; + min-width: 2rem; + outline: none; + padding: 0 var(--spacing-2x); + text-transform: uppercase; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c2:focus { + outline: none; +} + +.c2:focus { + outline: none; + border-color: #006296; + box-shadow: 0 0 0 2px #00629666; +} + +.c2:not(:disabled) { + cursor: pointer; +} + +.c2 > svg { + color: inherit; +} + +.c12 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #006296; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c12:focus { + outline: none; +} + +.c12:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c12:focus:not(:focus-visible) { + box-shadow: none; +} + +.c12:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c16 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c13 { + color: #006296; + font-size: 0.875rem; +} + +.c13:hover { + color: #003A5A; +} + +.c13:visited { + color: #62a; +} + +.c13:visited svg { + color: #62a; +} + +.c15 { + color: #000000; + display: block; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + -webkit-text-decoration: none; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c15:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c15:hover { + background-color: #DBDEE1; +} + +.c7 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c10 { + background-color: #F1F2F2; + border: 1px solid #DBDEE1; + border-radius: var(--border-radius); + float: left; + height: 38px; + text-align: center; + width: 38px; +} + +.c10 svg { + vertical-align: bottom; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c11 span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c11 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c9 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c9:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c9:hover { + background-color: #DBDEE1; +} + +.c4 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c4 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c4 ul:not(:last-child)::after, +.c4 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + +.c0 { + position: relative; +} + +.c3 { + background-color: transparent; + border-color: transparent; + color: #FFFFFF; + font-size: 0.875rem; + font-weight: var(--font-normal); + padding: 0; + text-transform: unset; +} + +.c3:hover { + background-color: #004E78; +} + +.c6 { + max-width: 350px; + min-width: 200px; + right: 0; + width: initial; +} + +.c1 .c5 { + max-width: 350px; + min-width: 200px; + padding: var(--spacing-3x) 0; + right: 0; + width: initial; +} + +.c1 .c5 .c8 { + padding: var(--spacing-1x) var(--spacing-4x); +} + +.c1 .c5 .c14 { + padding: 0 var(--spacing-4x); +} + +.c1 .c5 h3 { + margin: 0; + padding: 0 var(--spacing-4x); + padding-bottom: var(--spacing-1x); +} + +.c1 .c5 ul:not(:last-child)::after, +.c1 .c5 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: var(--spacing-2x) var(--spacing-4x); +} + + +`; diff --git a/packages/react/src/components/bento-menu-button/bento-menu-button.tsx b/packages/react/src/components/bento-menu-button/bento-menu-button.tsx new file mode 100644 index 0000000000..6521ae6639 --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.tsx @@ -0,0 +1,79 @@ +import React, { + ReactElement, +} from 'react'; +import styled from 'styled-components'; +import { ExternalItem, ExternalItemProps, GroupItem, NavItem, NavItemProps } from '../dropdown-menu/list-items'; +import { StyledNavItem } from '../dropdown-menu/list-items/nav-item'; +import { StyledExternalLink } from '../dropdown-menu/list-items/external-item'; +import { DropdownMenuButton, StyledDropdownMenu } from '../dropdown-menu-button/dropdown-menu-button'; +import { Icon } from '../icon/icon'; + +const StyledDropdownMenuButton = styled(DropdownMenuButton)` + ${StyledDropdownMenu} { + max-width: 350px; + min-width: 200px; + padding: var(--spacing-3x) 0; + right: 0; + width: initial; + + ${StyledNavItem} { + padding: var(--spacing-1x) var(--spacing-4x); + } + + ${StyledExternalLink} { + padding: 0 var(--spacing-4x); + } + + h3 { + margin: 0; + padding: 0 var(--spacing-4x); + padding-bottom: var(--spacing-1x); + } + + ul:not(:last-child)::after, + ol:not(:last-child)::after { + border-bottom: 1px solid ${({ theme }) => theme.greys.grey}; + content: ""; + display: block; + margin: var(--spacing-2x) var(--spacing-4x); + } + } +`; + +interface BentoMenuButtonProps { + productLinks: NavItemProps[]; + externalLinks: ExternalItemProps[]; +} + +export function BentoMenuButton({ + productLinks, + externalLinks, +}: BentoMenuButtonProps): ReactElement { + return ( + }> + + {productLinks.map((product) => ( + + ))} + + + {externalLinks.map((external) => ( + + ))} + + + ); +} diff --git a/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx b/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx index 658382f758..c9ff2cf036 100644 --- a/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx +++ b/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx @@ -6,7 +6,7 @@ import { ChooserButtonGroup } from './chooser-button-group'; jest.mock('../../utils/uuid'); -describe('Chooser Button Group', () => { +describe('Chooser Button GroupItem', () => { const maritalStatus = [ { value: 'single', label: 'Single, living alone or with a roommate' }, { value: 'married', label: 'Married or living with a spouse' }, diff --git a/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx.snap b/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx.snap index 57504551fb..e99700eed2 100644 --- a/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx.snap +++ b/packages/react/src/components/chooser-button-group/chooser-button-group.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Chooser Button Group Matches the snapshot 1`] = ` +exports[`Chooser Button GroupItem Matches the snapshot 1`] = ` Array [ .c3 { --border-radius: 8px; diff --git a/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx new file mode 100644 index 0000000000..014ad18244 --- /dev/null +++ b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx @@ -0,0 +1,115 @@ +import React, { ReactElement } from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { getByTestId } from '../../test-utils/enzyme-selectors'; +import { mountWithTheme, renderWithTheme, shallowWithTheme } from '../../test-utils/renderer'; +import { DropdownMenuButton } from './dropdown-menu-button'; +import { ExternalItem, GroupItem, NavItem } from '../dropdown-menu/list-items'; + +jest.mock('../../utils/uuid'); + +function setup(children: ReactElement): ReactElement { + return ( + + {children} + + ); +} + +const TestGroups = ( + <> + + + + + + + + + + + +); + +describe('DropdownMenuButton', () => { + test('dropdown-menu is open when defaultOpen prop is set to true', () => { + const wrapper = shallowWithTheme( + + {TestGroups} + , + ); + + expect(getByTestId(wrapper, 'menu-dropdownMenu').prop('hidden')).toBe(false); + }); + + test('Opens dropdown-menu when menu-button is clicked', () => { + const wrapper = shallowWithTheme( + + {TestGroups} + , + ); + + getByTestId(wrapper, 'menu-button').simulate('click'); + + expect(getByTestId(wrapper, 'menu-dropdownMenu').prop('hidden')).toBe(false); + }); + + // test('Focuses the first menu-item when menu opens', () => { + // const wrapper = mountWithTheme(setup( + // + // {TestGroups} + // , + // ), { attachTo: document.body }); + // + // getByTestId(wrapper, 'menu-button').simulate('click'); + // const focusedElement = document.activeElement; + // + // expect(focusedElement?.id).toBe('listitem-optionA'); + // }); + + test('Should close nav-menu when escape key is pressed in dropdown-menu', () => { + const wrapper = mountWithTheme(setup( + + {TestGroups} + , + )); + + getByTestId(wrapper, 'listitem-optionA').simulate('keydown', { key: 'Escape' }); + + expect(getByTestId(wrapper, 'menu-dropdownMenu').prop('hidden')).toBe(true); + }); + + test('Focuses menu-button when escape key is pressed in dropdown-menu', () => { + const wrapper = mountWithTheme( + setup( + + {TestGroups} + , + ), + { attachTo: document.body }, + ); + + getByTestId(wrapper, 'listitem-optionA').simulate('keydown', { key: 'Escape' }); + + expect(document.activeElement).toBe(getByTestId(wrapper, 'menu-button').getDOMNode()); + }); + + test('Matches Snapshot', () => { + const tree = renderWithTheme(setup( + + {TestGroups} + , + )); + + expect(tree).toMatchSnapshot(); + }); + + test('Matches Snapshot (defaultOpen)', () => { + const tree = renderWithTheme(setup( + + {TestGroups} + , + )); + + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx.snap b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx.snap new file mode 100644 index 0000000000..95144f4059 --- /dev/null +++ b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx.snap @@ -0,0 +1,788 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DropdownMenuButton Matches Snapshot (defaultOpen) 1`] = ` +.c1 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: inherit; + border: 1px solid; + border-radius: 1.5rem; + box-sizing: border-box; + color: inherit; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + font-family: inherit; + font-size: 0.75rem; + font-weight: var(--font-bold); + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-letter-spacing: 0.4px; + -moz-letter-spacing: 0.4px; + -ms-letter-spacing: 0.4px; + letter-spacing: 0.4px; + line-height: 1rem; + min-height: 32px; + min-width: 2rem; + outline: none; + padding: 0 var(--spacing-2x); + text-transform: uppercase; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c1:focus { + outline: none; +} + +.c1:focus { + outline: none; + border-color: #006296; + box-shadow: 0 0 0 2px #00629666; +} + +.c1:not(:disabled) { + cursor: pointer; +} + +.c1 > svg { + color: inherit; +} + +.c9 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #006296; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c9:focus { + outline: none; +} + +.c9:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c9:focus:not(:focus-visible) { + box-shadow: none; +} + +.c9:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c12 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c10 { + color: #006296; + font-size: 0.875rem; +} + +.c10:hover { + color: #003A5A; +} + +.c10:visited { + color: #62a; +} + +.c10:visited svg { + color: #62a; +} + +.c11 { + color: #000000; + display: block; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + -webkit-text-decoration: none; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c11:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c11:hover { + background-color: #DBDEE1; +} + +.c6 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c8 span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c8 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c7 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c7:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c7:hover { + background-color: #DBDEE1; +} + +.c4 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c4 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c4 ul:not(:last-child)::after, +.c4 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + +.c0 { + position: relative; +} + +.c2 { + background-color: #004E78; + border-color: transparent; + color: #FFFFFF; + font-size: 0.875rem; + font-weight: var(--font-normal); + text-transform: unset; +} + +.c2:hover { + background-color: #004E78; +} + +.c3 { + margin-left: var(--spacing-1x); +} + +.c5 { + max-width: 350px; + min-width: 200px; + right: 0; + width: initial; +} + + +`; + +exports[`DropdownMenuButton Matches Snapshot 1`] = ` +.c1 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: inherit; + border: 1px solid; + border-radius: 1.5rem; + box-sizing: border-box; + color: inherit; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + font-family: inherit; + font-size: 0.75rem; + font-weight: var(--font-bold); + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-letter-spacing: 0.4px; + -moz-letter-spacing: 0.4px; + -ms-letter-spacing: 0.4px; + letter-spacing: 0.4px; + line-height: 1rem; + min-height: 32px; + min-width: 2rem; + outline: none; + padding: 0 var(--spacing-2x); + text-transform: uppercase; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c1:focus { + outline: none; +} + +.c1:focus { + outline: none; + border-color: #006296; + box-shadow: 0 0 0 2px #00629666; +} + +.c1:not(:disabled) { + cursor: pointer; +} + +.c1 > svg { + color: inherit; +} + +.c9 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #006296; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c9:focus { + outline: none; +} + +.c9:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c9:focus:not(:focus-visible) { + box-shadow: none; +} + +.c9:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c12 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c10 { + color: #006296; + font-size: 0.875rem; +} + +.c10:hover { + color: #003A5A; +} + +.c10:visited { + color: #62a; +} + +.c10:visited svg { + color: #62a; +} + +.c11 { + color: #000000; + display: block; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + -webkit-text-decoration: none; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c11:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c11:hover { + background-color: #DBDEE1; +} + +.c6 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c8 span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c8 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c7 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c7:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c7:hover { + background-color: #DBDEE1; +} + +.c4 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c4 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c4 ul:not(:last-child)::after, +.c4 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + +.c0 { + position: relative; +} + +.c2 { + background-color: transparent; + border-color: transparent; + color: #FFFFFF; + font-size: 0.875rem; + font-weight: var(--font-normal); + text-transform: unset; +} + +.c2:hover { + background-color: #004E78; +} + +.c3 { + margin-left: var(--spacing-1x); +} + +.c5 { + max-width: 350px; + min-width: 200px; + right: 0; + width: initial; +} + + +`; diff --git a/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.tsx b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.tsx new file mode 100644 index 0000000000..d9dfa0e2f3 --- /dev/null +++ b/packages/react/src/components/dropdown-menu-button/dropdown-menu-button.tsx @@ -0,0 +1,187 @@ +import React, { + KeyboardEvent, + ReactElement, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import styled from 'styled-components'; +import { useTranslation } from '../../i18n/use-translation'; +import { Theme } from '../../themes'; +import { eventIsInside } from '../../utils/events'; +import { v4 as uuid } from '../../utils/uuid'; +import { AbstractButton } from '../buttons/abstract-button'; +import { useDeviceContext } from '../device-context-provider/device-context-provider'; +import { Icon, IconProps } from '../icon/icon'; +import { DropdownMenu } from '../dropdown-menu/dropdown-menu'; +import { ExternalItemProps, GroupItemProps, NavItemProps } from '../dropdown-menu/list-items'; +import { getRootDocument } from '../../utils/dom'; +import { AvatarProps } from '../avatar/avatar'; + +const StyledNav = styled.nav` + position: relative; +`; + +interface StyledButtonProps { + theme: Theme; + expanded: boolean; + removePadding?: boolean; +} + +const StyledButton = styled(AbstractButton)` + background-color: ${({ expanded, theme }) => (expanded ? theme.main['primary-3'] : 'transparent')}; + border-color: transparent; + color: ${({ theme }) => theme.greys.white}; + font-size: 0.875rem; + font-weight: var(--font-normal); + ${({ removePadding }) => (removePadding ? 'padding: 0;' : '')} + + text-transform: unset; + + &:hover { + background-color: ${({ theme }) => theme.main['primary-3']}; + } +`; + +const StyledRightIcon = styled(Icon)` + margin-left: var(--spacing-1x); +`; + +const Prefix = styled.span` + color: ${({ theme }) => theme.greys['mid-grey']}; + font-size: 0.875rem; + margin-right: var(--spacing-1x); +`; + +export const StyledDropdownMenu = styled(DropdownMenu)` + max-width: 350px; + min-width: 200px; + right: 0; + width: initial; +`; + +interface MenuButtonProps { + label?: string; + /** + * Sets nav's description + * @default 'Menu' + * */ + ariaLabel?: string; + children?: ReactElement | ReactElement[]; + className?: string; + /** + * Sets menu open by default + * @default false + * */ + defaultOpen?: boolean; + /** + * Sets chevron icon + * @default true + * */ + hasCaret?: boolean; + icon?: ReactElement; + id?: string; + prefix?: string; +} + +export function DropdownMenuButton({ + label, + ariaLabel, + children, + className, + defaultOpen = false, + hasCaret = true, + icon, + prefix, + id: providedId, +}: MenuButtonProps): ReactElement { + const { isMobile } = useDeviceContext(); + const { t } = useTranslation('nav-menu-button'); + const id = useMemo(() => providedId || uuid(), [providedId]); + const [isOpen, setOpen] = useState(defaultOpen); + const buttonRef = useRef(null); + const navMenuRef = useRef(null); + const navRef = useRef(null); + + const handleClickOutside: (event: MouseEvent) => void = useCallback((event) => { + const clickIsOutside = !eventIsInside(event, buttonRef.current, navMenuRef.current); + const shouldClose = (navMenuRef.current === null || clickIsOutside) && isOpen; + + if (shouldClose) { + setOpen(false); + } + }, [isOpen]); + + useEffect(() => { + if (children && isOpen) { + const groups = Array.isArray(children) ? children : [children]; + const flatItems = groups.reduce( + (items: ReactElement[], { props }) => ( + items.concat(Array.isArray(props.children) ? props.children : [props.children]) + ), [], + ).filter(({ props }) => !!props.id); + const { props: firstItemProps } = flatItems[0]; + document.getElementById(firstItemProps.id)?.focus(); + } + document.addEventListener('mouseup', handleClickOutside); + + return () => document.removeEventListener('mouseup', handleClickOutside); + }, [handleClickOutside, isOpen, children]); + + function handleNavMenuKeyDown(event: KeyboardEvent): void { + if (event.key === 'Escape') { + setOpen(false); + buttonRef.current?.focus(); + } + + if (isOpen) { + setTimeout(() => { + const focusedElement = getRootDocument(navRef.current)?.activeElement; + const isFocusInsideNav = navRef.current?.contains(focusedElement || null); + + if (!isFocusInsideNav) { + setOpen(false); + } + }); + } + } + + return ( + + setOpen(!isOpen)} + ref={buttonRef} + type="button" + removePadding={icon && !label && !prefix} + > + <> + {icon} + {prefix && {prefix}} + {label} + {hasCaret && ( + + setOpen(false)} + onKeyDown={handleNavMenuKeyDown} + hidden={!isOpen} + > + {children} + + + ); +} diff --git a/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx b/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx new file mode 100644 index 0000000000..a5c3c369db --- /dev/null +++ b/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { getByTestId } from '../../test-utils/enzyme-selectors'; +import { mountWithProviders, renderWithTheme } from '../../test-utils/renderer'; +import { DropdownMenu } from './dropdown-menu'; +import { ExternalItem, GroupItem, NavItem } from './list-items'; + +jest.mock('../../utils/uuid'); + +const TestGroups = ( + <> + + + + + + + + + + + +); + +describe('DropdownMenu', () => { + // test('Calls onChange callback when an option is clicked', () => { + // const callback = jest.fn(); + // const wrapper = mountWithProviders({TestGroups}); + // + // getByTestId(wrapper, 'listitem-optionC').simulate('click'); + // + // expect(callback).toHaveBeenCalledTimes(1); + // }); + + test('Calls onChange callback when enter key is pressed on option', () => { + const callback = jest.fn(); + const wrapper = mountWithProviders({TestGroups}); + + getByTestId(wrapper, 'listitem-optionC').simulate('keydown', { + key: 'Enter', + preventDefault: jest.fn(), + currentTarget: { click: jest.fn() }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + }); + + // test('Calls onKeyDown callback when a key is pressed on option', () => { + // const callback = jest.fn(); + // const wrapper = mountWithProviders({TestGroups}); + // + // getByTestId(wrapper, 'listitem-optionA').simulate('keydown', { key: '' }); + // + // expect(callback).toHaveBeenCalledTimes(1); + // }); + + test('Matches the snapshot', () => { + const tree = renderWithTheme( + + {TestGroups} + , + ); + + expect(tree).toMatchSnapshot(); + }); + + test('Is hidden', () => { + const tree = renderWithTheme( + + + , + ); + + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx.snap b/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx.snap new file mode 100644 index 0000000000..2ab5f9cb46 --- /dev/null +++ b/packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx.snap @@ -0,0 +1,576 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DropdownMenu Is hidden 1`] = ` +.c4 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #006296; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c4:focus { + outline: none; +} + +.c4:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c4:focus:not(:focus-visible) { + box-shadow: none; +} + +.c4:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c7 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c5 { + color: #006296; + font-size: 0.875rem; +} + +.c5:hover { + color: #003A5A; +} + +.c5:visited { + color: #62a; +} + +.c5:visited svg { + color: #62a; +} + +.c6 { + color: #000000; + display: block; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + -webkit-text-decoration: none; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c6:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c6:hover { + background-color: #DBDEE1; +} + +.c1 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c3 span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c3 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c2 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c2:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c2:hover { + background-color: #DBDEE1; +} + +.c0 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c0 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c0 ul:not(:last-child)::after, +.c0 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + + +`; + +exports[`DropdownMenu Matches the snapshot 1`] = ` +.c4 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #006296; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c4:focus { + outline: none; +} + +.c4:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c4:focus:not(:focus-visible) { + box-shadow: none; +} + +.c4:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c7 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c5 { + color: #006296; + font-size: 0.875rem; +} + +.c5:hover { + color: #003A5A; +} + +.c5:visited { + color: #62a; +} + +.c5:visited svg { + color: #62a; +} + +.c6 { + color: #000000; + display: block; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + -webkit-text-decoration: none; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c6:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c6:hover { + background-color: #DBDEE1; +} + +.c1 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c3 span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c3 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c2 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c2:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c2:hover { + background-color: #DBDEE1; +} + +.c0 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c0 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c0 ul:not(:last-child)::after, +.c0 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + + +`; diff --git a/packages/react/src/components/dropdown-menu/dropdown-menu.tsx b/packages/react/src/components/dropdown-menu/dropdown-menu.tsx new file mode 100644 index 0000000000..7dab308e0b --- /dev/null +++ b/packages/react/src/components/dropdown-menu/dropdown-menu.tsx @@ -0,0 +1,77 @@ +import React, { forwardRef, KeyboardEvent, ReactElement, Ref, useMemo } from 'react'; +import styled from 'styled-components'; +import { v4 as uuid } from '../../utils/uuid'; +import { GroupItemProps } from './list-items'; + +const List = styled.div` + background-color: ${({ theme }) => theme.greys.white}; + border: 1px solid ${({ theme }) => theme.greys['dark-grey']}; + border-radius: var(--border-radius); + box-shadow: ${({ theme }) => theme.tokens['overlay-box-shadow']}; + color: ${({ theme }) => theme.greys.black}; + list-style-type: none; + position: absolute; + width: 100%; + + h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); + } + + ul:not(:last-child)::after, + ol:not(:last-child)::after { + border-bottom: 1px solid ${({ theme }) => theme.greys.grey}; + content: ""; + display: block; + margin: 0 var(--spacing-2x); + } +`; + +export interface DropdownMenuProps { + children?: ReactElement | ReactElement[]; + id?: string; + className?: string; + hidden?: boolean; + + onChange?(event: KeyboardEvent): void; + onKeyDown?(event: KeyboardEvent): void; +} + +export const DropdownMenu = forwardRef(({ + children, + className, + id: providedId, + hidden, + onChange, + onKeyDown, +}: DropdownMenuProps, ref: Ref): ReactElement => { + const id = useMemo(() => providedId || uuid(), [providedId]); + + function handleKeyDown(event: KeyboardEvent): void { + if (event.key === 'Enter') { + if (onChange) { + onChange(event); + } + } + + if (onKeyDown) { + onKeyDown(event); + } + } + + return ( + + ); +}); + +DropdownMenu.displayName = 'DropdownMenu'; diff --git a/packages/react/src/components/dropdown-menu/list-items/external-item.tsx b/packages/react/src/components/dropdown-menu/list-items/external-item.tsx new file mode 100644 index 0000000000..dec1cc2518 --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/external-item.tsx @@ -0,0 +1,52 @@ +import React, { ReactElement } from 'react'; +import styled from 'styled-components'; +import { DeviceContextProps, useDeviceContext } from '../../device-context-provider/device-context-provider'; +import { ExternalLink, ExternalLinkProps } from '../../external-link/external-link'; + +export interface ExternalItemProps extends ExternalLinkProps { + id: string; + href: string; +} + +interface ExternalItemsStyledProps extends ExternalItemProps { + $device: DeviceContextProps; +} + +export const StyledExternalLink = styled(ExternalLink)` + color: ${({ theme }) => theme.greys.black}; + display: block; + line-height: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? 2.5 : 2)}rem; + overflow: hidden; + padding: 0 var(--spacing-2x) 0 var(--spacing-3x); + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + + &:focus { + box-shadow: ${({ theme }) => theme.tokens['focus-border-box-shadow-inset']}; + outline: none; + } + + :hover { + background-color: ${({ theme }) => theme.greys.grey}; + } +`; + +export const ExternalItem = ({ + id, + href, + label, +}: ExternalItemProps): ReactElement => { + const device = useDeviceContext(); + return ( +
  • + +
  • + ); +}; diff --git a/packages/react/src/components/dropdown-menu/list-items/group-item.tsx b/packages/react/src/components/dropdown-menu/list-items/group-item.tsx new file mode 100644 index 0000000000..ead6033025 --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/group-item.tsx @@ -0,0 +1,34 @@ +import React, { ReactElement } from 'react'; +import styled from 'styled-components'; +import { NavItemProps } from './nav-item'; + +export interface GroupItemProps { + id: string; + children: ReactElement | ReactElement[]; + ordered?: boolean; + label?: string; +} + +const StyledGroup = styled.ul` + margin: 0; + overflow-y: auto; + padding: 0; +`; + +export const GroupItem = ({ + id, + children, + ordered, + label, +}: GroupItemProps): ReactElement => ( + <> + {label &&

    {label}

    } + + {children} + + +); diff --git a/packages/react/src/components/dropdown-menu/list-items/index.ts b/packages/react/src/components/dropdown-menu/list-items/index.ts new file mode 100644 index 0000000000..faff66cd8a --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/index.ts @@ -0,0 +1,5 @@ +export { ExternalItem, ExternalItemProps } from './external-item'; +export { GroupItem, GroupItemProps } from './group-item'; +export { ItemContent } from './item-content'; +export { LabelItem } from './label-item'; +export { NavItem, NavItemProps } from './nav-item'; diff --git a/packages/react/src/components/dropdown-menu/list-items/item-content.tsx b/packages/react/src/components/dropdown-menu/list-items/item-content.tsx new file mode 100644 index 0000000000..8ecf41cd19 --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/item-content.tsx @@ -0,0 +1,67 @@ +import React, { ReactElement } from 'react'; +import styled from 'styled-components'; +import { DeviceContextProps } from '../../device-context-provider/device-context-provider'; +import { Icon, IconName } from '../../icon/icon'; + +export interface ItemContentProps { + device: DeviceContextProps; + description?: string; + iconName?: IconName; + label: string; +} + +interface LabelContainerStyledProps { + $device: DeviceContextProps; +} + +const IconContainer = styled.span` + background-color: ${({ theme }) => theme.greys['light-grey']}; + border: 1px solid ${({ theme }) => theme.greys.grey}; + border-radius: var(--border-radius); + float: left; + height: 38px; + text-align: center; + width: 38px; + + svg { + vertical-align: bottom; + } +`; + +const LabelContainer = styled.span` + display: flex; + flex-direction: column; + font-size: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? '1rem' : '0.875rem')}; + height: 100%; + line-height: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? 2.5 : 2)}rem; + margin: 0; + padding: 0; + + span { + line-height: 1.25rem; + margin: auto 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + span:nth-of-type(2) { + color: ${({ theme }) => theme.greys['dark-grey']}; + font-size: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? '0.875rem' : '0.75rem')}; + } +`; + +export const ItemContent = ({ + device, + description, + iconName, + label, +}: ItemContentProps): ReactElement => ( + <> + { iconName && } + + {label} + { description && {description} } + + +); diff --git a/packages/react/src/components/dropdown-menu/list-items/label-item.tsx b/packages/react/src/components/dropdown-menu/list-items/label-item.tsx new file mode 100644 index 0000000000..a563122104 --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/label-item.tsx @@ -0,0 +1,49 @@ +import React, { ReactElement } from 'react'; +import styled from 'styled-components'; +import { DeviceContextProps, useDeviceContext } from '../../device-context-provider/device-context-provider'; +import { IconName } from '../../icon/icon'; +import { ItemContent } from './item-content'; + +interface LabelItemProps { + description?: string; + iconName?: IconName; + label: string; +} + +interface LabelContainerStyledProps { + $device: DeviceContextProps; +} + +export const StyledLabelItem = styled.label` + color: ${({ theme }) => theme.greys.black}; + display: block; + font-size: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? '1rem' : '0.875rem')}; + height: 2.5rem; + line-height: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? 2.5 : 2)}rem; + overflow: hidden; + padding: var(--spacing-1x) var(--spacing-2x); + text-decoration: none; + white-space: nowrap; +`; + +export const LabelItem = ({ + description, + iconName, + label, +}: LabelItemProps): ReactElement => { + const device = useDeviceContext(); + return ( +
  • + + + +
  • + ); +}; diff --git a/packages/react/src/components/dropdown-menu/list-items/nav-item.tsx b/packages/react/src/components/dropdown-menu/list-items/nav-item.tsx new file mode 100644 index 0000000000..69f88f22bb --- /dev/null +++ b/packages/react/src/components/dropdown-menu/list-items/nav-item.tsx @@ -0,0 +1,70 @@ +import React, { ReactElement } from 'react'; +import styled from 'styled-components'; +import { NavLink, NavLinkProps } from 'react-router-dom'; +import { DeviceContextProps, useDeviceContext } from '../../device-context-provider/device-context-provider'; +import { IconName } from '../../icon/icon'; +import { ItemContent } from './item-content'; + +export interface NavItemProps extends NavLinkProps { + id: string; + value: string; + description?: string; + iconName?: IconName; + label?: string; +} + +interface NavItemStyledProps extends NavItemProps { + $device: DeviceContextProps; +} + +export const StyledNavItem = styled(NavLink)` + color: ${({ theme }) => theme.greys.black}; + display: block; + font-size: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? '1rem' : '0.875rem')}; + height: 2.5rem; + line-height: ${({ $device: { isMobile, isTablet } }) => ((isTablet || isMobile) ? 2.5 : 2)}rem; + overflow: hidden; + padding: 0 var(--spacing-2x); + text-decoration: none; + white-space: nowrap; + + &:focus { + box-shadow: ${({ theme }) => theme.tokens['focus-border-box-shadow-inset']}; + outline: none; + } + + :hover { + background-color: ${({ theme }) => theme.greys.grey}; + } +`; + +export const NavItem = ({ + id, + to, + value, + description, + exact, + iconName, + label, +}: NavItemProps): ReactElement => { + const device = useDeviceContext(); + return ( +
  • + + + +
  • + ); +}; diff --git a/packages/react/src/components/external-link/external-link.tsx b/packages/react/src/components/external-link/external-link.tsx index bc6890b236..6e576ba152 100644 --- a/packages/react/src/components/external-link/external-link.tsx +++ b/packages/react/src/components/external-link/external-link.tsx @@ -31,7 +31,7 @@ const Link = styled(StyledLink)<{isMobile: boolean}>` } `; -interface ExternalLinkProps { +export interface ExternalLinkProps { className?: string; disabled?: boolean; href?: string; diff --git a/packages/react/src/components/user-profile/user-profile.test.tsx b/packages/react/src/components/user-profile/user-profile.test.tsx index f6c52e6193..14afe63e70 100644 --- a/packages/react/src/components/user-profile/user-profile.test.tsx +++ b/packages/react/src/components/user-profile/user-profile.test.tsx @@ -1,6 +1,5 @@ -import { shallow } from 'enzyme'; import React from 'react'; -import { renderWithProviders } from '../../test-utils/renderer'; +import { mountWithProviders, renderWithProviders } from '../../test-utils/renderer'; import { UserProfile } from './user-profile'; import { getByTestId } from '../../test-utils/enzyme-selectors'; @@ -9,31 +8,35 @@ jest.mock('../../utils/uuid'); const options = [ { label: 'Option A', + id: 'optionA', value: 'optionA', - href: '/testa', + to: '/testa', }, { label: 'Option B', + id: 'optionB', value: 'optionB', - href: '/testb', + to: '/testb', }, { label: 'Option C', + id: 'optionC', value: 'optionC', - href: '/testc', + to: '/testc', }, { label: 'Option D', + id: 'optionD', value: 'optionD', - href: '/testd', + to: '/testd', }, ]; describe('UserProfile', () => { test('should have prefix when usernamePrefix is defined', () => { - const wrapper = shallow(); + const wrapper = mountWithProviders(); - expect(getByTestId(wrapper, 'username-prefix').exists()).toBe(true); + expect(getByTestId(wrapper, 'menu-button-prefix').exists()).toBe(true); }); test('Matches Snapshot (desktop)', () => { diff --git a/packages/react/src/components/user-profile/user-profile.test.tsx.snap b/packages/react/src/components/user-profile/user-profile.test.tsx.snap index 55508d0de8..f46ccb4a30 100644 --- a/packages/react/src/components/user-profile/user-profile.test.tsx.snap +++ b/packages/react/src/components/user-profile/user-profile.test.tsx.snap @@ -59,52 +59,100 @@ exports[`UserProfile Matches Snapshot (defaultOpen) 1`] = ` color: inherit; } -.c6 { - background-color: #FFFFFF; - border: 1px solid #60666E; - border-radius: var(--border-radius); - box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); - list-style-type: none; +.c8 { margin: 0; overflow-y: auto; padding: 0; - position: absolute; - width: 100%; } -.c9 { +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c10 span { + line-height: 1.25rem; + margin: auto 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.c8 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; +.c10 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c9 { color: #000000; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; + display: block; font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: var(--spacing-1x) var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c11 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; line-height: 2rem; overflow: hidden; padding: 0 var(--spacing-2x); -webkit-text-decoration: none; text-decoration: none; + white-space: nowrap; } -.c8:focus { +.c11:focus { box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; outline: none; } -.c8:hover { +.c11:hover { background-color: #DBDEE1; } +.c6 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c6 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c6 ul:not(:last-child)::after, +.c6 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + .c0 { position: relative; } @@ -195,63 +243,104 @@ exports[`UserProfile Matches Snapshot (defaultOpen) 1`] = ` width="16" /> - + + + + Option C + + + + +
  • + + + + Option D + + + +
  • + + `; @@ -314,52 +403,100 @@ exports[`UserProfile Matches Snapshot (desktop) 1`] = ` color: inherit; } -.c6 { - background-color: #FFFFFF; - border: 1px solid #60666E; - border-radius: var(--border-radius); - box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); - list-style-type: none; +.c8 { margin: 0; overflow-y: auto; padding: 0; - position: absolute; - width: 100%; } -.c9 { +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c10 span { + line-height: 1.25rem; + margin: auto 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.c8 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; +.c10 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c9 { color: #000000; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; + display: block; font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: var(--spacing-1x) var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c11 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; line-height: 2rem; overflow: hidden; padding: 0 var(--spacing-2x); -webkit-text-decoration: none; text-decoration: none; + white-space: nowrap; } -.c8:focus { +.c11:focus { box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; outline: none; } -.c8:hover { +.c11:hover { background-color: #DBDEE1; } +.c6 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c6 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c6 ul:not(:last-child)::after, +.c6 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + .c0 { position: relative; } @@ -450,64 +587,105 @@ exports[`UserProfile Matches Snapshot (desktop) 1`] = ` width="16" /> - + + + + Option D + + + + + + `; @@ -570,52 +748,100 @@ exports[`UserProfile Matches Snapshot (mobile) 1`] = ` color: inherit; } -.c6 { - background-color: #FFFFFF; - border: 1px solid #60666E; - border-radius: var(--border-radius); - box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); - list-style-type: none; +.c8 { margin: 0; overflow-y: auto; padding: 0; - position: absolute; - width: 100%; } -.c9 { +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 1rem; + height: 100%; + line-height: 2.5rem; + margin: 0; + padding: 0; +} + +.c10 span { + line-height: 1.25rem; + margin: auto 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.c8 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; +.c10 span:nth-of-type(2) { + color: #60666E; + font-size: 0.875rem; +} + +.c9 { color: #000000; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; + display: block; + font-size: 1rem; + height: 2.5rem; + line-height: 2.5rem; + overflow: hidden; + padding: var(--spacing-1x) var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c11 { + color: #000000; + display: block; font-size: 1rem; + height: 2.5rem; line-height: 2.5rem; overflow: hidden; padding: 0 var(--spacing-2x); -webkit-text-decoration: none; text-decoration: none; + white-space: nowrap; } -.c8:focus { +.c11:focus { box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; outline: none; } -.c8:hover { +.c11:hover { background-color: #DBDEE1; } +.c6 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c6 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c6 ul:not(:last-child)::after, +.c6 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + .c0 { position: relative; } @@ -626,6 +852,7 @@ exports[`UserProfile Matches Snapshot (mobile) 1`] = ` color: #FFFFFF; font-size: 0.875rem; font-weight: var(--font-normal); + padding: 0; text-transform: unset; } @@ -700,64 +927,105 @@ exports[`UserProfile Matches Snapshot (mobile) 1`] = ` - + + + + Option C + + + + +
  • + + + + Option D + + + +
  • + + `; @@ -820,52 +1088,100 @@ exports[`UserProfile Matches Snapshot (with username prefix) 1`] = ` color: inherit; } -.c7 { - background-color: #FFFFFF; - border: 1px solid #60666E; - border-radius: var(--border-radius); - box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); - list-style-type: none; +.c9 { margin: 0; overflow-y: auto; padding: 0; - position: absolute; - width: 100%; } -.c10 { +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 0.875rem; + height: 100%; + line-height: 2rem; + margin: 0; + padding: 0; +} + +.c11 span { + line-height: 1.25rem; + margin: auto 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.c9 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; +.c11 span:nth-of-type(2) { + color: #60666E; + font-size: 0.75rem; +} + +.c10 { color: #000000; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; + display: block; font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + overflow: hidden; + padding: var(--spacing-1x) var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c12 { + color: #000000; + display: block; + font-size: 0.875rem; + height: 2.5rem; line-height: 2rem; overflow: hidden; padding: 0 var(--spacing-2x); -webkit-text-decoration: none; text-decoration: none; + white-space: nowrap; } -.c9:focus { +.c12:focus { box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; outline: none; } -.c9:hover { +.c12:hover { background-color: #DBDEE1; } +.c7 { + background-color: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-shadow: 0 10px 20px 0 rgba(0,0,0,0.19); + color: #000000; + list-style-type: none; + position: absolute; + width: 100%; +} + +.c7 h3 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c7 ul:not(:last-child)::after, +.c7 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + .c0 { position: relative; } @@ -887,6 +1203,12 @@ exports[`UserProfile Matches Snapshot (with username prefix) 1`] = ` margin-left: var(--spacing-1x); } +.c5 { + color: #B7BBC2; + font-size: 0.875rem; + margin-right: var(--spacing-1x); +} + .c8 { max-width: 350px; min-width: 200px; @@ -925,12 +1247,6 @@ exports[`UserProfile Matches Snapshot (with username prefix) 1`] = ` margin-right: var(--spacing-1x); } -.c5 { - color: #B7BBC2; - font-size: 0.875rem; - margin-right: var(--spacing-1x); -} - `; diff --git a/packages/react/src/components/user-profile/user-profile.tsx b/packages/react/src/components/user-profile/user-profile.tsx index d4ede834c8..1d4912004d 100644 --- a/packages/react/src/components/user-profile/user-profile.tsx +++ b/packages/react/src/components/user-profile/user-profile.tsx @@ -1,12 +1,13 @@ +/* eslint-disable react/jsx-props-no-spreading */ import React, { ReactElement } from 'react'; import styled, { css } from 'styled-components'; import { useTranslation } from '../../i18n/use-translation'; import { Avatar } from '../avatar/avatar'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; -import { NavMenuButton } from '../nav-menu-button/nav-menu-button'; -import { NavMenuOption } from '../nav-menu/nav-menu'; +import { DropdownMenuButton } from '../dropdown-menu-button/dropdown-menu-button'; +import { GroupItem, LabelItem, NavItem, NavItemProps } from '../dropdown-menu/list-items'; -const StyledNavMenuButton = styled(NavMenuButton)<{ isMobile: boolean }>` +const StyledDropdownMenuButton = styled(DropdownMenuButton)<{ isMobile: boolean }>` button { ${({ isMobile }) => isMobile && css` height: fit-content; @@ -19,12 +20,6 @@ const StyledAvatar = styled(Avatar)<{ isMobile: boolean }>` margin-right: ${({ isMobile }) => (isMobile ? 0 : 'var(--spacing-1x)')}; `; -const Prefix = styled.span` - color: ${({ theme }) => theme.greys['mid-grey']}; - font-size: 0.875rem; - margin-right: var(--spacing-1x); -`; - interface UserProfileProps { /** * Sets nav's description @@ -38,9 +33,10 @@ interface UserProfileProps { * */ defaultOpen?: boolean; id?: string; + options: NavItemProps[]; username: string; + userEmail?: string; usernamePrefix?: string; - options: NavMenuOption[]; } export function UserProfile({ @@ -50,24 +46,34 @@ export function UserProfile({ id, options, username, + userEmail, usernamePrefix, }: UserProfileProps): ReactElement { const { t } = useTranslation('user-profile'); const { isMobile } = useDeviceContext(); return ( - } isMobile={isMobile} - options={options} + {...(isMobile ? {} : { + label: username, + prefix: usernamePrefix, + })} > - - {usernamePrefix && {usernamePrefix}} - {!isMobile && username} - + + + + + {options.map((action) => ( + + ))} + + ); } diff --git a/packages/react/src/icons/bento.svg b/packages/react/src/icons/bento.svg index a8ed9c6e46..87b8b78d06 100644 --- a/packages/react/src/icons/bento.svg +++ b/packages/react/src/icons/bento.svg @@ -8,6 +8,6 @@ - + diff --git a/packages/react/src/icons/files.svg b/packages/react/src/icons/files.svg index 2aeb5357b9..acc9dfb8ff 100644 --- a/packages/react/src/icons/files.svg +++ b/packages/react/src/icons/files.svg @@ -15,4 +15,4 @@ - \ No newline at end of file + diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 6592a87f06..b328ab4d41 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -4,6 +4,7 @@ export { Button } from './components/buttons/button'; export { IconButton } from './components/buttons/icon-button'; export { NavMenuOption } from './components/nav-menu/nav-menu'; export { NavMenuButton } from './components/nav-menu-button/nav-menu-button'; +export { BentoMenuButton } from './components/bento-menu-button/bento-menu-button'; export { ToggleButtonGroup } from './components/toggle-button-group/toggle-button-group'; // Form Elements @@ -42,6 +43,7 @@ export { EnsoSpinner } from './components/enso-spinner/enso-spinner'; export { ExternalLink } from './components/external-link/external-link'; export { Heading } from './components/heading/heading'; export { ApplicationMenu } from './components/application-menu/application-menu'; +export { DropdownMenu } from './components/dropdown-menu/dropdown-menu'; export { Icon } from './components/icon/icon'; export { SectionalBanner } from './components/sectional-banner/sectional-banner'; export * from './components/progress/progress'; diff --git a/packages/storybook/stories/application-menu.stories.tsx b/packages/storybook/stories/application-menu.stories.tsx index cd40a23a29..8f4106fa9a 100644 --- a/packages/storybook/stories/application-menu.stories.tsx +++ b/packages/storybook/stories/application-menu.stories.tsx @@ -59,10 +59,11 @@ export const WithSkipLinkAndUserProfile: Story = () => (

    Hello world

    diff --git a/packages/storybook/stories/assets/customLogo.svg b/packages/storybook/stories/assets/customLogo.svg index 3531846fca..17c8715899 100644 --- a/packages/storybook/stories/assets/customLogo.svg +++ b/packages/storybook/stories/assets/customLogo.svg @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/packages/storybook/stories/bento-menu-button.stories.tsx b/packages/storybook/stories/bento-menu-button.stories.tsx new file mode 100644 index 0000000000..18b2ca17c2 --- /dev/null +++ b/packages/storybook/stories/bento-menu-button.stories.tsx @@ -0,0 +1,67 @@ +import { ApplicationMenu, BentoMenuButton } from '@equisoft/design-elements-react'; +import { Story } from '@storybook/react'; +import React from 'react'; +import styled from 'styled-components'; +import { decorateWith } from './utils/decorator'; +import { DesktopDecorator } from './utils/device-context-decorator'; +import { RouterDecorator } from './utils/router-decorator'; +import { ExternalItemProps, NavItemProps } from '../../react/src/components/dropdown-menu/list-items'; + +const StyledDiv = styled.div` + height: 180px; +`; + +export default { + title: 'Navigation/Bento Menu', + component: BentoMenuButton, + decorators: [RouterDecorator, decorateWith(StyledDiv)], +}; + +const products: NavItemProps[] = [ + { + id: 'connect', + value: 'connect', + to: 'connect', + label: '/Connect', + description: 'Short app description', + }, + { + id: 'plan', + value: 'plan', + to: 'plan', + label: '/Plan', + description: 'Way to long app description to bust the max-width limit of the dropdown', + }, + { + id: 'analyze', + value: 'analyze', + to: 'analyze', + label: '/Analyze', + description: 'Short app description', + }, +]; + +const resources: ExternalItemProps[] = [ + { + id: 'calculatrice', + href: 'https://calculatrices-financieres.ca/', + label: 'Calculatrice financière', + }, + { + id: 'comparateur', + href: 'https://www.moncomparateurfinancier.com/', + label: 'Mon comparateur financier', + }, + { + id: 'google', + href: 'https://www.google.com/', + label: 'Way to long app link to bust the max-width limit of the dropdown', + }, +]; + +export const Desktop: Story = () => ( + + + +); +Desktop.decorators = [DesktopDecorator]; diff --git a/packages/storybook/stories/user-profile.stories.tsx b/packages/storybook/stories/user-profile.stories.tsx index d0bc367caf..5f6eafc9d9 100644 --- a/packages/storybook/stories/user-profile.stories.tsx +++ b/packages/storybook/stories/user-profile.stories.tsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { Story } from '@storybook/react'; import { ApplicationMenu, UserProfile } from '@equisoft/design-elements-react'; import { DesktopDecorator, MobileDecorator } from './utils/device-context-decorator'; +import { NavItemProps } from '../../react/src/components/dropdown-menu/list-items'; const StyledDiv = styled.div` height: 200px; @@ -27,34 +28,38 @@ export default { const options = [ { + id: 'option-a', label: 'Option A', value: 'optionA', - href: '/testa', + to: '/testa', }, { + id: 'option-b', label: 'Option B', value: 'optionB', - href: '/testb', + to: '/testb', }, { + id: 'option-c', label: 'Option C', value: 'optionC', - href: '/testc', + to: '/testc', }, { + id: 'option-d', label: 'Option D', value: 'optionD', - href: '/testd', + to: '/testd', }, -]; +] as NavItemProps[]; export const Desktop: Story = () => ( - + ); Desktop.decorators = [DesktopDecorator]; export const Mobile: Story = () => ( - + ); Mobile.decorators = [MobileDecorator];