Sfoglia il codice sorgente

Merge pull request #14712 from overleaf/mj-table-adjustbox

[visual] Disable caption dropdown when tabular is wrapped in command

GitOrigin-RevId: 08131d27e8bb4cb99eee3060435084f671bc3dd6
Mathias Jakobsen 2 anni fa
parent
commit
0d7730f37a

+ 1 - 0
services/web/frontend/extracted-translations.json

@@ -1086,6 +1086,7 @@
   "to_add_more_collaborators": "",
   "to_change_access_permissions": "",
   "to_confirm_transfer_enter_email_address": "",
+  "to_insert_or_move_a_caption_make_sure_tabular_is_directly_within_table": "",
   "to_modify_your_subscription_go_to": "",
   "toggle_compile_options_menu": "",
   "token": "",

+ 11 - 1
services/web/frontend/js/features/source-editor/components/table-generator/contexts/table-context.tsx

@@ -29,6 +29,7 @@ const TableContext = createContext<
       tableEnvironment?: TableEnvironmentData
       rows: number
       columns: number
+      directTableChild?: boolean
     }
   | undefined
 >(undefined)
@@ -38,7 +39,15 @@ export const TableProvider: FC<{
   tableNode: SyntaxNode | null
   tabularNode: SyntaxNode
   view: EditorView
-}> = ({ tableData, children, tableNode, tabularNode, view }) => {
+  directTableChild?: boolean
+}> = ({
+  tableData,
+  children,
+  tableNode,
+  tabularNode,
+  view,
+  directTableChild,
+}) => {
   try {
     const positions: Positions = {
       cells: tableData.cellPositions,
@@ -59,6 +68,7 @@ export const TableProvider: FC<{
           tableEnvironment,
           rows: tableData.table.rows.length,
           columns: tableData.table.columns.length,
+          directTableChild,
         }}
       >
         {children}

+ 3 - 1
services/web/frontend/js/features/source-editor/components/table-generator/tabular.tsx

@@ -227,7 +227,8 @@ export const Tabular: FC<{
   view: EditorView
   tableNode: SyntaxNode | null
   parsedTableData: ParsedTableData
-}> = ({ tabularNode, view, tableNode, parsedTableData }) => {
+  directTableChild?: boolean
+}> = ({ tabularNode, view, tableNode, parsedTableData, directTableChild }) => {
   return (
     <ErrorBoundary
       fallbackRender={() => (
@@ -241,6 +242,7 @@ export const Tabular: FC<{
               tabularNode={tabularNode}
               tableData={parsedTableData}
               tableNode={tableNode}
+              directTableChild={directTableChild}
               view={view}
             >
               <SelectionContextProvider>

+ 10 - 10
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/toolbar-dropdown.tsx

@@ -81,10 +81,17 @@ export const ToolbarDropdown: FC<{
     </Overlay>
   )
 
-  if (!tooltip) {
+  if (tooltip || (disabled && disabledTooltip)) {
     return (
       <>
-        {button}
+        <Tooltip
+          hidden={open}
+          id={id}
+          description={disabled && disabledTooltip ? disabledTooltip : tooltip}
+          overlayProps={{ placement: 'bottom' }}
+        >
+          {button}
+        </Tooltip>
         {overlay}
       </>
     )
@@ -92,14 +99,7 @@ export const ToolbarDropdown: FC<{
 
   return (
     <>
-      <Tooltip
-        hidden={open}
-        id={id}
-        description={disabled && disabledTooltip ? disabledTooltip : tooltip}
-        overlayProps={{ placement: 'bottom' }}
-      >
-        {button}
-      </Tooltip>
+      {button}
       {overlay}
     </>
   )

+ 12 - 3
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/toolbar.tsx

@@ -26,8 +26,14 @@ import { useTranslation } from 'react-i18next'
 export const Toolbar = memo(function Toolbar() {
   const { selection, setSelection } = useSelectionContext()
   const view = useCodeMirrorViewContext()
-  const { positions, rowSeparators, cellSeparators, tableEnvironment, table } =
-    useTableContext()
+  const {
+    positions,
+    rowSeparators,
+    cellSeparators,
+    tableEnvironment,
+    table,
+    directTableChild,
+  } = useTableContext()
   const { showHelp } = useTabularContext()
   const { t } = useTranslation()
 
@@ -64,7 +70,10 @@ export const Toolbar = memo(function Toolbar() {
         <ToolbarDropdown
           id="table-generator-caption-dropdown"
           label={captionLabel}
-          disabled={!tableEnvironment}
+          disabled={!tableEnvironment || !directTableChild}
+          disabledTooltip={t(
+            'to_insert_or_move_a_caption_make_sure_tabular_is_directly_within_table'
+          )}
         >
           <ToolbarDropdownItem
             id="table-generator-caption-none"

+ 11 - 4
services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts

@@ -45,6 +45,7 @@ import { EnvironmentLineWidget } from './visual-widgets/environment-line'
 import {
   ListEnvironmentName,
   ancestorOfNodeWithType,
+  isDirectChildOfEnvironment,
 } from '../../utils/tree-operations/ancestors'
 import { InlineGraphicsWidget } from './visual-widgets/inline-graphics'
 import getMeta from '../../../../utils/meta'
@@ -320,17 +321,23 @@ export const atomicDecorations = (options: Options) => {
             nodeRef.type.is('TabularEnvironment')
           ) {
             if (shouldDecorate(state, nodeRef)) {
+              const tabularNode = nodeRef.node
               const tableNode = ancestorOfNodeWithType(
-                nodeRef.node,
+                tabularNode,
                 'TableEnvironment'
               )
+              const directChild = isDirectChildOfEnvironment(
+                tabularNode.parent,
+                tableNode
+              )
               const tabularWidget = new TabularWidget(
-                nodeRef.node,
+                tabularNode,
                 state.doc.sliceString(
-                  (tableNode ?? nodeRef).from,
-                  (tableNode ?? nodeRef).to
+                  (tableNode ?? tabularNode).from,
+                  (tableNode ?? tabularNode).to
                 ),
                 tableNode,
+                directChild,
                 state
               )
 

+ 3 - 0
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/tabular.tsx

@@ -16,6 +16,7 @@ export class TabularWidget extends WidgetType {
     private tabularNode: SyntaxNode,
     private content: string,
     private tableNode: SyntaxNode | null,
+    private isDirectChildOfTableEnvironment: boolean,
     state: EditorState
   ) {
     super()
@@ -64,6 +65,7 @@ export class TabularWidget extends WidgetType {
           tabularNode={this.tabularNode}
           parsedTableData={this.parseResult}
           tableNode={this.tableNode}
+          directTableChild={this.isDirectChildOfTableEnvironment}
         />,
         this.element
       )
@@ -91,6 +93,7 @@ export class TabularWidget extends WidgetType {
         tabularNode={this.tabularNode}
         parsedTableData={this.parseResult}
         tableNode={this.tableNode}
+        directTableChild={this.isDirectChildOfTableEnvironment}
       />,
       this.element
     )

+ 8 - 0
services/web/frontend/js/features/source-editor/utils/tree-operations/ancestors.ts

@@ -278,3 +278,11 @@ export const minimumListDepthForSelection = (state: EditorState) => {
   }
   return Math.min(...depths)
 }
+
+export const isDirectChildOfEnvironment = (
+  child?: SyntaxNode | null,
+  ancestor?: SyntaxNode | null
+) => {
+  const possiblyAncestor = child?.parent?.parent?.parent // Text → Content → Environment
+  return ancestor === possiblyAncestor
+}

+ 1 - 0
services/web/locales/en.json

@@ -1722,6 +1722,7 @@
   "to_add_more_collaborators": "To add more collaborators or turn on link sharing, please ask the project owner",
   "to_change_access_permissions": "To change access permissions, please ask the project owner",
   "to_confirm_transfer_enter_email_address": "To accept the invitation, enter the email address linked to your account.",
+  "to_insert_or_move_a_caption_make_sure_tabular_is_directly_within_table": "To insert or move a caption, make sure \\begin{tabular} is directly within a table environment",
   "to_many_login_requests_2_mins": "This account has had too many login requests. Please wait 2 minutes before trying to log in again",
   "to_modify_your_subscription_go_to": "To modify your subscription go to",
   "toggle_compile_options_menu": "Toggle compile options menu",

+ 32 - 1
services/web/test/frontend/features/source-editor/components/codemirror-editor-table-generator.spec.tsx

@@ -413,8 +413,21 @@ cell 3 & cell 4 \\\\
         })
       })
 
-      // Removes caption when clicking "No caption"
       cy.get('@toolbar').findByText('Caption below').click()
+      cy.get('.table-generator-toolbar-dropdown-menu')
+        .findByText('Caption above')
+        .click()
+      // Check that caption is above table
+      cy.get('.ol-cm-command-caption').then(([caption]) => {
+        const { top: captionYPosition } = caption.getBoundingClientRect()
+        cy.get('.table-generator').then(([table]) => {
+          const { top: tableYPosition } = table.getBoundingClientRect()
+          cy.wrap(captionYPosition).should('be.lessThan', tableYPosition)
+        })
+      })
+
+      // Removes caption when clicking "No caption"
+      cy.get('@toolbar').findByText('Caption above').click()
       cy.get('.table-generator-toolbar-dropdown-menu')
         .findByText('No caption')
         .click()
@@ -445,5 +458,23 @@ cell 3 & cell 4 \\\\
       ])
       checkBordersWithNoMultiColumn([true, true, true], [true, true, true])
     })
+
+    it('Disables caption dropdown when not directly inside table environment', function () {
+      mountEditor(`
+\\begin{table}
+  \\caption{Table caption}
+  \\label{tab:table}
+  \\begin{adjustbox}{max width=\\textwidth}
+    \\begin{tabular}{c}
+      cell 1
+    \\end{tabular}
+  \\end{adjustbox}
+\\end{table}`)
+      cy.get('.table-generator').findByText('cell 1').click()
+      cy.get('.table-generator-floating-toolbar').as('toolbar').should('exist')
+      cy.get('@toolbar')
+        .contains('button', 'Caption above')
+        .should('be.disabled')
+    })
   })
 })