diff --git a/.changeset/hungry-monkeys-laugh.md b/.changeset/hungry-monkeys-laugh.md new file mode 100644 index 00000000..b47109ab --- /dev/null +++ b/.changeset/hungry-monkeys-laugh.md @@ -0,0 +1,5 @@ +--- +'squareone': minor +--- + +Usage of Reach UI is now removed and replaced with Radix UI. The user menu now uses `GafaelfawrUserMenu` from `@lsst-sqre/squared` and is based on Radix UI's Navigation Menu component. It is customized here to work with the Gafaelawr API to show a log in button for the logged out state, and to show the user's menu with a default log out button for the logged in state. Previously we also used Reach UI for showing an accessible validation alert in the Times Square page parameters UI. For now we've dropped this functionality. diff --git a/.changeset/loud-books-develop.md b/.changeset/loud-books-develop.md new file mode 100644 index 00000000..ccf675da --- /dev/null +++ b/.changeset/loud-books-develop.md @@ -0,0 +1,5 @@ +--- +'@lsst-sqre/squared': minor +--- + +Created GafaelfawrUserMenu based on the Radix UI [navigation-menu](https://www.radix-ui.com/primitives/docs/components/navigation-menu) component. That's the right primitive for an accessible menu that uses `` or Next `Link` elements. The existing Gafaelfawr menu is now `GafaelfawrUserDropdown` for reference (it is based on Radix UI's [dropdown menu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu), but is more appropriate as a menu of buttons. diff --git a/apps/squareone/package.json b/apps/squareone/package.json index 77522144..8133843f 100644 --- a/apps/squareone/package.json +++ b/apps/squareone/package.json @@ -44,8 +44,6 @@ "@lsst-sqre/rubin-style-dictionary": "workspace:*", "@lsst-sqre/squared": "workspace:*", "@microsoft/fetch-event-source": "^2.0.1", - "@reach/alert": "^0.17.0", - "@reach/menu-button": "^0.17.0", "ajv": "^8.11.0", "date-fns": "^3.6.0", "formik": "^2.2.9", diff --git a/apps/squareone/src/components/Header/UserMenu.js b/apps/squareone/src/components/Header/UserMenu.js index 58a90cfd..37d3e835 100644 --- a/apps/squareone/src/components/Header/UserMenu.js +++ b/apps/squareone/src/components/Header/UserMenu.js @@ -1,83 +1,24 @@ /* Menu for a user profile and settings, built on @react/menu-button. */ import PropTypes from 'prop-types'; -import styled from 'styled-components'; import getConfig from 'next/config'; -import { Menu, MenuList, MenuButton, MenuLink } from '@reach/menu-button'; -import '@reach/menu-button/styles.css'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import { getLogoutUrl } from '../../lib/utils/url'; -import useUserInfo from '../../hooks/useUserInfo'; - -const StyledMenuButton = styled(MenuButton)` - background-color: transparent; - color: var(--rsd-component-header-nav-text-color); - border: none; - - &:hover { - color: var(--rsd-component-header-nav-text-hover-color); - } -`; - -const StyledFontAwesomeIcon = styled(FontAwesomeIcon)` - margin-left: 0.25em; - font-size: 0.8em; - opacity: 0.9; -`; - -const StyledMenuList = styled(MenuList)` - font-size: 1rem; - background-color: var(--rsd-component-header-nav-menulist-background-color); - width: 12rem; - border-radius: 0.5rem; - // Top margin is related to the triangle; see :before - margin-top: 10px; - color: var(--rsd-component-header-nav-menulist-text-color); - a { - color: var(--rsd-component-header-nav-menulist-text-color); - } - - &:before { - // Make a CSS triangle on the top of the menu - content: ''; - border: 8px solid transparent; - border-bottom: 8px solid - var(--rsd-component-header-nav-menulist-background-color); - position: absolute; - display: inline-block; - // Top is related to the border size and margin-top of menu list - top: -5px; - right: 9px; - left: auto; - } - - [data-reach-menu-item][data-selected] { - background: var( - --rsd-component-header-nav-menulist-selected-background-color - ); - } -`; +import { GafaelfawrUserMenu } from '@lsst-sqre/squared'; export default function UserMenu({ pageUrl }) { - const { userInfo } = useUserInfo(); - const logoutUrl = getLogoutUrl(pageUrl); const { publicRuntimeConfig } = getConfig(); const { coManageRegistryUrl } = publicRuntimeConfig; return ( - - - {userInfo.username} - - - {coManageRegistryUrl && ( - Account settings - )} - Security tokens - Log out - - + + {coManageRegistryUrl && ( + + Account Settings + + )} + + Security tokens + + ); } diff --git a/apps/squareone/src/components/TimesSquareParameters/ParameterInput.js b/apps/squareone/src/components/TimesSquareParameters/ParameterInput.js index 64466f92..172a2d56 100644 --- a/apps/squareone/src/components/TimesSquareParameters/ParameterInput.js +++ b/apps/squareone/src/components/TimesSquareParameters/ParameterInput.js @@ -1,5 +1,4 @@ import styled from 'styled-components'; -import Alert from '@reach/alert'; export default function ParameterInput({ children, @@ -33,7 +32,7 @@ const ParameterName = styled.p` 'Courier New', monospace; `; -const ErrorMessage = styled(Alert)` +const ErrorMessage = styled.p` color: red; margin-top: 0.2em; margin-bottom: 0.2em; diff --git a/apps/squareone/src/lib/mocks/devstate.js b/apps/squareone/src/lib/mocks/devstate.js index c60b0aea..ff4090db 100644 --- a/apps/squareone/src/lib/mocks/devstate.js +++ b/apps/squareone/src/lib/mocks/devstate.js @@ -3,7 +3,7 @@ // the POST /api/dev/logout method. let DEV_STATE = { - loggedIn: false, + loggedIn: true, username: 'vera', name: 'Vera Rubin', uid: 1234, diff --git a/packages/squared/package.json b/packages/squared/package.json index 1e18a25c..fe76e7bf 100644 --- a/packages/squared/package.json +++ b/packages/squared/package.json @@ -29,6 +29,7 @@ "@lsst-sqre/global-css": "workspace:*", "@lsst-sqre/rubin-style-dictionary": "workspace:*", "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-navigation-menu": "^1.1.4", "react": "^17.0.2", "react-dom": "^17.0.2", "react-feather": "^2.0.10", diff --git a/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.stories.tsx b/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.stories.tsx new file mode 100644 index 00000000..55feb06d --- /dev/null +++ b/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.stories.tsx @@ -0,0 +1,133 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { rest } from 'msw'; +import { SWRConfig } from 'swr'; +import { within, userEvent, screen } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; + +import GafaelfawrUserDropdown from './GafaelfawrUserDropdown'; + +const meta: Meta = { + title: 'Components/GafaelfawrUserDropdown', + component: GafaelfawrUserDropdown, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + // The user menu always shows up on a dark background. + backgrounds: { + default: 'dark', + values: [{ name: 'dark', value: '#1f2121' }], + }, + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +const loggedInAuthHandlers = [ + rest.get('/auth/api/v1/user-info', (req, res, ctx) => { + return res( + ctx.json({ + username: 'someuser', + name: 'Alice Example', + email: 'alice@example.com', + uid: 4123, + gid: 4123, + groups: [ + { + name: 'g_special_users', + id: 123181, + }, + ], + quota: { + api: {}, + notebook: { + cpu: 4, + memory: 16, + }, + }, + }) + ); + }), +]; + +const loggedOutAuthHandlers = [ + rest.get('/auth/api/v1/user-info', (req, res, ctx) => { + return res(ctx.status(401)); + }), +]; + +export const Default: Story = { + args: { + currentUrl: 'http://localhost:6006/somepage', + }, + + parameters: { + msw: { + handlers: { + auth: loggedInAuthHandlers, + }, + }, + }, + + render: (args) => ( + new Map() }}> + + + Account Settings + + + Security tokens + + + + ), +}; + +export const LoggedOut: Story = { + args: { ...Default.args }, + + parameters: { + msw: { + handlers: { + auth: loggedOutAuthHandlers, + }, + }, + }, +}; + +export const OpenedMenu: Story = { + args: { ...Default.args }, + + parameters: { ...Default.parameters }, + + play: async ({ canvasElement }) => { + // Delay so msw can load + const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + await delay(1000); + + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole('button')); + // Using screen rather than canvas because Radix renders the dropdown + // outside the scope of the storybook canvas. + await expect(screen.getByText('Log out')).toBeInTheDocument(); + await expect(screen.getByText('Log out')).toHaveAttribute( + 'href', + 'http://localhost:6006/logout?rd=http%3A%2F%2Flocalhost%3A6006%2F' + ); + }, + + render: (args) => ( + new Map() }}> + + + Account Settings + + + Security tokens + + + + ), +}; diff --git a/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.tsx b/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.tsx new file mode 100644 index 00000000..d71d27f3 --- /dev/null +++ b/packages/squared/src/components/GafaelfawrUserDropdown/GafaelfawrUserDropdown.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +import styled from 'styled-components'; + +import Menu from './Menu'; +import Separator from './Separator'; +import MenuItem from './MenuItem'; +import { getLoginUrl, getLogoutUrl } from './authUrls'; +import useGafaelfawrUser from '../../hooks/useGafaelfawrUser'; + +export interface GafaelfawrUserDropdownProps { + children: React.ReactNode; + /** + * The URL of the current page. Used to construct the login and logout URLs + * with appropriate redirects. + */ + currentUrl: string; +} + +export const GafaelfawrUserDropdown = ({ + children, + currentUrl, +}: GafaelfawrUserDropdownProps) => { + const { user, isLoggedIn } = useGafaelfawrUser(); + // TODO: it'd be nice to integrate the useCurrentUrl hook into + // this component so the user doesn't have to pass this prop. + const logoutUrl = getLogoutUrl(currentUrl); + const loginUrl = getLoginUrl(currentUrl); + if (isLoggedIn && user) { + return ( + + {children} + + ); + } else { + return Log in / Sign up; + } +}; + +const SiteNavLink = styled.a` + color: var(--rsd-component-header-nav-text-color); + + &:hover { + color: var(--rsd-component-header-nav-text-hover-color); + } +`; + +// Associate child components with the parent for easier imports. +GafaelfawrUserDropdown.Item = MenuItem; +GafaelfawrUserDropdown.Separator = Separator; + +export default GafaelfawrUserDropdown; diff --git a/packages/squared/src/components/GafaelfawrUserDropdown/Menu.tsx b/packages/squared/src/components/GafaelfawrUserDropdown/Menu.tsx new file mode 100644 index 00000000..276f1154 --- /dev/null +++ b/packages/squared/src/components/GafaelfawrUserDropdown/Menu.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import styled from 'styled-components'; + +import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu'; +import { ChevronDown } from 'react-feather'; + +import MenuItem from './MenuItem'; +import Separator from './Separator'; + +export interface MenuProps { + children: React.ReactNode; + /** + * The URL to use for the logout link. This is the Gafaelfawr logout endpoint. + */ + logoutHref: string; + /** + * The username to display in the menu trigger. + */ + username: string; +} + +export const Menu = ({ children, logoutHref, username }: MenuProps) => { + return ( + + + + {username} + + + + + + {children} + + + Log out + + + + + + + ); +}; + +/** + * The button that triggers the menu, used in a `DropdownMenu.Trigger`. + */ +const MenuTriggerButton = styled.button` + background-color: transparent; + color: var(--rsd-component-header-nav-text-color); + border: 1px solid transparent; + border-radius: 0.25rem; + + &:focus { + outline: 1px solid var(--rsd-color-primary-500); + } + + &:hover { + color: var(--rsd-component-header-nav-text-hover-color); + } + + svg { + display: inline-block; + width: 1rem; + height: 1rem; + vertical-align: middle; + } + + &[data-state='open'] { + svg { + transform: rotate(180deg); + } + } +`; + +/** + * The menu content container, used in a `DropdownMenu.Portal`. + */ +const StyledContent = styled(RadixDropdownMenu.Content)` + /* This unit for the padding is also the basis for the spacing and + * sizing of the menu items. + */ + --gafaelfawr-user-menu-padding: 0.5rem; + + font-size: 1rem; + background-color: var(--rsd-component-header-nav-menulist-background-color); + min-width: 12rem; + border-radius: 0.5rem; + padding: var(--gafaelfawr-user-menu-padding); + color: var(--rsd-component-header-nav-menulist-text-color); + box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), + 0px 10px 20px -15px rgba(22, 23, 24, 0.2); + animation-duration: 400ms; + animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); + will-change: transform, opacity; + + a { + color: var(--rsd-component-header-nav-menulist-text-color); + } + + .DropdownMenuArrow { + fill: var(--rsd-component-header-nav-menulist-background-color); + } +`; + +export default Menu; diff --git a/packages/squared/src/components/GafaelfawrUserMenu/MenuItem.tsx b/packages/squared/src/components/GafaelfawrUserDropdown/MenuItem.tsx similarity index 100% rename from packages/squared/src/components/GafaelfawrUserMenu/MenuItem.tsx rename to packages/squared/src/components/GafaelfawrUserDropdown/MenuItem.tsx diff --git a/packages/squared/src/components/GafaelfawrUserMenu/Separator.tsx b/packages/squared/src/components/GafaelfawrUserDropdown/Separator.tsx similarity index 100% rename from packages/squared/src/components/GafaelfawrUserMenu/Separator.tsx rename to packages/squared/src/components/GafaelfawrUserDropdown/Separator.tsx diff --git a/packages/squared/src/components/GafaelfawrUserDropdown/authUrls.ts b/packages/squared/src/components/GafaelfawrUserDropdown/authUrls.ts new file mode 100644 index 00000000..356e3814 --- /dev/null +++ b/packages/squared/src/components/GafaelfawrUserDropdown/authUrls.ts @@ -0,0 +1,25 @@ +/* + * Get the Science Platform login URL based on the hostname in the window, + * requesting a redirect back to the current page. + * @param currentUrl The current URL (including host and protocol). + * @returns The login URL. + */ +export function getLoginUrl(currentUrl: string) { + const loginUrl = new URL('/login', currentUrl); + const url = new URL(currentUrl); + loginUrl.searchParams.append('rd', url.href); + return loginUrl.href; +} + +/* + * Get the Science Platform logout URL based on the hostname in the window, + * requesting a redirect back to the homepage. + * @param currentUrl The current URL (including host and protocol). + * @returns The logout URL. + */ +export function getLogoutUrl(currentUrl: string) { + const logoutUrl = new URL('/logout', currentUrl); + const homeUrl = new URL('/', currentUrl); + logoutUrl.searchParams.append('rd', homeUrl.href); + return logoutUrl.href; +} diff --git a/packages/squared/src/components/GafaelfawrUserDropdown/index.ts b/packages/squared/src/components/GafaelfawrUserDropdown/index.ts new file mode 100644 index 00000000..74c0a5ec --- /dev/null +++ b/packages/squared/src/components/GafaelfawrUserDropdown/index.ts @@ -0,0 +1,2 @@ +export * from './GafaelfawrUserDropdown'; +export { default } from './GafaelfawrUserDropdown'; diff --git a/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.stories.tsx b/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.stories.tsx index f0672bf7..ad032cc9 100644 --- a/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.stories.tsx +++ b/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.stories.tsx @@ -18,8 +18,6 @@ const meta: Meta = { values: [{ name: 'dark', value: '#1f2121' }], }, }, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs - tags: ['autodocs'], }; export default meta; @@ -74,59 +72,12 @@ export const Default: Story = { render: (args) => ( new Map() }}> - - Account Settings - - - Security tokens - - - - ), -}; - -export const LoggedOut: Story = { - args: { ...Default.args }, - - parameters: { - msw: { - handlers: { - auth: loggedOutAuthHandlers, - }, - }, - }, -}; - -export const OpenedMenu: Story = { - args: { ...Default.args }, - - parameters: { ...Default.parameters }, - - play: async ({ canvasElement }) => { - // Delay so msw can load - const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); - await delay(1000); - - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole('button')); - // Using screen rather than canvas because Radix renders the dropdown - // outside the scope of the storybook canvas. - await expect(screen.getByText('Log out')).toBeInTheDocument(); - await expect(screen.getByText('Log out')).toHaveAttribute( - 'href', - 'http://localhost:6006/logout?rd=http%3A%2F%2Flocalhost%3A6006%2F' - ); - }, - - render: (args) => ( - new Map() }}> - - - Account Settings - - - Security tokens - + + Account Settings + + + Security tokens + ), diff --git a/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.tsx b/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.tsx index a7c22f09..a3a88e9a 100644 --- a/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.tsx +++ b/packages/squared/src/components/GafaelfawrUserMenu/GafaelfawrUserMenu.tsx @@ -2,11 +2,9 @@ import React from 'react'; import styled from 'styled-components'; -import Menu from './Menu'; -import Separator from './Separator'; -import MenuItem from './MenuItem'; -import { getLoginUrl, getLogoutUrl } from './authUrls'; import useGafaelfawrUser from '../../hooks/useGafaelfawrUser'; +import { getLoginUrl, getLogoutUrl } from './authUrls'; +import Menu, { MenuLink } from './Menu'; export interface GafaelfawrUserMenuProps { children: React.ReactNode; @@ -46,7 +44,6 @@ const SiteNavLink = styled.a` `; // Associate child components with the parent for easier imports. -GafaelfawrUserMenu.Item = MenuItem; -GafaelfawrUserMenu.Separator = Separator; +GafaelfawrUserMenu.Link = MenuLink; export default GafaelfawrUserMenu; diff --git a/packages/squared/src/components/GafaelfawrUserMenu/Menu.tsx b/packages/squared/src/components/GafaelfawrUserMenu/Menu.tsx index 276f1154..65e9e852 100644 --- a/packages/squared/src/components/GafaelfawrUserMenu/Menu.tsx +++ b/packages/squared/src/components/GafaelfawrUserMenu/Menu.tsx @@ -1,11 +1,8 @@ import React from 'react'; import styled from 'styled-components'; - -import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu'; import { ChevronDown } from 'react-feather'; -import MenuItem from './MenuItem'; -import Separator from './Separator'; +import * as RadixNavigationMenu from '@radix-ui/react-navigation-menu'; export interface MenuProps { children: React.ReactNode; @@ -21,32 +18,33 @@ export interface MenuProps { export const Menu = ({ children, logoutHref, username }: MenuProps) => { return ( - - - - {username} - - - - - - {children} - - - Log out - - - - - - + + + + + {username} + + + {children} + Log out + + + + + + + ); }; -/** - * The button that triggers the menu, used in a `DropdownMenu.Trigger`. - */ -const MenuTriggerButton = styled.button` +const MenuRoot = styled(RadixNavigationMenu.Root)``; + +const MenuList = styled(RadixNavigationMenu.List)` + list-style: none; + margin-bottom: 0; +`; + +const MenuTrigger = styled(RadixNavigationMenu.Trigger)` background-color: transparent; color: var(--rsd-component-header-nav-text-color); border: 1px solid transparent; @@ -74,34 +72,71 @@ const MenuTriggerButton = styled.button` } `; -/** - * The menu content container, used in a `DropdownMenu.Portal`. - */ -const StyledContent = styled(RadixDropdownMenu.Content)` +const MenuContent = styled(RadixNavigationMenu.Content)` /* This unit for the padding is also the basis for the spacing and * sizing of the menu items. */ --gafaelfawr-user-menu-padding: 0.5rem; + display: flex; + flex-direction: column; + gap: var(0.25rem); + font-size: 1rem; background-color: var(--rsd-component-header-nav-menulist-background-color); min-width: 12rem; border-radius: 0.5rem; padding: var(--gafaelfawr-user-menu-padding); - color: var(--rsd-component-header-nav-menulist-text-color); box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; +`; + +export const MenuLink = styled(RadixNavigationMenu.Link)` + /* The styling on the menu triggers is overriding this colour. Need to re-address. */ + color: var(--rsd-component-header-nav-menulist-text-color) !important; + border-radius: 0.5rem; + padding: calc(var(--gafaelfawr-user-menu-padding) / 2) + var(--gafaelfawr-user-menu-padding); + margin: calc(var(--gafaelfawr-user-menu-padding) / -2); + margin-bottom: calc(var(--gafaelfawr-user-menu-padding) / 2); + + &:last-of-type { + margin-bottom: 0; + } + + outline: 1px solid transparent; - a { - color: var(--rsd-component-header-nav-menulist-text-color); + &:focus { + outline: 1px solid + var(--rsd-component-header-nav-menulist-selected-background-color); } - .DropdownMenuArrow { - fill: var(--rsd-component-header-nav-menulist-background-color); + &:hover { + background-color: var( + --rsd-component-header-nav-menulist-selected-background-color + ); + color: white !important; } `; +const ContentViewport = styled(RadixNavigationMenu.Viewport)` + /* This pushes the menu leftward to stay on the page. + It's a magic number. Perhaps we can use the pushover API to + position the menu more accurately? + */ + position: relative; + top: 0.5rem; + right: 40%; + height: var(--radix-navigation-menu-viewport-height); + width: var(--radix-navigation-menu-viewport-width); +`; + +const ViewportContainer = styled.div` + position: absolute; + z-index: 1000; +`; + export default Menu; diff --git a/packages/squared/src/index.ts b/packages/squared/src/index.ts index f185e52a..25a04e0b 100644 --- a/packages/squared/src/index.ts +++ b/packages/squared/src/index.ts @@ -1,5 +1,9 @@ /* Components */ export { Button, type ButtonProps } from './Button'; +export { + default as GafaelfawrUserDropdown, + type GafaelfawrUserDropdownProps, +} from './components/GafaelfawrUserDropdown'; export { default as GafaelfawrUserMenu, type GafaelfawrUserMenuProps, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0fb39130..b9f42059 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,12 +62,6 @@ importers: '@microsoft/fetch-event-source': specifier: ^2.0.1 version: 2.0.1 - '@reach/alert': - specifier: ^0.17.0 - version: 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/menu-button': - specifier: ^0.17.0 - version: 0.17.0(react-dom@17.0.2)(react-is@17.0.2)(react@17.0.2) ajv: specifier: ^8.11.0 version: 8.11.0 @@ -112,7 +106,7 @@ importers: version: 15.0.1 styled-components: specifier: ^5.3.6 - version: 5.3.6(react-dom@17.0.2)(react-is@17.0.2)(react@17.0.2) + version: 5.3.6(react-dom@17.0.2)(react-is@18.2.0)(react@17.0.2) swr: specifier: ^1.3.0 version: 1.3.0(react@17.0.2) @@ -229,6 +223,9 @@ importers: '@radix-ui/react-dropdown-menu': specifier: ^2.0.5 version: 2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-navigation-menu': + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) react: specifier: ^17.0.2 version: 17.0.2 @@ -4454,6 +4451,31 @@ packages: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.6 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.19)(react@17.0.2) + '@types/react': 18.2.19 + '@types/react-dom': 18.2.7 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + /@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==} peerDependencies: @@ -4568,6 +4590,40 @@ packages: react-remove-scroll: 2.5.5(@types/react@18.2.19)(react@17.0.2) dev: false + /@radix-ui/react-navigation-menu@1.1.4(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-Cc+seCS3PmWmjI51ufGG7zp1cAAIRqHVw7C9LOA2TZ+R4hG6rDvHcTqIsEEFLmZO3zNVH72jOOE7kKNy8W+RtA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.6 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.19)(react@17.0.2) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2) + '@types/react': 18.2.19 + '@types/react-dom': 18.2.7 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + /@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.19)(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: @@ -4809,7 +4865,6 @@ packages: '@babel/runtime': 7.22.6 '@types/react': 18.2.19 react: 17.0.2 - dev: true /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.19)(react@17.0.2): resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} @@ -4858,155 +4913,12 @@ packages: '@types/react-dom': 18.2.7 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) - dev: true /@radix-ui/rect@1.0.1: resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} dependencies: '@babel/runtime': 7.22.6 - /@reach/alert@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-O70K5LCRJNPMKSX2DVK9GL7VJnyqhV7nAOlcDyx0hGmJzDXUVDIxHAgXO8WHSRekWOaOlup+VeN+R9y+LFBpPA==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/visually-hidden': 0.17.0(react-dom@17.0.2)(react@17.0.2) - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tslib: 2.6.1 - dev: false - - /@reach/auto-id@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-ud8iPwF52RVzEmkHq1twuqGuPA+moreumUHdtgvU3sr3/15BNhwp3KyDLrKKSz0LP1r3V4pSdyF9MbYM8BoSjA==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tslib: 2.6.1 - dev: false - - /@reach/descendants@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-c7lUaBfjgcmKFZiAWqhG+VnXDMEhPkI4kAav/82XKZD6NVvFjsQOTH+v3tUkskrAPV44Yuch0mFW/u5Ntifr7Q==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tslib: 2.6.1 - dev: false - - /@reach/dropdown@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-qBTIGInhxtPHtdj4Pl2XZgZMz3e37liydh0xR3qc48syu7g71sL4nqyKjOzThykyfhA3Pb3/wFgsFJKGTSdaig==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/auto-id': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/descendants': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/popover': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tslib: 2.6.1 - dev: false - - /@reach/menu-button@0.17.0(react-dom@17.0.2)(react-is@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-YyuYVyMZKamPtivoEI6D0UEILYH3qZtg4kJzEAuzPmoR/aHN66NZO75Fx0gtjG1S6fZfbiARaCOZJC0VEiDOtQ==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - react-is: ^16.8.0 || 17.x - dependencies: - '@reach/dropdown': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/popover': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-is: 17.0.2 - tiny-warning: 1.0.3 - tslib: 2.6.1 - dev: false - - /@reach/observe-rect@1.2.0: - resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} - dev: false - - /@reach/popover@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-yYbBF4fMz4Ml4LB3agobZjcZ/oPtPsNv70ZAd7lEC2h7cvhF453pA+zOBGYTPGupKaeBvgAnrMjj7RnxDU5hoQ==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/portal': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/rect': 0.17.0(react-dom@17.0.2)(react@17.0.2) - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tabbable: 4.0.0 - tslib: 2.6.1 - dev: false - - /@reach/portal@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-+IxsgVycOj+WOeNPL2NdgooUdHPSY285wCtj/iWID6akyr4FgGUK7sMhRM9aGFyrGpx2vzr+eggbUmAVZwOz+A==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tiny-warning: 1.0.3 - tslib: 2.6.1 - dev: false - - /@reach/rect@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-3YB7KA5cLjbLc20bmPkJ06DIfXSK06Cb5BbD2dHgKXjUkT9WjZaLYIbYCO8dVjwcyO3GCNfOmPxy62VsPmZwYA==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/observe-rect': 1.2.0 - '@reach/utils': 0.17.0(react-dom@17.0.2)(react@17.0.2) - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tiny-warning: 1.0.3 - tslib: 2.6.1 - dev: false - - /@reach/utils@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-M5y8fCBbrWeIsxedgcSw6oDlAMQDkl5uv3VnMVJ7guwpf4E48Xlh1v66z/1BgN/WYe2y8mB/ilFD2nysEfdGeA==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tiny-warning: 1.0.3 - tslib: 2.6.1 - dev: false - - /@reach/visually-hidden@0.17.0(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-T6xF3Nv8vVnjVkGU6cm0+kWtvliLqPAo8PcZ+WxkKacZsaHTjaZb4v1PaCcyQHmuTNT/vtTVNOJLG0SjQOIb7g==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - tslib: 2.6.1 - dev: false - /@rollup/pluginutils@5.0.2: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} @@ -6629,6 +6541,7 @@ packages: /@storybook/expect@28.1.3-5: resolution: {integrity: sha512-lS1oJnY1qTAxnH87C765NdfvGhksA6hBcbUVI5CHiSbNsEtr456wtg/z+dT9XlPriq1D5t2SgfNL9dBAoIGyIA==} + deprecated: In Storybook 8, this package functionality has been integrated to a new package called @storybook/test, which uses Vitest APIs for an improved experience. When upgrading to Storybook 8 with 'npx storybook@latest upgrade', you will get prompted and will get an automigration for the new package. Please migrate when you can. dependencies: '@types/jest': 28.1.3 dev: true @@ -8885,7 +8798,7 @@ packages: babel-plugin-syntax-jsx: 6.18.0 lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.6(react-dom@17.0.2)(react-is@17.0.2)(react@17.0.2) + styled-components: 5.3.6(react-dom@17.0.2)(react-is@18.2.0)(react@17.0.2) /babel-plugin-syntax-jsx@6.18.0: resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==} @@ -16688,6 +16601,7 @@ packages: /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true /react-is@18.1.0: resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==} @@ -18034,29 +17948,6 @@ packages: inline-style-parser: 0.1.1 dev: false - /styled-components@5.3.6(react-dom@17.0.2)(react-is@17.0.2)(react@17.0.2): - resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==} - engines: {node: '>=10'} - requiresBuild: true - peerDependencies: - react: '>= 16.8.0' - react-dom: '>= 16.8.0' - react-is: '>= 16.8.0' - dependencies: - '@babel/helper-module-imports': 7.22.5 - '@babel/traverse': 7.22.8(supports-color@5.5.0) - '@emotion/is-prop-valid': 1.2.1 - '@emotion/stylis': 0.8.5 - '@emotion/unitless': 0.7.5 - babel-plugin-styled-components: 2.0.7(styled-components@5.3.6) - css-to-react-native: 3.2.0 - hoist-non-react-statics: 3.3.2 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-is: 17.0.2 - shallowequal: 1.1.0 - supports-color: 5.5.0 - /styled-components@5.3.6(react-dom@17.0.2)(react-is@18.2.0)(react@17.0.2): resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==} engines: {node: '>=10'} @@ -18079,7 +17970,6 @@ packages: react-is: 18.2.0 shallowequal: 1.1.0 supports-color: 5.5.0 - dev: false /styled-jsx@5.0.2(@babel/core@7.22.0)(react@17.0.2): resolution: {integrity: sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==} @@ -18229,10 +18119,6 @@ packages: resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} dev: true - /tabbable@4.0.0: - resolution: {integrity: sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==} - dev: false - /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} diff --git a/turbo.json b/turbo.json index cef4b6bf..65f6da59 100644 --- a/turbo.json +++ b/turbo.json @@ -8,7 +8,8 @@ "lint": {}, "dev": { "cache": false, - "persistent": true + "persistent": true, + "dependsOn": ["^build"] }, "storybook": { "cache": false,