diff --git a/.github/workflows/render-preview-deploy.yml b/.github/workflows/render-preview-deploy.yml index ead9ba50bf..203ee88150 100644 --- a/.github/workflows/render-preview-deploy.yml +++ b/.github/workflows/render-preview-deploy.yml @@ -13,12 +13,42 @@ permissions: jobs: # Community Edition - create-ce-review-app: if: ${{ github.event.action == 'labeled' && (github.event.label.name == 'create-ce-review-app' || github.event.label.name == 'review-app') }} runs-on: ubuntu-latest steps: + - name: Sync repo + uses: actions/checkout@v3 + + - name: Check if Forked Repository + id: check_repo + run: | + if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then + echo "is_fork=true" >> $GITHUB_ENV + echo "FORKED_OWNER=${{ github.event.pull_request.head.repo.owner.login }}" >> $GITHUB_ENV + else + echo "is_fork=false" >> $GITHUB_ENV + fi + + - name: Set Repository URL + run: | + if [[ "$is_fork" == "true" ]]; then + echo "REPO_URL=https://github.com/${FORKED_OWNER}/ToolJet" >> $GITHUB_ENV + else + echo "REPO_URL=https://github.com/ToolJet/ToolJet" >> $GITHUB_ENV + fi + + - name: Fetch and Checkout Forked Branch + if: env.is_fork == 'true' + run: | + git fetch origin pull/${{ github.event.number }}/head:${{ env.BRANCH_NAME }} + git checkout ${{ env.BRANCH_NAME }} + + - name: Checkout Default Branch + if: env.is_fork == 'false' + uses: actions/checkout@v3 + - name: Creating deployment for CE id: create-ce-deployment run: | @@ -34,7 +64,7 @@ jobs: "name": "ToolJet CE PR #${{ env.PR_NUMBER }}", "notifyOnFail": "default", "ownerId": "tea-caeo4bj19n072h3dddc0", - "repo": "https://github.com/ToolJet/ToolJet", + "repo": "'"$REPO_URL"'", "slug": "tooljet-ce-pr-${{ env.PR_NUMBER }}", "suspended": "not_suspended", "suspenders": [], diff --git a/.version b/.version index 7c69a55dbb..19811903a7 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -3.7.0 +3.8.0 diff --git a/frontend/.version b/frontend/.version index 7c69a55dbb..19811903a7 100644 --- a/frontend/.version +++ b/frontend/.version @@ -1 +1 @@ -3.7.0 +3.8.0 diff --git a/frontend/src/AppBuilder/AppCanvas/Container.jsx b/frontend/src/AppBuilder/AppCanvas/Container.jsx index e622e1a2cd..c320c3f0cb 100644 --- a/frontend/src/AppBuilder/AppCanvas/Container.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Container.jsx @@ -5,7 +5,12 @@ import WidgetWrapper from './WidgetWrapper'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; import { useDrop } from 'react-dnd'; -import { addChildrenWidgetsToParent, addNewWidgetToTheEditor, computeViewerBackgroundColor } from './appCanvasUtils'; +import { + addChildrenWidgetsToParent, + addNewWidgetToTheEditor, + computeViewerBackgroundColor, + getSubContainerWidthAfterPadding, +} from './appCanvasUtils'; import { CANVAS_WIDTHS, NO_OF_GRIDS, @@ -20,6 +25,7 @@ import NoComponentCanvasContainer from './NoComponentCanvasContainer'; import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants'; import { isPDFSupported } from '@/_helpers/appUtils'; import toast from 'react-hot-toast'; +import useSortedComponents from '../_hooks/useSortedComponents'; //TODO: Revisit the logic of height (dropRef) @@ -103,12 +109,7 @@ export const Container = React.memo( if (canvasWidth !== undefined) { if (componentType === 'Listview' && listViewMode == 'grid') return canvasWidth / columns - 2; if (id === 'canvas') return canvasWidth; - if (componentType === 'Container' || componentType === 'Form') { - return ( - canvasWidth - (2 * CONTAINER_FORM_CANVAS_PADDING + 2 * SUBCONTAINER_CANVAS_BORDER_WIDTH + 2 * BOX_PADDING) - ); - } - return canvasWidth - 2; // Need to update this 2 to correct value for other subcontainers + return getSubContainerWidthAfterPadding(canvasWidth, componentType, id); } return realCanvasRef?.current?.offsetWidth; } @@ -146,6 +147,8 @@ export const Container = React.memo( [setLastCanvasClickPosition] ); + const sortedComponents = useSortedComponents(components, currentLayout, id); + return (
- {components.map((id) => ( + {sortedComponents.map((id) => ( state.setReorderContainerChildren, shallow); useEffect(() => { const selectedSet = new Set(selectedComponents); @@ -536,6 +537,7 @@ export default function Grid({ gridWidth, currentLayout }) { }) ); } + setReorderContainerChildren(draggedOverElemId ?? 'canvas'); } catch (error) { console.error('Error dragging group', error); } @@ -696,6 +698,7 @@ export default function Grid({ gridWidth, currentLayout }) { resizeData.gw = _gridWidth; } handleResizeStop([resizeData]); + setReorderContainerChildren(currentWidget?.parent ?? 'canvas'); } catch (error) { console.error('ResizeEnd error ->', error); } @@ -775,6 +778,11 @@ export default function Grid({ gridWidth, currentLayout }) { ev.target.style.transform = `translate(${posX}px, ${posY}px)`; }); } + + const groupParentId = + boxList.find(({ id }) => id === groupResizeDataRef.current[0].target.id)?.parent ?? 'canvas'; + setReorderContainerChildren(groupParentId); + groupResizeDataRef.current = []; reloadGrid(); } catch (error) { @@ -841,6 +849,8 @@ export default function Grid({ gridWidth, currentLayout }) { useStore.getState().setDraggingComponentId(null); isDraggingRef.current = false; } + + const oldParentId = boxList.find((b) => b.id === e.target.id)?.parent ?? 'canvas'; prevDragParentId.current = null; newDragParentId.current = null; setDragParentId(null); @@ -880,6 +890,12 @@ export default function Grid({ gridWidth, currentLayout }) { // Apply transform for smooth transition e.target.style.transform = `translate(${left}px, ${top}px)`; + // Force reordering of conatiner if the parent has not changed + const newParentId = target.slotId === 'real-canvas' ? 'canvas' : target.slotId; + if (oldParentId === newParentId) { + setReorderContainerChildren(newParentId); + } + // Select the dragged component after drop setTimeout(() => setSelectedComponents([dragged.id])); } catch (error) { diff --git a/frontend/src/AppBuilder/AppCanvas/appCanvasConstants.js b/frontend/src/AppBuilder/AppCanvas/appCanvasConstants.js index e6a789fba0..5725baf7ed 100644 --- a/frontend/src/AppBuilder/AppCanvas/appCanvasConstants.js +++ b/frontend/src/AppBuilder/AppCanvas/appCanvasConstants.js @@ -23,3 +23,7 @@ export const CONTAINER_FORM_CANVAS_PADDING = 7; export const SUBCONTAINER_CANVAS_BORDER_WIDTH = 1; export const BOX_PADDING = 2; + +export const TAB_CANVAS_PADDING = 7.5; + +export const MODAL_CANVAS_PADDING = 5; diff --git a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js index b5325c0801..8d5f2dfe62 100644 --- a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js +++ b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js @@ -6,7 +6,16 @@ import { toast } from 'react-hot-toast'; import _, { debounce } from 'lodash'; import { useGridStore } from '@/_stores/gridStore'; import { findHighestLevelofSelection } from './Grid/gridUtils'; -import { CANVAS_WIDTHS, NO_OF_GRIDS, WIDGETS_WITH_DEFAULT_CHILDREN } from './appCanvasConstants'; +import { + CANVAS_WIDTHS, + NO_OF_GRIDS, + WIDGETS_WITH_DEFAULT_CHILDREN, + CONTAINER_FORM_CANVAS_PADDING, + SUBCONTAINER_CANVAS_BORDER_WIDTH, + BOX_PADDING, + TAB_CANVAS_PADDING, + MODAL_CANVAS_PADDING, +} from './appCanvasConstants'; export function snapToGrid(canvasWidth, x, y) { const gridX = canvasWidth / 43; @@ -712,3 +721,25 @@ export const getSubContainerIdWithSlots = (parentId) => { } return cleanParentId; }; + +export const getSubContainerWidthAfterPadding = (canvasWidth, componentType, componentId) => { + let padding = 2; //Need to update this 2 to correct value for other subcontainers + if (componentType === 'Container' || componentType === 'Form') { + padding = 2 * CONTAINER_FORM_CANVAS_PADDING + 2 * SUBCONTAINER_CANVAS_BORDER_WIDTH + 2 * BOX_PADDING; + } + if (componentType === 'Tabs') { + padding = 2 * TAB_CANVAS_PADDING + 2 * SUBCONTAINER_CANVAS_BORDER_WIDTH + 2 * BOX_PADDING; + } + if (componentType === 'ModalV2') { + const isModalHeader = componentId?.includes('header'); + if (isModalHeader) { + const isModalHeaderCloseBtnEnabled = !useStore.getState().getResolvedComponent(componentId)?.properties + ?.hideCloseButton; + console.log('isModalHeaderCloseBtnEnabled', isModalHeaderCloseBtnEnabled); + padding = 2 * (MODAL_CANVAS_PADDING + (isModalHeaderCloseBtnEnabled ? 56 : 0)); + } else { + padding = 2 * MODAL_CANVAS_PADDING; + } + } + return canvasWidth - padding; +}; diff --git a/frontend/src/AppBuilder/QueryManager/Components/ParameterDetails.jsx b/frontend/src/AppBuilder/QueryManager/Components/ParameterDetails.jsx index 6ff93ee9b6..2b9d03dc28 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/ParameterDetails.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/ParameterDetails.jsx @@ -4,6 +4,7 @@ import cx from 'classnames'; import PlusRectangle from '@/_ui/Icon/solidIcons/PlusRectangle'; import Remove from '@/_ui/Icon/bulkIcons/Remove'; import ParameterForm from './ParameterForm'; +import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver'; const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRemove, otherParams }) => { const [showModal, setShowModal] = useState(false); @@ -47,6 +48,17 @@ const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRe } }; + usePopoverObserver( + document.getElementsByClassName('query-details')[0], + isEdit + ? document.getElementById(`query-param-${String(name).toLowerCase()}`) + : document.getElementById('runjs-param-add-btn'), + document.getElementById('parameter-form-popover'), + showModal, + () => setShowModal(true), + closeMenu + ); + return ( setShowMenu(true), + () => setShowMenu(false) + ); + function checkElementPosition() { if (isForeignKeyInEditCell) { return 'bottom-start'; diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx index c3add3f1cc..6c8398724d 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx @@ -30,6 +30,8 @@ import { appService } from '@/_services'; import { deepClone } from '@/_helpers/utilities/utils.helpers'; import useStore from '@/AppBuilder/_stores/store'; import { useEventActions, useEvents } from '@/AppBuilder/_stores/slices/eventsSlice'; +import ToggleGroup from '@/ToolJetUI/SwitchGroup/ToggleGroup'; +import ToggleGroupItem from '@/ToolJetUI/SwitchGroup/ToggleGroupItem'; export const EventManager = ({ sourceId, @@ -503,7 +505,7 @@ export const EventManager = ({ )} {event.actionId === 'open-webpage' && ( -
+
+
+ + handlerChanged(index, 'windowTarget', _value)} + defaultValue={event?.windowTarget || 'newTab'} + style={{ width: '74%' }} + > + New tab + Current tab + +
)} diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js b/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js index ab3eb40c2c..8ae34a5647 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js @@ -123,6 +123,14 @@ export const buttonGroupConfig = { defaultValue: '#007bff', }, }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'left', + }, + }, }, exposedVariables: { selected: [1], @@ -155,6 +163,7 @@ export const buttonGroupConfig = { disabledState: { value: '{{false}}' }, selectedTextColor: { value: '#FFFFFF' }, selectedBackgroundColor: { value: '#4368E3' }, + alignment: { value: 'left' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/image.js b/frontend/src/AppBuilder/WidgetManager/widgets/image.js index c4bd7b6147..b962c270a5 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/image.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/image.js @@ -143,6 +143,15 @@ export const imageConfig = { }, accordian: 'Image', }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'center', + }, + accordian: 'Image', + }, backgroundColor: { type: 'color', displayName: 'Background', @@ -179,11 +188,11 @@ export const imageConfig = { padding: { type: 'switch', displayName: 'Padding', - validation: { schema: { type: 'string' }, defaultValue: 'default' }, options: [ { displayName: 'Default', value: 'default' }, { displayName: 'Custom', value: 'custom' }, ], + validation: { schema: { type: 'string' }, defaultValue: 'default' }, accordian: 'Container', isFxNotRequired: true, }, @@ -244,7 +253,6 @@ export const imageConfig = { loadingState: { value: '{{false}}' }, disabledState: { value: '{{false}}' }, visibility: { value: '{{true}}' }, - visible: { value: '{{true}}' }, }, events: [], styles: { @@ -256,6 +264,7 @@ export const imageConfig = { boxShadow: { value: '0px 0px 0px 0px #00000090' }, padding: { value: 'default' }, customPadding: { value: '{{0}}' }, + alignment: { value: 'center' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/link.js b/frontend/src/AppBuilder/WidgetManager/widgets/link.js index 673abb1e75..b4da6d30ee 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/link.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/link.js @@ -159,6 +159,14 @@ export const linkConfig = { ], accordian: 'container', }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'left', + }, + }, }, exposedVariables: {}, actions: [ diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/pagination.js b/frontend/src/AppBuilder/WidgetManager/widgets/pagination.js index 6fabfa889a..116d0e9849 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/pagination.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/pagination.js @@ -50,6 +50,14 @@ export const paginationConfig = { defaultValue: false, }, }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'left', + }, + }, }, exposedVariables: { totalPages: null, @@ -73,6 +81,7 @@ export const paginationConfig = { styles: { visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + alignment: { value: 'left' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/svgImage.js b/frontend/src/AppBuilder/WidgetManager/widgets/svgImage.js index 315a6e2c28..9780f0cdd6 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/svgImage.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/svgImage.js @@ -32,6 +32,14 @@ export const svgImageConfig = { defaultValue: true, }, }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'left', + }, + }, }, exposedVariables: { value: {}, @@ -50,6 +58,7 @@ export const svgImageConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + alignment: { value: 'left' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/tags.js b/frontend/src/AppBuilder/WidgetManager/widgets/tags.js index 8af289b23a..73cd44b550 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/tags.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/tags.js @@ -38,6 +38,14 @@ export const tagsConfig = { defaultValue: true, }, }, + alignment: { + type: 'alignButtons', + displayName: 'Alignment', + validation: { + schema: { type: 'string' }, + defaultValue: 'left', + }, + }, }, exposedVariables: {}, definition: { @@ -54,6 +62,7 @@ export const tagsConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + alignment: { value: 'left' }, }, }, }; diff --git a/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx index e25027ce33..8ff4c0cbb1 100644 --- a/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx +++ b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx @@ -19,6 +19,7 @@ export const ModalFooter = React.memo(({ id, isDisabled, customStyles, darkMode, overflowX: 'hidden', overflowY: isDisabled ? 'hidden' : 'auto', }} + componentType="ModalV2" /> {isDisabled && (
{isDisabled && ( diff --git a/frontend/src/AppBuilder/Widgets/ModalV2/Components/Modal.jsx b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Modal.jsx index 25796a9951..2615ca3d5c 100644 --- a/frontend/src/AppBuilder/Widgets/ModalV2/Components/Modal.jsx +++ b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Modal.jsx @@ -105,9 +105,10 @@ export const ModalWidget = ({ ...restProps }) => { ) : ( diff --git a/frontend/src/AppBuilder/Widgets/ModalV2/helpers/stylesFactory.js b/frontend/src/AppBuilder/Widgets/ModalV2/helpers/stylesFactory.js index ee536dcfe1..9c6d4eb0c8 100644 --- a/frontend/src/AppBuilder/Widgets/ModalV2/helpers/stylesFactory.js +++ b/frontend/src/AppBuilder/Widgets/ModalV2/helpers/stylesFactory.js @@ -1,4 +1,5 @@ -var tinycolor = require('tinycolor2'); +const tinycolor = require('tinycolor2'); +import { MODAL_CANVAS_PADDING } from '@/AppBuilder/AppCanvas/appCanvasConstants'; export function createModalStyles({ height, @@ -17,25 +18,27 @@ export function createModalStyles({ boxShadow, }) { const backwardCompatibilityCheck = height == '34' || modalHeight != undefined ? true : false; - return { modalBody: { height: backwardCompatibilityCheck ? computedCanvasHeight : height, backgroundColor: ['#fff', '#ffffffff'].includes(bodyBackgroundColor) && darkMode ? '#1F2837' : bodyBackgroundColor, overflowY: isDisabledModal ? 'hidden' : 'auto', + padding: `${MODAL_CANVAS_PADDING}px`, }, modalHeader: { backgroundColor: ['#fff', '#ffffffff'].includes(headerBackgroundColor) && darkMode ? '#1F2837' : headerBackgroundColor, height: headerHeightPx, overflowY: isDisabledModal ? 'hidden' : 'auto', + padding: `${4.5}px ${MODAL_CANVAS_PADDING}px`, }, modalFooter: { backgroundColor: ['#fff', '#ffffffff'].includes(footerBackgroundColor) && darkMode ? '#1F2837' : footerBackgroundColor, height: footerHeightPx, overflowY: isDisabledModal ? 'hidden' : 'auto', + padding: `${4.5}px ${MODAL_CANVAS_PADDING}px`, }, buttonStyles: { backgroundColor: triggerButtonBackgroundColor, diff --git a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx index 257de7c4ae..b7ebe93721 100644 --- a/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/columns/index.jsx @@ -130,6 +130,8 @@ export default function generateColumnsData({ return 1; } }; + } else if (columnType === 'number') { + sortType = 'basic'; } const width = columnSize || defaultColumn.width; return { diff --git a/frontend/src/AppBuilder/Widgets/Tabs.jsx b/frontend/src/AppBuilder/Widgets/Tabs.jsx index 7f4fb527e4..dc3a55dcd2 100644 --- a/frontend/src/AppBuilder/Widgets/Tabs.jsx +++ b/frontend/src/AppBuilder/Widgets/Tabs.jsx @@ -2,7 +2,7 @@ import React, { useRef, useState, useEffect } from 'react'; import { Container as SubContainer } from '@/AppBuilder/AppCanvas/Container'; import { resolveWidgetFieldValue, isExpectedDataType } from '@/_helpers/utils'; import useStore from '@/AppBuilder/_stores/store'; - +import { TAB_CANVAS_PADDING } from '@/AppBuilder/AppCanvas/appCanvasConstants'; export const Tabs = function Tabs({ id, component, @@ -117,6 +117,7 @@ export const Tabs = function Tabs({ position: 'absolute', top: parsedHideTabs ? '0px' : '41px', width: '100%', + padding: TAB_CANVAS_PADDING, }} >