diff --git a/packages/jupyter-chat/src/components/code-blocks/code-toolbar.tsx b/packages/jupyter-chat/src/components/code-blocks/code-toolbar.tsx index e2325601..e740a3b0 100644 --- a/packages/jupyter-chat/src/components/code-blocks/code-toolbar.tsx +++ b/packages/jupyter-chat/src/components/code-blocks/code-toolbar.tsx @@ -4,10 +4,11 @@ */ import { addAboveIcon, addBelowIcon } from '@jupyterlab/ui-components'; -import { Box, IconButton, Tooltip } from '@mui/material'; +import { Box } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { CopyButton } from './copy-button'; +import { TooltippedIconButton } from '../mui-extras'; import { IActiveCellManager } from '../../active-cell-manager'; import { replaceCellIcon } from '../../icons'; import { IChatModel } from '../../model'; @@ -114,24 +115,15 @@ function InsertAboveButton(props: ToolbarButtonProps) { : 'Insert above active cell (no active cell)'; return ( - - - props.activeCellManager?.insertAbove(props.content)} - disabled={!props.activeCellAvailable} - aria-label={tooltip} - sx={{ - lineHeight: 0, - '&.Mui-disabled': { - opacity: 0.5 - } - }} - > - - - - + props.activeCellManager?.insertAbove(props.content)} + disabled={!props.activeCellAvailable} + inputToolbar={false} + > + + ); } @@ -141,24 +133,15 @@ function InsertBelowButton(props: ToolbarButtonProps) { : 'Insert below active cell (no active cell)'; return ( - - - props.activeCellManager?.insertBelow(props.content)} - aria-label={tooltip} - sx={{ - lineHeight: 0, - '&.Mui-disabled': { - opacity: 0.5 - } - }} - > - - - - + props.activeCellManager?.insertBelow(props.content)} + inputToolbar={false} + > + + ); } @@ -187,23 +170,14 @@ function ReplaceButton(props: ToolbarButtonProps) { }; return ( - - - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/code-blocks/copy-button.tsx b/packages/jupyter-chat/src/components/code-blocks/copy-button.tsx index 6fb67d1b..08cefe8a 100644 --- a/packages/jupyter-chat/src/components/code-blocks/copy-button.tsx +++ b/packages/jupyter-chat/src/components/code-blocks/copy-button.tsx @@ -3,10 +3,10 @@ * Distributed under the terms of the Modified BSD License. */ +import { copyIcon } from '@jupyterlab/ui-components'; import React, { useState, useCallback, useRef } from 'react'; -import { copyIcon } from '@jupyterlab/ui-components'; -import { IconButton, Tooltip } from '@mui/material'; +import { TooltippedIconButton } from '../mui-extras'; enum CopyStatus { None, @@ -61,23 +61,16 @@ export function CopyButton(props: CopyButtonProps): JSX.Element { const tooltip = COPYBTN_TEXT_BY_STATUS[copyStatus]; return ( - - - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/input/buttons/attach-button.tsx b/packages/jupyter-chat/src/components/input/buttons/attach-button.tsx index bbbdbe5a..0aaa6386 100644 --- a/packages/jupyter-chat/src/components/input/buttons/attach-button.tsx +++ b/packages/jupyter-chat/src/components/input/buttons/attach-button.tsx @@ -8,7 +8,7 @@ import AttachFileIcon from '@mui/icons-material/AttachFile'; import React from 'react'; import { InputToolbarRegistry } from '../toolbar-registry'; -import { TooltippedButton } from '../../mui-extras/tooltipped-button'; +import { TooltippedIconButton } from '../../mui-extras'; const ATTACH_BUTTON_CLASS = 'jp-chat-attach-button'; @@ -47,23 +47,15 @@ export function AttachButton( }; return ( - - - + + ); } diff --git a/packages/jupyter-chat/src/components/input/buttons/cancel-button.tsx b/packages/jupyter-chat/src/components/input/buttons/cancel-button.tsx index d2689e78..0f329783 100644 --- a/packages/jupyter-chat/src/components/input/buttons/cancel-button.tsx +++ b/packages/jupyter-chat/src/components/input/buttons/cancel-button.tsx @@ -4,10 +4,10 @@ */ import CloseIcon from '@mui/icons-material/Close'; -import { IconButton, Tooltip } from '@mui/material'; import React from 'react'; import { InputToolbarRegistry } from '../toolbar-registry'; +import { TooltippedIconButton } from '../../mui-extras'; const CANCEL_BUTTON_CLASS = 'jp-chat-cancel-button'; @@ -20,24 +20,17 @@ export function CancelButton( if (!props.model.cancel) { return <>; } - const tooltip = 'Cancel editing'; + const tooltip = 'Cancel edition'; return ( - - - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/input/buttons/save-edit-button.tsx b/packages/jupyter-chat/src/components/input/buttons/save-edit-button.tsx index 74eb8c04..5a340c69 100644 --- a/packages/jupyter-chat/src/components/input/buttons/save-edit-button.tsx +++ b/packages/jupyter-chat/src/components/input/buttons/save-edit-button.tsx @@ -4,10 +4,10 @@ */ import CheckIcon from '@mui/icons-material/Check'; -import { IconButton, Tooltip } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { InputToolbarRegistry } from '../toolbar-registry'; +import { TooltippedIconButton } from '../../mui-extras'; const SAVE_EDIT_BUTTON_CLASS = 'jp-chat-save-edit-button'; @@ -50,26 +50,17 @@ export function SaveEditButton( } return ( - - - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/input/buttons/send-button.tsx b/packages/jupyter-chat/src/components/input/buttons/send-button.tsx index 96108191..d3ba7b8e 100644 --- a/packages/jupyter-chat/src/components/input/buttons/send-button.tsx +++ b/packages/jupyter-chat/src/components/input/buttons/send-button.tsx @@ -4,10 +4,10 @@ */ import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; -import { Button, Tooltip } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { InputToolbarRegistry } from '../toolbar-registry'; +import { TooltippedIconButton } from '../../mui-extras'; import { IInputModel, InputModel } from '../../../input-model'; const SEND_BUTTON_CLASS = 'jp-chat-send-button'; @@ -69,38 +69,17 @@ export function SendButton( } return ( - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/input/buttons/stop-button.tsx b/packages/jupyter-chat/src/components/input/buttons/stop-button.tsx index 4e0f9e16..e943780b 100644 --- a/packages/jupyter-chat/src/components/input/buttons/stop-button.tsx +++ b/packages/jupyter-chat/src/components/input/buttons/stop-button.tsx @@ -4,10 +4,10 @@ */ import StopIcon from '@mui/icons-material/Stop'; -import { Button, Tooltip } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { InputToolbarRegistry } from '../toolbar-registry'; +import { TooltippedIconButton } from '../../mui-extras'; const STOP_BUTTON_CLASS = 'jp-chat-stop-button'; @@ -51,38 +51,17 @@ export function StopButton( } return ( - - - - - + + + ); } diff --git a/packages/jupyter-chat/src/components/messages/toolbar.tsx b/packages/jupyter-chat/src/components/messages/toolbar.tsx index b7fe0b9d..736cd242 100644 --- a/packages/jupyter-chat/src/components/messages/toolbar.tsx +++ b/packages/jupyter-chat/src/components/messages/toolbar.tsx @@ -5,9 +5,11 @@ // import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; -import { Box, IconButton, Tooltip } from '@mui/material'; +import { Box } from '@mui/material'; import React from 'react'; +import { TooltippedIconButton } from '../mui-extras'; + const TOOLBAR_CLASS = 'jp-chat-toolbar'; /** @@ -18,43 +20,27 @@ export function MessageToolbar(props: MessageToolbar.IProps): JSX.Element { // if (props.edit !== undefined) { // const editButton = ( - // - // - // - // - // - // - // + // + // + // // ); // buttons.push(editButton); // } if (props.delete !== undefined) { const deleteButton = ( - - - - - - - + + + ); buttons.push(deleteButton); } diff --git a/packages/jupyter-chat/src/components/mui-extras/tooltipped-button.tsx b/packages/jupyter-chat/src/components/mui-extras/tooltipped-button.tsx index e2c714a3..dd516a2a 100644 --- a/packages/jupyter-chat/src/components/mui-extras/tooltipped-button.tsx +++ b/packages/jupyter-chat/src/components/mui-extras/tooltipped-button.tsx @@ -3,17 +3,55 @@ * Distributed under the terms of the Modified BSD License. */ -import { Button, ButtonProps, SxProps, TooltipProps } from '@mui/material'; +import { + Button, + ButtonOwnProps, + ButtonProps, + SxProps, + TooltipProps +} from '@mui/material'; import React from 'react'; import { ContrastingTooltip } from './contrasting-tooltip'; -const TOOLTIPPED_WRAP_CLASS = 'jp-chat-tooltipped-wrap'; +export const TOOLTIPPED_WRAP_CLASS = 'jp-chat-tooltipped-wrap'; + +export const DEFAULT_BUTTON_PROPS: Partial = { + size: 'small', + variant: 'contained' +}; + +export const DEFAULT_BUTTON_SX = { + minWidth: '24px', + width: '24px', + height: '24px', + lineHeight: 0, + '&:disabled': { + opacity: 0.5 + } +}; + +export const INPUT_TOOLBAR_BUTTON_SX = { + backgroundColor: 'var(--jp-brand-color1)', + color: 'white', + borderRadius: '4px', + boxShadow: 'none', + '&:hover': { + backgroundColor: 'var(--jp-brand-color0)', + boxShadow: 'none' + }, + '&:disabled': { + backgroundColor: 'var(--jp-border-color2)', + color: 'var(--jp-ui-font-color3)', + opacity: 0.5 + } +}; export type TooltippedButtonProps = { onClick: React.MouseEventHandler; tooltip: string; children: JSX.Element; + inputToolbar?: boolean; disabled?: boolean; placement?: TooltipProps['placement']; /** @@ -76,15 +114,16 @@ export function TooltippedButton(props: TooltippedButtonProps): JSX.Element { */} diff --git a/packages/jupyter-chat/src/components/mui-extras/tooltipped-icon-button.tsx b/packages/jupyter-chat/src/components/mui-extras/tooltipped-icon-button.tsx index c49632ce..a78889d3 100644 --- a/packages/jupyter-chat/src/components/mui-extras/tooltipped-icon-button.tsx +++ b/packages/jupyter-chat/src/components/mui-extras/tooltipped-icon-button.tsx @@ -4,20 +4,34 @@ */ import { classes } from '@jupyterlab/ui-components'; -import { IconButton, IconButtonProps, TooltipProps } from '@mui/material'; +import { + IconButton, + IconButtonProps, + SvgIconOwnProps, + TooltipProps +} from '@mui/material'; import React from 'react'; import { ContrastingTooltip } from './contrasting-tooltip'; - -const TOOLTIPPED_WRAP_CLASS = 'jp-chat-tooltipped-wrap'; +import { + DEFAULT_BUTTON_PROPS, + DEFAULT_BUTTON_SX, + INPUT_TOOLBAR_BUTTON_SX, + TOOLTIPPED_WRAP_CLASS +} from './tooltipped-button'; export type TooltippedIconButtonProps = { onClick: () => unknown; tooltip: string; children: JSX.Element; className?: string; + inputToolbar?: boolean; disabled?: boolean; placement?: TooltipProps['placement']; + /** + * The font size of the icon. By default it will be set to 'small'. + */ + fontSize?: SvgIconOwnProps['fontSize']; /** * The offset of the tooltip popup. * @@ -47,6 +61,8 @@ export type TooltippedIconButtonProps = { export function TooltippedIconButton( props: TooltippedIconButtonProps ): JSX.Element { + // Override the default icon font size from 'medium' to 'small' + props.children.props.fontSize = props.fontSize ?? 'small'; return ( diff --git a/ui-tests/tests/code-toolbar.spec.ts b/ui-tests/tests/code-toolbar.spec.ts index e947060b..4817cd69 100644 --- a/ui-tests/tests/code-toolbar.spec.ts +++ b/ui-tests/tests/code-toolbar.spec.ts @@ -58,7 +58,7 @@ test.describe('#codeToolbar', () => { test('buttons should be disabled without notebook', async ({ page }) => { const chatPanel = await openChat(page, FILENAME); const message = chatPanel.locator('.jp-chat-message'); - const toolbarButtons = message.locator('.jp-chat-code-toolbar-item'); + const toolbarButtons = message.locator('.jp-chat-code-toolbar-item button'); await sendMessage(page, FILENAME, MESSAGE); await expect(toolbarButtons).toHaveCount(4); @@ -73,7 +73,7 @@ test.describe('#codeToolbar', () => { }) => { const chatPanel = await openChat(page, FILENAME); const message = chatPanel.locator('.jp-chat-message'); - const toolbarButtons = message.locator('.jp-chat-code-toolbar-item'); + const toolbarButtons = message.locator('.jp-chat-code-toolbar-item button'); await page.notebook.createNew(); @@ -88,7 +88,7 @@ test.describe('#codeToolbar', () => { }) => { const chatPanel = await openChat(page, FILENAME); const message = chatPanel.locator('.jp-chat-message'); - const toolbarButtons = message.locator('.jp-chat-code-toolbar-item'); + const toolbarButtons = message.locator('.jp-chat-code-toolbar-item button'); const notebook = await page.notebook.createNew(); @@ -102,7 +102,7 @@ test.describe('#codeToolbar', () => { test('insert code above', async ({ page }) => { const chatPanel = await openChat(page, FILENAME); const message = chatPanel.locator('.jp-chat-message'); - const toolbarButtons = message.locator('.jp-chat-code-toolbar-item'); + const toolbarButtons = message.locator('.jp-chat-code-toolbar-item button'); const notebook = await page.notebook.createNew(); @@ -123,7 +123,7 @@ test.describe('#codeToolbar', () => { test('insert code below', async ({ page }) => { const chatPanel = await openChat(page, FILENAME); const message = chatPanel.locator('.jp-chat-message'); - const toolbarButtons = message.locator('.jp-chat-code-toolbar-item'); + const toolbarButtons = message.locator('.jp-chat-code-toolbar-item button'); const notebook = await page.notebook.createNew();