Skip to content

Commit

Permalink
Move Help section to the Topbar (#12059)
Browse files Browse the repository at this point in the history
Closes: enso-org/cloud-v2#1699

This PR is stacked on top of:
1. #12079
2. #12080

And ***shall*** be reviewed in order.

This PR _adds_ new 3 items in the topbar: what's new, community and docs. This should improve discoverability for the users and simplify their day-to-day job.

---

***Note***: This PR ***does not*** remove related items from the `Discovery` dialog.
  • Loading branch information
MrFlashAccount authored Jan 24, 2025
1 parent b3dbfa9 commit 092d7c0
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 227 deletions.
15 changes: 13 additions & 2 deletions app/common/src/text/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@
"versions": "Versions",
"properties": "Properties",
"projectSessions": "Sessions",
"docs": "Docs",
"docs": "Documentation",
"datalink": "Datalink",
"secret": "Secret",
"createDatalink": "Create Datalink",
Expand All @@ -295,6 +295,7 @@
"options": "Options",
"googleIcon": "Google icon",
"gitHubIcon": "GitHub icon",
"more": "More",
"close": "Close",

"enterSecretPath": "Enter secret path",
Expand Down Expand Up @@ -587,8 +588,18 @@
"newPasswordPlaceholder": "Enter your new password",
"confirmNewPasswordLabel": "Confirm new password",
"confirmNewPasswordPlaceholder": "Confirm your new password",

"projectName": "Project name",
"help": "Help",
"community": "Community",
"componentExamples": "Component examples",
"enso101": "Enso 101",
"askAQuestion": "Ask a question",
"whatsNew": "What's new",
"selectTemplate": "Discover Enso Analytics",
"chooseATemplate": "Choose a template",
"basicTemplates": "Basic templates",
"advancedTemplates": "Advanced templates",
"startWithTemplate": "Start with a template",
"welcomeSubtitle": "Explore templates, plugins, and data sources to kickstart your next big idea.",
"newsItem3Beta": "Read what’s new in Enso",
"newsItem3BetaDescription": "Learn about new features and whats coming soon.",
Expand Down
17 changes: 11 additions & 6 deletions app/gui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,32 @@ const reactPreview: ReactPreview = {
// Decorators are applied in the reverse order they are defined
decorators: [
(Story, context) => {
const [portalRoot, setPortalRoot] = useState<Element | null>(null)
const [roots, setRoots] = useState<{ appRoot: HTMLElement; portalRoot: HTMLElement } | null>(
null,
)

useLayoutEffect(() => {
const appRoot = document.querySelector('#enso-app')
invariant(appRoot instanceof HTMLElement, 'AppRoot element not found')

const portalRoot = document.querySelector('#enso-portal-root')
invariant(portalRoot, 'PortalRoot element not found')
invariant(portalRoot instanceof HTMLElement, 'PortalRoot element not found')

setPortalRoot(portalRoot)
setRoots({ appRoot, portalRoot })
}, [])

if (!portalRoot) return <></>
if (!roots) return <></>

return (
<UIProviders locale="en-US" portalRoot={portalRoot}>
<UIProviders locale="en-US" {...roots}>
<Story {...context} />
</UIProviders>
)
},

(Story, context) => (
<>
<div className="enso-app">
<div id="enso-app" className="enso-app">
<Story {...context} />
</div>

Expand Down
27 changes: 0 additions & 27 deletions app/gui/integration-test/dashboard/assetsTableFeatures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,6 @@ function locateRootDirectoryDropzone(page: Page) {

const PASS_TIMEOUT = 5_000

test('extra columns should stick to right side of assets table', ({ page }) =>
mockAllAndLogin({ page })
.withAssetsTable(async (table) => {
await table.evaluate((element) => {
let scrollableParent: HTMLElement | SVGElement | null = element
while (
scrollableParent != null &&
scrollableParent.scrollWidth <= scrollableParent.clientWidth
) {
scrollableParent = scrollableParent.parentElement
}
scrollableParent?.scrollTo({ left: 999999, behavior: 'instant' })
})
})
.withAssetsTable(async (assetsTable, _, thePage) => {
const extraColumns = locateExtraColumns(thePage)
await expect(async () => {
const extraColumnsRight = await extraColumns.evaluate(
(element) => element.getBoundingClientRect().right,
)
const assetsTableRight = await assetsTable.evaluate(
(element) => element.getBoundingClientRect().right,
)
expect(extraColumnsRight).toEqual(assetsTableRight - 8)
}).toPass({ timeout: PASS_TIMEOUT })
}))

test('extra columns should stick to top of scroll container', ({ page }) =>
mockAllAndLogin({
page,
Expand Down
10 changes: 8 additions & 2 deletions app/gui/src/ReactRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,27 @@ export default function ReactRoot(props: ReactRootProps) {
const { config, queryClient, onAuthenticated } = props

const httpClient = new HttpClient()

const supportsDeepLinks = !IS_DEV_MODE && !isOnLinux() && isOnElectron()

const appRoot = document.querySelector('#enso-app')
invariant(appRoot instanceof HTMLElement, 'AppRoot element not found')

const portalRoot = document.querySelector('#enso-portal-root')
invariant(portalRoot instanceof HTMLElement, 'PortalRoot element not found')

const shouldUseAuthentication = config.authentication.enabled
const projectManagerUrl =
(config.engine.projectManagerUrl || resolveEnvUrl($config.PROJECT_MANAGER_URL)) ?? null
const ydocUrl = (config.engine.ydocUrl || resolveEnvUrl($config.YDOC_SERVER_URL)) ?? null
const initialProjectName = config.startup.project || null
invariant(portalRoot, 'PortalRoot element not found')
const isCloudBuild = $config.CLOUD_BUILD === 'true'

return (
<StrictMode>
<QueryClientProvider client={queryClient}>
<ErrorBoundary>
<UIProviders locale="en-US" portalRoot={portalRoot}>
<UIProviders locale="en-US" portalRoot={portalRoot} appRoot={appRoot}>
<Suspense fallback={<LoadingScreen />}>
<OfflineNotificationManager>
<LoggerProvider logger={console}>
Expand Down
1 change: 0 additions & 1 deletion app/gui/src/dashboard/components/AnimatedBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const DEFAULT_TRANSITION: Transition = {
mass: 0.3,
velocity: 8,
}

/* eslint-enable @typescript-eslint/no-magic-numbers */

/** `<AnimatedBackground />` component visually highlights selected items by sliding a background into view when hovered over or clicked. */
Expand Down
179 changes: 100 additions & 79 deletions app/gui/src/dashboard/components/AriaComponents/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ import * as suspense from '#/components/Suspense'

import * as mergeRefs from '#/utilities/mergeRefs'

import { DialogDismiss } from '#/components/AriaComponents'
import { DialogDismiss, ResetButtonGroupContext } from '#/components/AriaComponents'
import { useEventCallback } from '#/hooks/eventCallbackHooks'
import { useMeasure } from '#/hooks/measureHooks'
import { LayoutGroup, motion, type Spring } from '#/utilities/motion'
import type { VariantProps } from '#/utilities/tailwindVariants'
import { tv } from '#/utilities/tailwindVariants'
import { unsafeWriteValue } from '#/utilities/write'
import { useRootContext } from '../../UIProviders'
import { Close } from './Close'
import * as dialogProvider from './DialogProvider'
import * as dialogStackProvider from './DialogStackProvider'
import { DialogTrigger } from './DialogTrigger'
import type * as types from './types'
import * as utlities from './utilities'
import { DIALOG_BACKGROUND } from './variants'

// eslint-disable-next-line no-restricted-syntax
const MotionDialog = motion(aria.Dialog)

Expand Down Expand Up @@ -254,6 +257,8 @@ function DialogContent(props: DialogContentProps) {
const scrollerRef = React.useRef<HTMLDivElement | null>(null)
const dialogId = aria.useId()

const { appRoot } = useRootContext()

const titleId = `${dialogId}-title`
const padding = paddingRaw ?? (type === 'modal' ? 'medium' : 'xlarge')
const isFullscreen = type === 'fullscreen'
Expand Down Expand Up @@ -297,6 +302,20 @@ function DialogContent(props: DialogContentProps) {
}
}, [isFullscreen])

React.useEffect(() => {
if (isFullscreen && modalState.isOpen) {
unsafeWriteValue(appRoot.style, 'scale', '0.99')
unsafeWriteValue(appRoot.style, 'filter', 'blur(8px)')
unsafeWriteValue(appRoot.style, 'willChange', 'scale, filter')

return () => {
unsafeWriteValue(appRoot.style, 'scale', '')
unsafeWriteValue(appRoot.style, 'filter', '')
unsafeWriteValue(appRoot.style, 'willChange', '')
}
}
}, [isFullscreen, modalState, appRoot])

const styles = variants({
className,
type,
Expand All @@ -322,86 +341,88 @@ function DialogContent(props: DialogContentProps) {
}

return (
<LayoutGroup>
<MotionDialog
layout
transition={TRANSITION}
style={{ height: getDialogHeight() }}
id={dialogId}
onLayoutAnimationStart={() => {
if (scrollerRef.current) {
scrollerRef.current.style.overflowY = 'clip'
}
}}
onLayoutAnimationComplete={() => {
if (scrollerRef.current) {
scrollerRef.current.style.overflowY = ''
}
}}
ref={(ref: HTMLDivElement | null) => {
mergeRefs.mergeRefs(dialogRef, (element) => {
if (element) {
// This is a workaround for the `data-testid` attribute not being
// supported by the 'react-aria-components' library.
// We need to set the `data-testid` attribute on the dialog element
// so that we can use it in our tests.
// This is a temporary solution until we refactor the Dialog component
// to use `useDialog` hook from the 'react-aria-components' library.
// this will allow us to set the `data-testid` attribute on the dialog
element.dataset.testid = testId
<ResetButtonGroupContext>
<LayoutGroup>
<MotionDialog
layout
transition={TRANSITION}
style={{ height: getDialogHeight() }}
id={dialogId}
onLayoutAnimationStart={() => {
if (scrollerRef.current) {
scrollerRef.current.style.overflowY = 'clip'
}
})(ref)
}}
className={styles.base()}
aria-labelledby={titleId}
{...ariaDialogProps}
>
{(opts) => (
<>
<motion.div layout className="w-full" transition={{ duration: 0 }}>
<DialogHeader
closeButton={closeButton}
title={title}
titleId={titleId}
scrollerRef={scrollerRef}
fitContent={fitContent}
hideCloseButton={hideCloseButton}
padding={padding}
rounded={rounded}
size={size}
type={type}
headerDimensionsRef={headerDimensionsRef}
close={opts.close}
variants={variants}
/>
</motion.div>

<motion.div
layout
layoutScroll
className={styles.scroller()}
ref={scrollerRef}
transition={{ duration: 0 }}
>
<DialogBody
close={opts.close}
contentDimensionsRef={contentDimensionsRef}
dialogId={dialogId}
headerDimensionsRef={headerDimensionsRef}
scrollerRef={scrollerRef}
measurerWrapperClassName={styles.measurerWrapper()}
contentClassName={styles.content()}
type={type}
}}
onLayoutAnimationComplete={() => {
if (scrollerRef.current) {
scrollerRef.current.style.overflowY = ''
}
}}
ref={(ref: HTMLDivElement | null) => {
mergeRefs.mergeRefs(dialogRef, (element) => {
if (element) {
// This is a workaround for the `data-testid` attribute not being
// supported by the 'react-aria-components' library.
// We need to set the `data-testid` attribute on the dialog element
// so that we can use it in our tests.
// This is a temporary solution until we refactor the Dialog component
// to use `useDialog` hook from the 'react-aria-components' library.
// this will allow us to set the `data-testid` attribute on the dialog
element.dataset.testid = testId
}
})(ref)
}}
className={styles.base()}
aria-labelledby={titleId}
{...ariaDialogProps}
>
{(opts) => (
<>
<motion.div layout className="w-full" transition={{ duration: 0 }}>
<DialogHeader
closeButton={closeButton}
title={title}
titleId={titleId}
scrollerRef={scrollerRef}
fitContent={fitContent}
hideCloseButton={hideCloseButton}
padding={padding}
rounded={rounded}
size={size}
type={type}
headerDimensionsRef={headerDimensionsRef}
close={opts.close}
variants={variants}
/>
</motion.div>

<motion.div
layout
layoutScroll
className={styles.scroller()}
ref={scrollerRef}
transition={{ duration: 0 }}
>
{children}
</DialogBody>
</motion.div>
</>
)}
</MotionDialog>

<dialogStackProvider.DialogStackRegistrar id={dialogId} type={TYPE_TO_DIALOG_TYPE[type]} />
</LayoutGroup>
<DialogBody
close={opts.close}
contentDimensionsRef={contentDimensionsRef}
dialogId={dialogId}
headerDimensionsRef={headerDimensionsRef}
scrollerRef={scrollerRef}
measurerWrapperClassName={styles.measurerWrapper()}
contentClassName={styles.content()}
type={type}
>
{children}
</DialogBody>
</motion.div>
</>
)}
</MotionDialog>

<dialogStackProvider.DialogStackRegistrar id={dialogId} type={TYPE_TO_DIALOG_TYPE[type]} />
</LayoutGroup>
</ResetButtonGroupContext>
)
}

Expand Down
Loading

0 comments on commit 092d7c0

Please sign in to comment.