From 85dc77ae7188ef7f302139388d2c801512670275 Mon Sep 17 00:00:00 2001 From: Jean-Simon Garneau Date: Mon, 20 Sep 2021 15:44:41 -0400 Subject: [PATCH] feat(DropdownMenu): added dropdown menu component, created bento menu and update user-profile --- packages/kronos-crm-icons/src/eye-slave.svg | 2 +- packages/kronos-crm-icons/src/recur-slave.svg | 2 +- .../kronos-crm-icons/src/sort-alpha-desc.svg | 2 +- packages/kronos-fna-icons/src/print.svg | 2 +- packages/kronos-fna-icons/src/synchro.svg | 2 +- .../react/src/components/avatar/avatar.tsx | 2 +- .../bento-menu-button.test.tsx | 46 + .../bento-menu-button.test.tsx.snap | 500 +++++++++ .../bento-menu-button/bento-menu-button.tsx | 100 ++ .../src/components/buttons/icon-button.tsx | 17 +- .../chooser-button-group.test.tsx | 2 +- .../chooser-button-group.test.tsx.snap | 2 +- .../dropdown-menu-button.test.tsx | 79 ++ .../dropdown-menu-button.test.tsx.snap | 804 +++++++++++++ .../dropdown-menu-button.tsx | 208 ++++ .../dropdown-menu/dropdown-menu.test.tsx | 49 + .../dropdown-menu/dropdown-menu.test.tsx.snap | 588 ++++++++++ .../dropdown-menu/dropdown-menu.tsx | 46 + .../list-items/external-item.tsx | 52 + .../dropdown-menu/list-items/group-item.tsx | 48 + .../dropdown-menu/list-items/index.ts | 5 + .../dropdown-menu/list-items/item-content.tsx | 89 ++ .../dropdown-menu/list-items/label-item.tsx | 47 + .../dropdown-menu/list-items/nav-item.tsx | 104 ++ .../external-link/external-link.tsx | 2 +- .../react/src/components/heading/heading.tsx | 2 +- .../components/lozenge/lozenge.test.tsx.snap | 7 +- .../react/src/components/lozenge/lozenge.tsx | 7 +- .../user-profile/user-profile.test.tsx | 35 +- .../user-profile/user-profile.test.tsx.snap | 1000 +++++++++-------- .../components/user-profile/user-profile.tsx | 58 +- packages/react/src/i18n/translations.ts | 8 + packages/react/src/icons/bento.svg | 2 +- packages/react/src/icons/files.svg | 2 +- packages/react/src/index.ts | 2 + .../stories/application-menu.stories.tsx | 2 +- .../storybook/stories/assets/customLogo.svg | 2 +- .../stories/bento-menu-button.stories.tsx | 77 ++ .../stories/user-profile.stories.tsx | 19 +- 39 files changed, 3482 insertions(+), 541 deletions(-) create mode 100644 packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx create mode 100644 packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap create mode 100644 packages/react/src/components/bento-menu-button/bento-menu-button.tsx create mode 100644 packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx create mode 100644 packages/react/src/components/dropdown-menu-button/dropdown-menu-button.test.tsx.snap create mode 100644 packages/react/src/components/dropdown-menu-button/dropdown-menu-button.tsx create mode 100644 packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx create mode 100644 packages/react/src/components/dropdown-menu/dropdown-menu.test.tsx.snap create mode 100644 packages/react/src/components/dropdown-menu/dropdown-menu.tsx create mode 100644 packages/react/src/components/dropdown-menu/list-items/external-item.tsx create mode 100644 packages/react/src/components/dropdown-menu/list-items/group-item.tsx create mode 100644 packages/react/src/components/dropdown-menu/list-items/index.ts create mode 100644 packages/react/src/components/dropdown-menu/list-items/item-content.tsx create mode 100644 packages/react/src/components/dropdown-menu/list-items/label-item.tsx create mode 100644 packages/react/src/components/dropdown-menu/list-items/nav-item.tsx create mode 100644 packages/storybook/stories/bento-menu-button.stories.tsx 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..9a7c27b00b --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { renderWithProviders } from '../../test-utils/renderer'; +import { BentoMenuButton } from './bento-menu-button'; +import { ExternalItemProps, NavItemProps } from '../dropdown-menu/list-items'; + +jest.mock('../../utils/uuid'); + +const products: NavItemProps[] = [ + { + label: 'Option A', + value: 'optionA', + href: '/testa', + }, + { + label: 'Option B', + value: 'optionB', + href: '/testb', + }, + { + label: 'Option C', + value: 'optionC', + href: '/testc', + }, + { + label: 'Option D', + value: 'optionD', + href: '/testd', + }, +]; + +const externals: ExternalItemProps[] = [ + { + label: 'Option A', + href: '/testa', + }, +]; + +describe('BentoMenuButton', () => { + test('Matches Snapshot', () => { + const tree = renderWithProviders( + , + ); + + 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..41f09de02a --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap @@ -0,0 +1,500 @@ +// 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; +} + +.c3 { + background-color: #006296; + border-color: #006296; + color: #FFFFFF; + padding: 0; + width: 32px; +} + +.c3:focus { + outline: none; +} + +.c3:focus { + outline: none; + border-color: #006296; + box-shadow: 0 0 0 2px #00629666; +} + +.c3:hover { + background-color: #003A5A; + border-color: #003A5A; +} + +.c3:disabled { + background-color: #84C6EA; + border-color: #84C6EA; +} + +.c16 { + -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; +} + +.c16:focus { + outline: none; +} + +.c16:focus { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c16:focus:not(:focus-visible) { + box-shadow: none; +} + +.c16:focus-visible { + outline: none; + box-shadow: 0 0 0 2px #00629666; + box-shadow: 0 0 0 3px #00629666,0 0 0 1px #006296; +} + +.c20 { + margin-left: var(--spacing-half); + margin-right: 0; +} + +.c17 { + color: #006296; + font-size: 0.875rem; +} + +.c17:hover { + color: #003A5A; +} + +.c17:visited { + color: #62a; +} + +.c17:visited svg { + color: #62a; +} + +.c19 { + 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; +} + +.c19:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c19:hover { + background-color: #DBDEE1; +} + +.c8 { + font-size: 1rem; + font-weight: var(--font-semi-bold); + line-height: 1.5rem; + margin: var(--spacing-3x) 0; +} + +.c10 { + margin: 0; + padding: 0 var(--spacing-2x); + padding-bottom: var(--spacing-1x); +} + +.c11 { + margin: 0; + overflow-y: auto; + padding: 0; +} + +.c11:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: 0 var(--spacing-2x); +} + +.c13 { + -webkit-align-self: flex-start; + -ms-flex-item-align: start; + align-self: flex-start; + background-color: #F1F2F2; + border: 1px solid #DBDEE1; + border-radius: var(--border-radius); + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + margin: 2px 0; + margin-right: var(--spacing-1x); + padding: var(--spacing-1x); +} + +.c15 { + line-height: 1.5rem; + margin: auto 0; + margin-right: var(--spacing-1x); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c14 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + font-size: 0.875rem; + height: 100%; + margin: 0; + overflow: auto; + padding: 0; + padding-left: var(--spacing-half); +} + +.c12 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #000000; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-size: 0.875rem; + height: 2.5rem; + line-height: 2rem; + padding: 0 var(--spacing-2x); + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; +} + +.c12:focus { + box-shadow: inset 0 0 0 3px #00629666,inset 0 0 0 1px #006296; + outline: none; +} + +.c12:hover { + background-color: #DBDEE1; +} + +.c5 { + 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 { + position: relative; +} + +.c4 { + background-color: transparent; + border-color: transparent; + color: #FFFFFF; +} + +.c4:hover { + background-color: #004E78; + border-color: #004E78; +} + +.c7 { + max-width: 350px; + min-width: 200px; + right: 0; + width: initial; +} + +.c1 .c6 { + border-radius: var(--border-radius-2x); + box-sizing: border-box; + max-width: 384px; + min-width: 200px; + padding: var(--spacing-3x) 0; + right: 0; + width: initial; +} + +.c1 .c6 .c18 { + line-height: 1.5rem; + padding: var(--spacing-half) var(--spacing-4x); +} + +.c1 .c6 .c9 { + margin: 0; + padding: 0 var(--spacing-4x); + padding-bottom: calc(var(--spacing-1x) + var(--spacing-half)); +} + +.c1 .c6 ul:not(:last-child)::after, +.c1 .c6 ol:not(:last-child)::after { + border-bottom: 1px solid #DBDEE1; + content: ""; + display: block; + margin: var(--spacing-2x) var(--spacing-4x); + margin-bottom: var(--spacing-3x); +} + + +`; 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..ce4d1ce53c --- /dev/null +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.tsx @@ -0,0 +1,100 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import React, { FunctionComponent, useRef } from 'react'; +import styled from 'styled-components'; +import { ExternalItem, ExternalItemProps, GroupItem, NavItem, NavItemProps } from '../dropdown-menu/list-items'; +import { HtmlLink, 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'; +import { useTranslation } from '../../i18n/use-translation'; +import { StyledHeading } from '../dropdown-menu/list-items/group-item'; +import { useDeviceContext } from '../device-context-provider/device-context-provider'; + +const StyledDropdownMenuButton = styled(DropdownMenuButton)` + ${StyledDropdownMenu} { + border-radius: var(--border-radius-2x); + box-sizing: border-box; + max-width: 384px; + min-width: 200px; + padding: var(--spacing-3x) 0; + right: 0; + width: initial; + + ${StyledNavItem}, ${HtmlLink} { + height: 2.75rem; + padding: var(--spacing-1x) var(--spacing-4x); + } + + ${StyledExternalLink} { + line-height: 1.5rem; + padding: var(--spacing-half) var(--spacing-4x); + } + + ${StyledHeading} { + margin: 0; + padding: 0 var(--spacing-4x); + padding-bottom: calc(var(--spacing-1x) + var(--spacing-half)); + } + + 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); + margin-bottom: var(--spacing-3x); + } + } +`; + +interface BentoMenuButtonProps { + productLinks: NavItemProps[]; + externalLinks: ExternalItemProps[]; +} + +export const BentoMenuButton: FunctionComponent = ({ + productLinks, + externalLinks, +}) => { + const { isMobile } = useDeviceContext(); + const { t } = useTranslation('bento'); + const firstItemRef = useRef(null); + return ( + ( + <> + + {productLinks.map((product, idx) => ( + + ))} + + + {externalLinks.map((external) => ( + + ))} + + + )} + hasCaret={false} + icon={} + firstItemRef={firstItemRef} + /> + ); +}; diff --git a/packages/react/src/components/buttons/icon-button.tsx b/packages/react/src/components/buttons/icon-button.tsx index da8802427e..abaee40717 100644 --- a/packages/react/src/components/buttons/icon-button.tsx +++ b/packages/react/src/components/buttons/icon-button.tsx @@ -1,14 +1,16 @@ import React, { forwardRef, MouseEvent, ReactElement, Ref } from 'react'; import styled from 'styled-components'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; -import { Icon, IconName } from '../icon/icon'; +import { Icon, IconName, IconProps } from '../icon/icon'; import { AbstractButton, getButtonTypeStyles } from './abstract-button'; +import { AvatarProps } from '../avatar/avatar'; type ButtonType = 'primary' | 'secondary' | 'tertiary' | 'destructive'; type Type = 'submit' | 'button' | 'reset'; export interface IconButtonProps { + children?: ReactElement; /** * Visual style * @default primary @@ -39,6 +41,7 @@ const StyledButton = styled(AbstractButton)` `; export const IconButton = forwardRef(({ + children, className, iconName, label, @@ -64,11 +67,13 @@ export const IconButton = forwardRef(({ disabled={disabled} {...props /* eslint-disable-line react/jsx-props-no-spreading */} > -