From 3a4b942aae3d0ea2f8fc02948c95f13ba7ea520c Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Wed, 7 Aug 2024 10:49:24 -0400 Subject: [PATCH] Help tab (#10771) Add context sensitive help to right side panel; implements #10582. https://github.com/user-attachments/assets/0d6bce44-735a-4deb-86c9-72a088ab1d59 # Important Notes - The rightmost (i.e. current) breadcrumb is no longer clickable. --- app/dashboard/src/layouts/TabBar.tsx | 49 ++--------- .../{docPanel.spec.ts => rightPanel.spec.ts} | 21 +++++ app/gui2/src/components/ComponentBrowser.vue | 15 +--- .../src/components/ComponentDocumentation.vue | 50 +++++++++++ app/gui2/src/components/DockPanel.vue | 81 ++++++++++++----- .../src/components/DocumentationEditor.vue | 45 ++++++++-- .../DocumentationPanel.vue | 4 +- .../DocumentationPanel/DocsBreadcrumb.vue | 3 +- .../DocumentationPanel/DocsBreadcrumbs.vue | 8 +- app/gui2/src/components/GraphEditor.vue | 73 ++++++++------- .../src/components/GraphEditor/GraphNode.vue | 2 +- .../components/GraphEditor/NodeWidgetTree.vue | 2 +- app/gui2/src/composables/selection.ts | 13 +++ app/gui2/src/stores/graph/graphDatabase.ts | 18 ++-- app/ide-desktop/common/package.json | 7 +- .../utilities/style/__tests__/tabBar.test.ts | 50 +++++++++++ .../common/src/utilities/style/tabBar.ts | 88 +++++++++++++++++++ pnpm-lock.yaml | 3 + 18 files changed, 397 insertions(+), 135 deletions(-) rename app/gui2/e2e/{docPanel.spec.ts => rightPanel.spec.ts} (69%) create mode 100644 app/gui2/src/components/ComponentDocumentation.vue rename app/gui2/src/components/{ComponentBrowser => }/DocumentationPanel.vue (98%) create mode 100644 app/ide-desktop/common/src/utilities/style/__tests__/tabBar.test.ts create mode 100644 app/ide-desktop/common/src/utilities/style/tabBar.ts diff --git a/app/dashboard/src/layouts/TabBar.tsx b/app/dashboard/src/layouts/TabBar.tsx index 9246e9c5f4..ff6e82c14d 100644 --- a/app/dashboard/src/layouts/TabBar.tsx +++ b/app/dashboard/src/layouts/TabBar.tsx @@ -5,6 +5,7 @@ import * as reactQuery from '@tanstack/react-query' import invariant from 'tiny-invariant' import type * as text from 'enso-common/src/text' +import * as tabBar from 'enso-common/src/utilities/style/tabBar' import * as projectHooks from '#/hooks/projectHooks' @@ -82,39 +83,10 @@ export default function TabBar(props: TabBarProps) { selectedTabRef.current = element const bounds = element.getBoundingClientRect() const rootBounds = backgroundElement.getBoundingClientRect() - const tabLeft = bounds.left - rootBounds.left + TAB_RADIUS_PX - const tabRight = bounds.right - rootBounds.left - TAB_RADIUS_PX - const rightSegments = [ - 'M 0 0', - `L ${rootBounds.width + window.outerWidth} 0`, - `L ${rootBounds.width + window.outerWidth} ${rootBounds.height}`, - `L ${tabRight + TAB_RADIUS_PX} ${rootBounds.height}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${tabRight} ${rootBounds.height - TAB_RADIUS_PX}`, - ] - const leftSegments = [ - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${tabLeft - TAB_RADIUS_PX} ${rootBounds.height}`, - `L 0 ${rootBounds.height}`, - 'Z', - ] - const segments = [ - ...rightSegments, - `L ${tabRight} ${TAB_RADIUS_PX}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 0 ${tabRight - TAB_RADIUS_PX} 0`, - `L ${tabLeft + TAB_RADIUS_PX} 0`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 0 ${tabLeft} ${TAB_RADIUS_PX}`, - `L ${tabLeft} ${rootBounds.height - TAB_RADIUS_PX}`, - ...leftSegments, - ] - backgroundElement.style.clipPath = `path("${segments.join(' ')}")` - const rootSegments = [ - ...rightSegments, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${tabRight - TAB_RADIUS_PX} ${rootBounds.height}`, - `L ${tabLeft + TAB_RADIUS_PX} ${rootBounds.height}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${tabLeft} ${rootBounds.height - TAB_RADIUS_PX}`, - ...leftSegments, - ] + const { clipPath, rootClipPath } = tabBar.barClipPath(bounds, rootBounds, TAB_RADIUS_PX) + backgroundElement.style.clipPath = clipPath if (rootElement) { - rootElement.style.clipPath = `path("${rootSegments.join(' ')}")` + rootElement.style.clipPath = rootClipPath } } } @@ -214,18 +186,7 @@ export function Tab(props: InternalTabProps) { const element = ref.current if (element) { const bounds = element.getBoundingClientRect() - const segments = [ - `M 0 ${bounds.height}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 0 ${TAB_RADIUS_PX} ${bounds.height - TAB_RADIUS_PX}`, - `L ${TAB_RADIUS_PX} ${TAB_RADIUS_PX}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${TAB_RADIUS_PX * 2} 0`, - `L ${bounds.width - TAB_RADIUS_PX * 2} 0`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 1 ${bounds.width - TAB_RADIUS_PX} ${TAB_RADIUS_PX}`, - `L ${bounds.width - TAB_RADIUS_PX} ${bounds.height - TAB_RADIUS_PX}`, - `A ${TAB_RADIUS_PX} ${TAB_RADIUS_PX} 0 0 0 ${bounds.width} ${bounds.height}`, - 'Z', - ] - element.style.clipPath = `path("${segments.join(' ')}")` + element.style.clipPath = tabBar.tabClipPath(bounds, TAB_RADIUS_PX) } } }) diff --git a/app/gui2/e2e/docPanel.spec.ts b/app/gui2/e2e/rightPanel.spec.ts similarity index 69% rename from app/gui2/e2e/docPanel.spec.ts rename to app/gui2/e2e/rightPanel.spec.ts index b1ee73b4ba..d8cc94aa46 100644 --- a/app/gui2/e2e/docPanel.spec.ts +++ b/app/gui2/e2e/rightPanel.spec.ts @@ -1,5 +1,6 @@ import { expect, test } from 'playwright/test' import * as actions from './actions' +import { mockMethodCallInfo } from './expressionUpdates' import { CONTROL_KEY } from './keyboard' import * as locate from './locate' @@ -48,3 +49,23 @@ test('Doc panel focus (regression #10471)', async ({ page }) => { expect(content.includes('The main TEST method')).toBe(true) await expect(locate.rightDock(page)).toContainText('The main TEST method') }) + +test('Component help', async ({ page }) => { + await actions.goToGraph(page, false) + await locate.rightDock(page).getByRole('button', { name: 'Help' }).click() + await expect(locate.rightDock(page)).toHaveText(/Select a single component/) + + await locate.graphNodeByBinding(page, 'final').click() + await expect(locate.rightDock(page)).toHaveText(/No documentation available/) + + await mockMethodCallInfo(page, 'data', { + methodPointer: { + module: 'Standard.Base.Data', + definedOnType: 'Standard.Base.Data', + name: 'read', + }, + notAppliedArguments: [0, 1, 2], + }) + await locate.graphNodeByBinding(page, 'data').click() + await expect(locate.rightDock(page)).toHaveText(/Reads a file into Enso/) +}) diff --git a/app/gui2/src/components/ComponentBrowser.vue b/app/gui2/src/components/ComponentBrowser.vue index 0f803fa954..c50539caca 100644 --- a/app/gui2/src/components/ComponentBrowser.vue +++ b/app/gui2/src/components/ComponentBrowser.vue @@ -1,11 +1,11 @@ + + + + diff --git a/app/gui2/src/components/DockPanel.vue b/app/gui2/src/components/DockPanel.vue index 41e676c32e..2ef0d06c16 100644 --- a/app/gui2/src/components/DockPanel.vue +++ b/app/gui2/src/components/DockPanel.vue @@ -6,15 +6,21 @@ import ToggleIcon from '@/components/ToggleIcon.vue' import { useResizeObserver } from '@/composables/events' import { Rect } from '@/util/data/rect' import { Vec2 } from '@/util/data/vec2' +import { tabClipPath } from 'enso-common/src/utilities/style/tabBar' import { computed, ref } from 'vue' const MIN_DOCK_SIZE_PX = 200 +const TAB_EDGE_MARGIN_PX = 4 +const TAB_SIZE_PX = { width: 48 - TAB_EDGE_MARGIN_PX, height: 48 } +const TAB_RADIUS_PX = 8 -const toolbarElement = ref() -const slideInPanel = ref() +type Tab = 'docs' | 'help' const show = defineModel('show', { required: true }) const size = defineModel('size') +const tab = defineModel('tab') + +const slideInPanel = ref() const computedSize = useResizeObserver(slideInPanel) const computedBounds = computed(() => new Rect(Vec2.Zero, computedSize.value)) @@ -26,6 +32,14 @@ function clampSize(size: number) { const style = computed(() => ({ width: size.value != null ? `${clampSize(size.value)}px` : 'var(--right-dock-default-width)', })) + +const tabStyle = { + clipPath: tabClipPath(TAB_SIZE_PX, TAB_RADIUS_PX, 'right'), + width: `${TAB_SIZE_PX.width}px`, + height: `${TAB_SIZE_PX.height}px`, + margin: `${-TAB_RADIUS_PX}px ${TAB_EDGE_MARGIN_PX}px ${-TAB_RADIUS_PX}px 0`, + paddingLeft: `${TAB_EDGE_MARGIN_PX / 2}px`, +}