Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:enso-org/enso into wip/farmaazon…
Browse files Browse the repository at this point in the history
…/fix-integration-test
  • Loading branch information
farmaazon committed Nov 22, 2024
2 parents 7092738 + a2e87d3 commit 2440448
Show file tree
Hide file tree
Showing 15 changed files with 529 additions and 194 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- [Table Input Widget is now matched for Table.input method instead of
Table.new. Values must be string literals, and their content is parsed to the
suitable type][11612].
- [New design for vector-editing widget][11620]

[11151]: https://github.com/enso-org/enso/pull/11151
[11271]: https://github.com/enso-org/enso/pull/11271
Expand All @@ -55,6 +56,7 @@
[11564]: https://github.com/enso-org/enso/pull/11564
[11597]: https://github.com/enso-org/enso/pull/11597
[11612]: https://github.com/enso-org/enso/pull/11612
[11620]: https://github.com/enso-org/enso/pull/11620

#### Enso Standard Library

Expand Down
20 changes: 19 additions & 1 deletion app/gui/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,30 @@ const sharedConfig: Partial<ReactStorybookConfig> = {
env: { FRAMEWORK: framework },

previewHead: (head) => {
return `
return /*html*/ `
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=M+PLUS+1:wght@300;400;500;600;700&display=swap"
rel="preload"
as="style"
crossorigin
/>
<script>
window.global = window;
// Pass environment variables to the storybook
window.ENV = {
// The framework used to render the story
// Used by the preview to determine which framework to use
FRAMEWORK: '${framework}',
}
// Allow React DevTools to work in Storybook
if (window.parent !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__
}
</script>
${head}
`
Expand Down
7 changes: 7 additions & 0 deletions app/gui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useLayoutEffect, useState } from 'react'
import invariant from 'tiny-invariant'
import UIProviders from '../src/dashboard/components/UIProviders'

import { QueryClientProvider } from '@tanstack/react-query'
import { createQueryClient } from 'enso-common/src/queryClient'
import { MotionGlobalConfig } from 'framer-motion'
import z from 'zod'
import '../src/dashboard/tailwind.css'
Expand Down Expand Up @@ -68,6 +70,11 @@ const reactPreview: ReactPreview = {
<div id="enso-portal-root" className="enso-portal-root" />
</>
),

(Story, context) => {
const [queryClient] = useState(() => createQueryClient())
return <QueryClientProvider client={queryClient}>{Story(context)}</QueryClientProvider>
},
],
}

Expand Down
10 changes: 10 additions & 0 deletions app/gui/integration-test/project-view/locate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ export function bottomDock(page: Page) {
return page.getByTestId('bottomDock')
}

/** Button to add an item to a vector */
export function addItemButton(page: Locator | Page) {
return page.getByRole('button', { name: 'new item' })
}

/** Button to delete a specific item from a vector */
export function deleteItemButton(page: Locator | Page) {
return page.getByRole('button', { name: 'Remove item' })
}

export const navBreadcrumb = componentLocator('.NavBreadcrumb')
export const componentBrowserInput = componentLocator('.ComponentEditor')

Expand Down
62 changes: 54 additions & 8 deletions app/gui/integration-test/project-view/widgets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ test('Multi-selection widget', async ({ page }) => {
await dropDown.expectVisibleWithOptions(['Column A', 'Column B'])
await expect(dropDown.rootWidget).toHaveClass(/multiSelect/)
const vector = node.locator('.WidgetVector')
const vectorItems = vector.locator('.item .WidgetPort input')
const vectorItems = vector.getByTestId('list-item-content').locator('.WidgetPort input')
await expect(vector).toBeVisible()
await expect(dropDown.selectedItems).toHaveCount(0)
await expect(vectorItems).toHaveCount(0)
Expand All @@ -121,7 +121,7 @@ test('Multi-selection widget', async ({ page }) => {
// Add-item button opens dropdown, after closing with escape.
await page.keyboard.press('Escape')
await dropDown.expectNotVisible()
await vector.locator('.add-item').click()
await locate.addItemButton(vector).click()
await expect(dropDown.items).toHaveCount(2)
await expect(dropDown.selectedItems).toHaveCount(1)

Expand Down Expand Up @@ -180,7 +180,7 @@ test('Multi-selection widget: Item edits', async ({ page }) => {
.graphNodeByBinding(page, 'selected')
.locator('.WidgetTopLevelArgument')
.filter({ has: page.getByText('columns') })
const vectorItems = columnsArg.locator('.WidgetVector .item .WidgetPort input')
const vectorItems = columnsArg.getByTestId('list-item-content').locator('.WidgetPort input')
const dropDown = new DropDownLocator(columnsArg)
await dropDown.clickWidget()
await dropDown.clickOption('Column A')
Expand All @@ -195,6 +195,46 @@ test('Multi-selection widget: Item edits', async ({ page }) => {
await expect(dropDown.selectedItem('Column B')).toExist()
})

test('Editing list', async ({ page }) => {
await actions.goToGraph(page)
const node = locate.graphNodeByBinding(page, 'autoscoped')
const vector = node.locator('.WidgetVector')
const vectorItems = vector.locator('.item')
const vectorElements = vector.getByTestId('list-item-content')
await expect(vectorElements).toHaveText(['..Group_By'])
await node.click()

// Test add
await locate.addItemButton(node).click()
await locate.addItemButton(node).click()
await expect(vectorElements).toHaveText(['..Group_By', '_', '_'])

// Test drag: remove item
await vectorItems.nth(1).locator('[draggable]').dragTo(locate.graphEditor(page))
await expect(vectorElements).toHaveText(['..Group_By', '_'])

// Test drag: reorder items
await vectorItems.nth(1).locator('[draggable]').hover()
await page.mouse.down()
await vectorItems
.nth(1)
.locator('[draggable]')
.hover({ position: { x: 10, y: 10 } })
await expect(vectorElements).toHaveText(['..Group_By'])
await vectorElements.first().hover({ position: { x: 10, y: 10 }, force: true })
await page.mouse.up()
await expect(vectorElements).toHaveText(['_', '..Group_By'])

// Test delete
await locate.deleteItemButton(vectorItems.first()).click()
await expect(vectorElements).toHaveText(['..Group_By'])

// Test delete: last item
await locate.deleteItemButton(vectorItems).click()
await expect(vectorItems).not.toExist()
await expect(vector).toExist()
})

async function dataReadNodeWithMethodCallInfo(page: Page): Promise<Locator> {
await mockMethodCallInfo(page, 'data', {
methodPointer: {
Expand Down Expand Up @@ -374,7 +414,7 @@ test('Manage aggregates in `aggregate` node', async ({ page }) => {
// Add first aggregate
const columnsArg = topLevelArgs.filter({ has: page.getByText('columns') })

await columnsArg.locator('.add-item').click()
await locate.addItemButton(columnsArg).click()
await expect(columnsArg.locator('.WidgetToken')).toContainText([
'Aggregate_Column',
'.',
Expand Down Expand Up @@ -423,7 +463,10 @@ test('Manage aggregates in `aggregate` node', async ({ page }) => {
)

// Set column
const firstItem = columnsArg.locator('.item > .WidgetPort > .WidgetSelection').nth(0)
const firstItem = columnsArg
.getByTestId('list-item-content')
.locator('.WidgetPort > .WidgetSelection')
.nth(0)
const firstItemDropdown = new DropDownLocator(firstItem)
await firstItemDropdown.clickWidget()
await firstItemDropdown.expectVisibleWithOptions(['column 1', 'column 2'])
Expand All @@ -436,7 +479,7 @@ test('Manage aggregates in `aggregate` node', async ({ page }) => {
await expect(columnsArg.locator('.WidgetText > input').first()).toHaveValue('column 1')

// Add another aggregate
await columnsArg.locator('.add-item').click()
await locate.addItemButton(columnsArg).click()
await expect(columnsArg.locator('.WidgetToken')).toContainText([
'Aggregate_Column',
'.',
Expand All @@ -462,7 +505,10 @@ test('Manage aggregates in `aggregate` node', async ({ page }) => {
)

// Set new aggregate's column
const secondItem = columnsArg.locator('.item > .WidgetPort > .WidgetSelection').nth(1)
const secondItem = columnsArg
.getByTestId('list-item-content')
.nth(1)
.locator('.WidgetPort > .WidgetSelection')
const secondItemDropdown = new DropDownLocator(secondItem)
await secondItemDropdown.clickWidget()
await secondItemDropdown.expectVisibleWithOptions(['column 1', 'column 2'])
Expand Down Expand Up @@ -533,7 +579,7 @@ test('Autoscoped constructors', async ({ page }) => {
await node.click()
await expect(topLevelArgs).toHaveCount(3)

const groupBy = node.locator('.item').nth(0)
const groupBy = node.getByTestId('list-item-content').first()
await expect(groupBy).toBeVisible()
await expect(groupBy.locator('.WidgetArgumentName')).toContainText(['column', 'new_name'])
})
Expand Down
4 changes: 2 additions & 2 deletions app/gui/src/dashboard/components/AnimatedBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ AnimatedBackground.Item = memo(function AnimatedBackgroundItem(props: AnimatedBa
const isActive = isSelected ?? activeValue === value

return (
<div className={twJoin('relative', className)}>
<div className={twJoin('relative *:isolate', className)}>
<AnimatedBackgroundItemUnderlay
isActive={isActive}
underlayElement={underlayElement}
layoutId={layoutId}
transition={transition}
/>

<div className="isolate contents">{children}</div>
<div className="isolate contents *:isolate">{children}</div>
</div>
)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from '@storybook/react'
import { userEvent, within } from '@storybook/test'
import { z } from 'zod'
import { Form } from '../../Form'
import type { MultiSelectorProps } from './MultiSelector.tsx'
import { MultiSelector } from './MultiSelector.tsx'

type Props = MultiSelectorProps<typeof schema, 'value'>
type Story = StoryObj<Props>

const schema = z.object({ value: z.array(z.enum(['one', 'two', 'three'])) })

export default {
title: 'Components/AriaComponents/Inputs/MultiSelector',
component: MultiSelector,
render: (args) => <MultiSelector {...args} />,
tags: ['autodocs'],
decorators: [(Story, context) => <Form schema={schema}>{Story(context)}</Form>],
args: { name: 'value', items: ['one', 'two', 'three'] },
} as Meta<Props>

export const Default: Story = {}

export const TwoColumns: Story = { args: { columns: 2 } }

export const SelectedItems: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
await userEvent.click(canvas.getByText('one'))

await userEvent.click(canvas.getByText('two'))

await userEvent.click(canvas.getByText('three'))

await userEvent.click(canvas.getByText('one'))
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { Meta, StoryObj } from '@storybook/react'
import { z } from 'zod'

import { Form } from '#/components/AriaComponents'
import { userEvent, within } from '@storybook/test'
import type { SelectorProps } from './Selector'
import { Selector } from './Selector'

// Schema for our form
const schema = z.object({
plan: z.enum(['basic', 'pro', 'enterprise']),
})

type Props = SelectorProps<typeof schema, 'plan'>

export default {
title: 'Components/AriaComponents/Inputs/Selector',
component: Selector,
parameters: {
layout: 'centered',
},
render: (args) => <Selector {...args} />,
args: {
name: 'plan',
items: ['basic', 'pro', 'enterprise'],
},
decorators: [
(Story, context) => (
<Form schema={schema} className="w-96" defaultValues={{ plan: 'basic' }}>
{Story(context)}
</Form>
),
],
} as Meta<Props>

type Story = StoryObj<Props>

// Basic usage
export const Default: Story = {}

// Different rounded variants
export const VisualVariants: Story = {
render: (args) => {
return (
<div className="w-full space-y-12">
<div className="w-full space-y-2">
{(['outline'] as const).map((variant) => (
<Selector {...args} key={variant} label={variant} variant={variant} />
))}
</div>

<div className="w-full space-y-2">
{(['medium', 'small'] as const).map((size) => (
<Selector {...args} key={size} label={size} size={size} />
))}
</div>

<div className="w-full space-y-2">
{(
['medium', 'xxxlarge', 'none', 'small', 'large', 'xlarge', 'xxlarge', 'full'] as const
).map((rounded) => (
<Selector {...args} key={rounded} label={rounded} rounded={rounded} />
))}
</div>

<div className="w-full space-y-2">
<Selector {...args} label="Invalid" isInvalid />
<Selector {...args} label="Required" isRequired />
<Selector {...args} label="Disabled" isDisabled />
<Selector {...args} label="Readonly" isReadOnly />
<Selector {...args} label="Invalid & Disabled" isInvalid isDisabled />
</div>
</div>
)
},
}

export const Interactions: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)

await userEvent.click(canvas.getByText('enterprise'))
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const SELECTOR_STYLES = tv({
readOnly: { true: 'cursor-default' },
size: {
medium: { base: '' },
small: { base: '' },
},
rounded: {
none: 'rounded-none',
Expand Down Expand Up @@ -92,6 +93,7 @@ export const Selector = forwardRef(function Selector<
label,
size,
rounded,
variant,
isRequired = false,
isInvalid = false,
fieldVariants,
Expand All @@ -108,6 +110,7 @@ export const Selector = forwardRef(function Selector<
rounded,
readOnly: inputProps.readOnly,
disabled: isDisabled || formInstance.formState.isSubmitting,
variant,
})

return (
Expand Down Expand Up @@ -157,7 +160,14 @@ export const Selector = forwardRef(function Selector<
>
<AnimatedBackground value={String(items.indexOf(value))}>
{items.map((item, i) => (
<SelectorOption key={i} value={String(i)} label={children(item)} />
<SelectorOption
key={i}
value={String(i)}
label={children(item)}
rounded={rounded}
size={size}
variant={variant}
/>
))}
</AnimatedBackground>
</RadioGroup>
Expand Down
Loading

0 comments on commit 2440448

Please sign in to comment.