diff --git a/.github/config.json b/.github/config.json index 162c3545f3..8ffcc723a6 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1 +1 @@ -{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Team Managers Pod":{"conditions":[{"label":"Collaboration","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Settings","type":"hasLabel","value":true},{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"ACL","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true},{"label":"advanced","type":"hasLabel","value":true},{"label":"Team Managers Pod","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Deployment","type":"hasLabel","value":true},{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"New Developers Pod","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"Datasources","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"UQI","type":"hasLabel","value":true},{"label":"Query Editor","type":"hasLabel","value":true},{"label":"API pane","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"BE Coders Pod","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Query Execution","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"JS Editor","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"FE Coders Pod","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Viewers Pod","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"UI Builders Pod","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Canvas Zoom","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"Platform Pod":{"conditions":[{"label":"Platform Pod","type":"hasLabel","value":true},{"label":"Team Managers Pod","type":"hasLabel","value":true},{"label":"New Developers Pod","type":"hasLabel","value":true}],"requires":1},"Actions Pod":{"conditions":[{"label":"FE Coders Pod","type":"hasLabel","value":true},{"label":"BE Coders Pod","type":"hasLabel","value":true}],"requires":1},"UI Building Pod":{"conditions":[{"label":"App Viewers Pod","type":"hasLabel","value":true},{"label":"UI Builders Pod","type":"hasLabel","value":true}],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"advanced":{"color":"5f4115","name":"advanced","description":"Features aimed at advanced users"},"Checkbox Group widget":{"color":"D2ACD2","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"B0F9F4","name":"MySQL","description":""},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"community":{"color":"dded34","name":"community","description":"issues reported by community members"},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"UQI":{"color":"FB8E9C","name":"UQI","description":""},"AutomationGap":{"color":"ed13bb","name":"AutomationGap","description":"Issues that needs automated tests"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"F1C2DF","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Performance":{"color":"d30e53","name":"Performance","description":"Page Load and evaluations"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"c57928","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"JS Editor":{"color":"48b992","name":"JS Editor","description":"Issues related to JS Editor"},"Widget Styling":{"color":"37EA75","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"JS":{"color":"e46785","name":"JS","description":"Issues related to JS on the platform"},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"074ac6","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"47075c","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"API pane":{"color":"e417c7","name":"API pane","description":"API configuration section"},"Import-Export-App":{"color":"a7768a","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"ACL":{"color":"5428a9","name":"ACL","description":"User permissions and access controls"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"UI Building Pod":{"color":"e2ffb2","name":"UI Building Pod","description":""},"Cannot Reproduce":{"color":"35d6c8","name":"Cannot Reproduce","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"12b715","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"949fe0","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"bdf989","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Actions Pod":{"color":"61ed84","name":"Actions Pod","description":"Issues picked up by the action pod"},"Google Sheets":{"color":"51D86A","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"fef2c0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"C4568E","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"ABAEB5","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"f285e1","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Query Editor":{"color":"8887af","name":"Query Editor","description":"The section where a user can write DB queries."},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"Platform Pod":{"color":"500B69","name":"Platform Pod","description":"All issues related to using the appsmith platform"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"color":"9168f4","name":"In App Comms","description":"Issues around communication with appsmith instances"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"Collaboration":{"color":"2ac49c","name":"Collaboration","description":"In-app communication between Appsmith users"},"regression":{"color":"ffe5bc","name":"regression","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"High impact":{"color":"853A9F","name":"High impact","description":""},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"SQL":{"name":"SQL","description":"Issues related to SQL Datasources","color":"947a52"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8b19cc"},"New Datasource":{"color":"9c0cf7","name":"New Datasource","description":"Requests for new datasources"},"Query Execution":{"color":"9c0cf7","name":"Query Execution","description":"Issues related to API / Query execution"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"12b715","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Canvas Zoom":{"name":"Canvas Zoom","description":"Issues related to zooming the canvas","color":"57da0d"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"849aff"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"}}} \ No newline at end of file +{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Team Managers Pod":{"conditions":[{"label":"Collaboration","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Settings","type":"hasLabel","value":true},{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true},{"label":"advanced","type":"hasLabel","value":true},{"label":"Team Managers Pod","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true},{"label":"ACL","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Deployment","type":"hasLabel","value":true},{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"New Developers Pod","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"Datasources","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"UQI","type":"hasLabel","value":true},{"label":"Query Editor","type":"hasLabel","value":true},{"label":"API pane","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"BE Coders Pod","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Query Execution","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"JS Editor","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"FE Coders Pod","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Viewers Pod","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"UI Builders Pod","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Canvas Zoom","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"Platform Pod":{"conditions":[{"label":"Platform Pod","type":"hasLabel","value":true},{"label":"Team Managers Pod","type":"hasLabel","value":true},{"label":"New Developers Pod","type":"hasLabel","value":true}],"requires":1},"Actions Pod":{"conditions":[{"label":"FE Coders Pod","type":"hasLabel","value":true},{"label":"BE Coders Pod","type":"hasLabel","value":true}],"requires":1},"UI Building Pod":{"conditions":[{"label":"App Viewers Pod","type":"hasLabel","value":true},{"label":"UI Builders Pod","type":"hasLabel","value":true}],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"advanced":{"color":"5f4115","name":"advanced","description":"Features aimed at advanced users"},"Checkbox Group widget":{"color":"D2ACD2","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"B0F9F4","name":"MySQL","description":""},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"community":{"color":"dded34","name":"community","description":"issues reported by community members"},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"UQI":{"color":"FB8E9C","name":"UQI","description":""},"AutomationGap":{"color":"ed13bb","name":"AutomationGap","description":"Issues that needs automated tests"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"F1C2DF","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Performance":{"color":"d30e53","name":"Performance","description":"Page Load and evaluations"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"c57928","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"JS Editor":{"color":"48b992","name":"JS Editor","description":"Issues related to JS Editor"},"Widget Styling":{"color":"37EA75","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"JS":{"color":"e46785","name":"JS","description":"Issues related to JS on the platform"},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"074ac6","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"47075c","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"API pane":{"color":"e417c7","name":"API pane","description":"API configuration section"},"Import-Export-App":{"color":"a7768a","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"ACL":{"color":"747224","name":"ACL","description":"User permissions and access controls"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"UI Building Pod":{"color":"e2ffb2","name":"UI Building Pod","description":""},"Cannot Reproduce":{"color":"35d6c8","name":"Cannot Reproduce","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"12b715","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"949fe0","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"bdf989","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Actions Pod":{"color":"61ed84","name":"Actions Pod","description":"Issues picked up by the action pod"},"Google Sheets":{"color":"51D86A","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"fef2c0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"C4568E","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"ABAEB5","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"f285e1","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Query Editor":{"color":"8887af","name":"Query Editor","description":"The section where a user can write DB queries."},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"Platform Pod":{"color":"500B69","name":"Platform Pod","description":"All issues related to using the appsmith platform"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"color":"9168f4","name":"In App Comms","description":"Issues around communication with appsmith instances"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"Collaboration":{"color":"2ac49c","name":"Collaboration","description":"In-app communication between Appsmith users"},"regression":{"color":"ffe5bc","name":"regression","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"High impact":{"color":"853A9F","name":"High impact","description":""},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"SQL":{"name":"SQL","description":"Issues related to SQL Datasources","color":"947a52"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8b19cc"},"New Datasource":{"color":"9c0cf7","name":"New Datasource","description":"Requests for new datasources"},"Query Execution":{"color":"9c0cf7","name":"Query Execution","description":"Issues related to API / Query execution"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"12b715","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Canvas Zoom":{"name":"Canvas Zoom","description":"Issues related to zooming the canvas","color":"57da0d"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"849aff"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"},"Old App Issues":{"name":"Old App Issues","description":"Issues related to apps old apps a few weeks old and app issues in stale browser session","color":"87ab18"}}} \ No newline at end of file diff --git a/.github/workflows/integration-tests-command.yml b/.github/workflows/integration-tests-command.yml index 7d1f43953c..1342365359 100644 --- a/.github/workflows/integration-tests-command.yml +++ b/.github/workflows/integration-tests-command.yml @@ -257,7 +257,7 @@ jobs: strategy: fail-fast: false matrix: - job: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + job: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] # Service containers to run with this job. Required for running tests services: # Label used to access the service container diff --git a/.github/workflows/test-build-docker-image.yml b/.github/workflows/test-build-docker-image.yml index c5e0a729c4..eeabcef78e 100644 --- a/.github/workflows/test-build-docker-image.yml +++ b/.github/workflows/test-build-docker-image.yml @@ -291,7 +291,7 @@ jobs: strategy: fail-fast: false matrix: - job: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + job: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] # Service containers to run with this job. Required for running tests services: diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/app/client/.eslintrc.json b/app/client/.eslintrc.json index 406a0da28d..7b2ab9a77a 100644 --- a/app/client/.eslintrc.json +++ b/app/client/.eslintrc.json @@ -36,7 +36,8 @@ "react/jsx-fragments": "error", "react/jsx-no-useless-fragment": "error", "sort-destructure-keys/sort-destructure-keys": ["error", {"caseSensitive": false}], - "no-console": "warn" + "no-console": "warn", + "no-debugger": "warn" }, "settings": { "import/resolver": { diff --git a/app/client/craco.common.config.js b/app/client/craco.common.config.js index d4fefe6466..0819a60227 100644 --- a/app/client/craco.common.config.js +++ b/app/client/craco.common.config.js @@ -1,6 +1,11 @@ const CracoAlias = require("craco-alias"); module.exports = { + style: { + postcss: { + plugins: [require("tailwindcss"), require("autoprefixer")], + }, + }, plugins: [ { plugin: CracoAlias, diff --git a/app/client/cypress.json b/app/client/cypress.json index 6ecfd0080f..9a28325624 100644 --- a/app/client/cypress.json +++ b/app/client/cypress.json @@ -12,6 +12,11 @@ "html": true, "json": false }, + "ignoreTestFiles": [ + "**/Smoke_TestSuite/Application/PgAdmin_spec*.js", + "**/Smoke_TestSuite/ServerSideTests/QueryPane/PostgresCRUDOps_spec*.js", + "**/Smoke_TestSuite/ClientSideTests/Onboarding/FirstTimeUserOnboarding_spec*.js" + ], "chromeWebSecurity": false, "viewportHeight": 900, "viewportWidth": 1400, diff --git a/app/client/cypress/fixtures/CMSdsl.json b/app/client/cypress/fixtures/CMSdsl.json new file mode 100644 index 0000000000..6479481a4f --- /dev/null +++ b/app/client/cypress/fixtures/CMSdsl.json @@ -0,0 +1,1434 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1118, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 750, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 45, + "minHeight": 740, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "NONE", + "widgetName": "Container1", + "borderColor": "transparent", + "isCanvas": true, + "displayName": "Container", + "iconSVG": "/static/media/icon.1977dca3.svg", + "topRow": 10, + "bottomRow": 70, + "parentRowSpace": 10, + "type": "CONTAINER_WIDGET", + "hideCard": false, + "parentColumnSpace": 17.28125, + "leftColumn": 2, + "children": [ + { + "widgetName": "Canvas1", + "rightColumn": 414.75, + "detachFromLayout": true, + "displayName": "Canvas", + "widgetId": "zos1ro7s26", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 570, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "version": 1, + "hideCard": true, + "parentId": "qbyu9spy6e", + "minHeight": 400, + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "widgetName": "Table1", + "defaultPageSize": 0, + "columnOrder": [ + "id", + "due", + "assignee", + "title", + "description" + ], + "isVisibleDownload": true, + "dynamicPropertyPathList": [], + "displayName": "Table", + "iconSVG": "/static/media/icon.db8a9cbd.svg", + "topRow": 1, + "bottomRow": 49, + "isSortable": true, + "parentRowSpace": 10, + "type": "TABLE_WIDGET", + "defaultSelectedRow": "0", + "hideCard": false, + "parentColumnSpace": 15.888671875, + "dynamicTriggerPathList": [], + "dynamicBindingPathList": [ + { "key": "tableData" }, + { "key": "primaryColumns.due.computedValue" }, + { "key": "primaryColumns.assignee.computedValue" }, + { "key": "primaryColumns.title.computedValue" }, + { + "key": "primaryColumns.description.computedValue" + }, + { "key": "primaryColumns.id.computedValue" } + ], + "leftColumn": 3, + "primaryColumns": { + "due": { + "index": 0, + "width": 150, + "id": "due", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "date", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "Due date", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.due))}}", + "inputFormat": "YYYY-MM-DD", + "outputFormat": "YYYY-MM-DD" + }, + "assignee": { + "index": 1, + "width": 150, + "id": "assignee", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "Assignee", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.assignee))}}" + }, + "title": { + "index": 2, + "width": 150, + "id": "title", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "title", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.title))}}" + }, + "description": { + "index": 3, + "width": 150, + "id": "description", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "description", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.description))}}" + }, + "id": { + "index": 4, + "width": 150, + "id": "id", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "id", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.id))}}" + } + }, + "delimiter": ",", + "key": "gim9l3s5p2", + "derivedColumns": {}, + "rightColumn": 28, + "textSize": "PARAGRAPH", + "widgetId": "h4mh57zrj1", + "isVisibleFilters": true, + "tableData": "{{get_data.data.headers.info}}", + "isVisible": true, + "label": "Data", + "searchKey": "", + "version": 3, + "totalRecordsCount": 0, + "parentId": "zos1ro7s26", + "renderMode": "CANVAS", + "isLoading": false, + "horizontalAlignment": "LEFT", + "isVisibleSearch": true, + "isVisiblePagination": true, + "verticalAlignment": "CENTER", + "columnSizeMap": { + "task": 245, + "step": 62, + "status": 75, + "due": 114, + "id": 60 + } + }, + { + "widgetName": "Form1", + "backgroundColor": "white", + "rightColumn": 61, + "isCanvas": true, + "displayName": "Form", + "iconSVG": "/static/media/icon.ea3e08d1.svg", + "widgetId": "nnyzjgf35r", + "topRow": 1, + "bottomRow": 49, + "parentRowSpace": 10, + "isVisible": true, + "type": "FORM_WIDGET", + "hideCard": false, + "parentId": "zos1ro7s26", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 15.888671875, + "leftColumn": 32, + "children": [ + { + "widgetName": "Canvas2", + "rightColumn": 381.328125, + "detachFromLayout": true, + "displayName": "Canvas", + "widgetId": "hvvj91zc5v", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 470, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "version": 1, + "hideCard": true, + "parentId": "nnyzjgf35r", + "minHeight": 400, + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "resetFormOnClick": true, + "widgetName": "FormButton1", + "onClick": "{{showModal('Modal1')}}", + "buttonColor": "#03B365", + "dynamicPropertyPathList": [{ "key": "onClick" }], + "displayName": "FormButton", + "iconSVG": "/static/media/icon.c8f649ed.svg", + "topRow": 41, + "bottomRow": 45, + "type": "FORM_BUTTON_WIDGET", + "hideCard": true, + "dynamicTriggerPathList": [{ "key": "onClick" }], + "leftColumn": 46, + "dynamicBindingPathList": [], + "text": "Mail", + "key": "3yro25toql", + "rightColumn": 62, + "isDefaultClickDisabled": true, + "widgetId": "xg747y64mq", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "disabledWhenInvalid": true, + "buttonVariant": "PRIMARY" + }, + { + "widgetName": "Input1", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 8, + "bottomRow": 20, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 5.645751953125, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 23, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 62, + "widgetId": "o97o13rsru", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.description}}" + }, + { + "widgetName": "Input2", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 22, + "bottomRow": 26, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 5.645751953125, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 24, + "dynamicBindingPathList": [], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 53, + "widgetId": "nfx01pnpxu", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "In Progress" + }, + { + "widgetName": "Input3", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 28, + "bottomRow": 32, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 5.645751953125, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 24, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 39, + "widgetId": "udt44xvy4p", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.due}}" + }, + { + "widgetName": "Input4", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 34, + "bottomRow": 38, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 5.645751953125, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 24, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "EMAIL", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 56, + "widgetId": "xf4mcip7d2", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.assignee}}" + }, + { + "widgetName": "Input5", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 2, + "bottomRow": 6, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 23, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 62, + "widgetId": "5ma00c7uoh", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.title}}" + }, + { + "widgetName": "Text4", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 2, + "bottomRow": 6, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Title", + "key": "6szdyqy4kw", + "rightColumn": 19, + "textAlign": "LEFT", + "widgetId": "0cqbtz8byh", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text5", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 8, + "bottomRow": 12, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Description", + "key": "6szdyqy4kw", + "rightColumn": 19, + "textAlign": "LEFT", + "widgetId": "clk59gs1y0", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text6", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 27, + "bottomRow": 31, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Due", + "key": "6szdyqy4kw", + "rightColumn": 19, + "textAlign": "LEFT", + "widgetId": "qvw2pd4je8", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text7", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 34, + "bottomRow": 38, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Assignee", + "key": "6szdyqy4kw", + "rightColumn": 19, + "textAlign": "LEFT", + "widgetId": "oc6y87200o", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text8", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 22, + "bottomRow": 26, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.887054443359375, + "dynamicTriggerPathList": [], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Status", + "key": "6szdyqy4kw", + "rightColumn": 19, + "textAlign": "LEFT", + "widgetId": "z9ke6qdjsv", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "hvvj91zc5v", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + } + ], + "key": "op9a2ii2bm" + } + ], + "key": "7me5bjjd3w" + }, + { + "widgetName": "Divider1", + "thickness": 2, + "displayName": "Divider", + "iconSVG": "/static/media/icon.cbe8f608.svg", + "topRow": 1, + "bottomRow": 49, + "parentRowSpace": 10, + "type": "DIVIDER_WIDGET", + "capType": "nc", + "hideCard": false, + "parentColumnSpace": 15.888671875, + "dynamicTriggerPathList": [], + "leftColumn": 29, + "dynamicBindingPathList": [], + "key": "ddn6drz055", + "dividerColor": "#EBEBEB", + "orientation": "vertical", + "strokeStyle": "solid", + "rightColumn": 31, + "widgetId": "1buwzc2fir", + "capSide": 0, + "isVisible": true, + "version": 1, + "parentId": "zos1ro7s26", + "renderMode": "CANVAS", + "isLoading": false + }, + { + "widgetName": "Button4", + "onClick": "{{showModal('Modal2')}}", + "buttonColor": "#03B365", + "dynamicPropertyPathList": [{ "key": "onClick" }], + "displayName": "Button", + "iconSVG": "/static/media/icon.cca02633.svg", + "topRow": 51, + "bottomRow": 55, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "parentColumnSpace": 17.28125, + "dynamicTriggerPathList": [{ "key": "onClick" }], + "leftColumn": 3, + "dynamicBindingPathList": [], + "text": "Delete Proposal", + "isDisabled": false, + "key": "8qgjvzg52n", + "rightColumn": 13, + "isDefaultClickDisabled": true, + "widgetId": "ykz7d6gbld", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "zos1ro7s26", + "renderMode": "CANVAS", + "isLoading": false, + "buttonVariant": "PRIMARY" + } + ], + "key": "op9a2ii2bm" + } + ], + "borderWidth": "0", + "key": "6winp67jci", + "backgroundColor": "#FFFFFF", + "rightColumn": 62, + "widgetId": "qbyu9spy6e", + "containerStyle": "card", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "0" + }, + { + "boxShadow": "NONE", + "widgetName": "Container2", + "borderColor": "transparent", + "isCanvas": true, + "displayName": "Container", + "iconSVG": "/static/media/icon.1977dca3.svg", + "topRow": 4, + "bottomRow": 10, + "parentRowSpace": 10, + "type": "CONTAINER_WIDGET", + "hideCard": false, + "parentColumnSpace": 17.28125, + "leftColumn": 2, + "children": [ + { + "widgetName": "Canvas3", + "rightColumn": 414.75, + "detachFromLayout": true, + "displayName": "Canvas", + "widgetId": "voago0sim9", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 390, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "version": 1, + "hideCard": true, + "parentId": "pa6iz68csi", + "minHeight": 400, + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "widgetName": "Text2", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 0, + "bottomRow": 4, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 15.888671875, + "dynamicTriggerPathList": [], + "leftColumn": 0, + "dynamicBindingPathList": [], + "text": "Submit New Proposal", + "key": "6szdyqy4kw", + "rightColumn": 16, + "textAlign": "LEFT", + "widgetId": "ml2f9v9ite", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "voago0sim9", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Button1", + "buttonColor": "#03B365", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca02633.svg", + "topRow": 0, + "bottomRow": 4, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "parentColumnSpace": 15.888671875, + "leftColumn": 54, + "text": "Submit", + "isDisabled": false, + "key": "8qgjvzg52n", + "rightColumn": 64, + "isDefaultClickDisabled": true, + "widgetId": "7h2wrs1iyt", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "voago0sim9", + "renderMode": "CANVAS", + "isLoading": false, + "buttonVariant": "PRIMARY" + } + ], + "key": "op9a2ii2bm" + } + ], + "borderWidth": "0", + "key": "6winp67jci", + "backgroundColor": "#FFFFFF", + "rightColumn": 62, + "widgetId": "pa6iz68csi", + "containerStyle": "card", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "0" + }, + { + "widgetName": "Text3", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 0, + "bottomRow": 4, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 17.28125, + "dynamicTriggerPathList": [], + "leftColumn": 29, + "dynamicBindingPathList": [], + "text": "Echo CMS ", + "key": "6szdyqy4kw", + "rightColumn": 36, + "textAlign": "LEFT", + "widgetId": "7wm8mw3w2d", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Modal1", + "isCanvas": true, + "displayName": "Modal", + "iconSVG": "/static/media/icon.4975978e.svg", + "topRow": 22, + "bottomRow": 46, + "parentRowSpace": 10, + "type": "MODAL_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "parentColumnSpace": 15.888671875, + "leftColumn": 21, + "children": [ + { + "widgetName": "Canvas4", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 460, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 468, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "leftColumn": 0, + "dynamicBindingPathList": [], + "children": [ + { + "widgetName": "Icon1", + "rightColumn": 64, + "onClick": "{{closeModal('Modal1')}}", + "color": "#040627", + "iconName": "cross", + "displayName": "Icon", + "iconSVG": "/static/media/icon.31d6cfe0.svg", + "widgetId": "11p85abc5c", + "topRow": 1, + "bottomRow": 5, + "isVisible": true, + "type": "ICON_WIDGET", + "version": 1, + "hideCard": true, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "leftColumn": 58, + "iconSize": 24, + "key": "8i0vmxrr9g" + }, + { + "widgetName": "Text9", + "rightColumn": 41, + "textAlign": "LEFT", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "widgetId": "3l9j4myadm", + "topRow": 1, + "bottomRow": 5, + "isVisible": true, + "fontStyle": "BOLD", + "type": "TEXT_WIDGET", + "textColor": "#231F20", + "version": 1, + "hideCard": false, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "dynamicTriggerPathList": [], + "leftColumn": 1, + "dynamicBindingPathList": [], + "fontSize": "HEADING2", + "text": "Send Mail", + "key": "6szdyqy4kw" + }, + { + "widgetName": "Button2", + "onClick": "{{closeModal('Modal1')}}", + "buttonColor": "#03B365", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca02633.svg", + "topRow": 40, + "bottomRow": 44, + "type": "BUTTON_WIDGET", + "hideCard": false, + "leftColumn": 7, + "text": "Close", + "isDisabled": false, + "key": "8qgjvzg52n", + "rightColumn": 19, + "isDefaultClickDisabled": true, + "widgetId": "o4gbjwa6om", + "buttonStyle": "PRIMARY", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "buttonVariant": "SECONDARY" + }, + { + "widgetName": "Button3", + "onClick": "{{send_mail.run()}}", + "buttonColor": "#03B365", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca02633.svg", + "topRow": 40, + "bottomRow": 44, + "type": "BUTTON_WIDGET", + "hideCard": false, + "dynamicTriggerPathList": [{ "key": "onClick" }], + "leftColumn": 45, + "dynamicBindingPathList": [], + "text": "Confirm", + "isDisabled": false, + "key": "8qgjvzg52n", + "rightColumn": 59, + "isDefaultClickDisabled": true, + "widgetId": "7wpkvbzz6w", + "buttonStyle": "PRIMARY_BUTTON", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "buttonVariant": "PRIMARY" + }, + { + "widgetName": "Text10", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 21, + "bottomRow": 25, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "leftColumn": 4, + "dynamicBindingPathList": [], + "text": "Content", + "key": "6szdyqy4kw", + "rightColumn": 20, + "textAlign": "LEFT", + "widgetId": "5bki1kuxaj", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text11", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 15, + "bottomRow": 19, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "leftColumn": 4, + "dynamicBindingPathList": [], + "text": "Subject", + "key": "6szdyqy4kw", + "rightColumn": 20, + "textAlign": "LEFT", + "widgetId": "jfyra9co8z", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text12", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 9, + "bottomRow": 13, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "leftColumn": 4, + "dynamicBindingPathList": [], + "text": "To", + "key": "6szdyqy4kw", + "rightColumn": 20, + "textAlign": "LEFT", + "widgetId": "b7pssbui2t", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "to_input", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 9, + "bottomRow": 13, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 25, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 60, + "widgetId": "q8j9r8htkk", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Input4.text}}" + }, + { + "widgetName": "subject", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 15, + "bottomRow": 19, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 25, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 60, + "widgetId": "hqzde4u9yp", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{send_mail.data.body.subject}}" + }, + { + "widgetName": "content", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 21, + "bottomRow": 36, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.34375, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 25, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "0ob107w0gh", + "isRequired": false, + "rightColumn": 60, + "widgetId": "cymag7b6bu", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "6fd0z9su7u", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{send_mail.data.body.content}}" + } + ], + "isDisabled": false, + "key": "op9a2ii2bm", + "rightColumn": 381.328125, + "detachFromLayout": true, + "widgetId": "6fd0z9su7u", + "isVisible": true, + "version": 1, + "parentId": "pxiiwd73sn", + "renderMode": "CANVAS", + "isLoading": false + } + ], + "key": "72wb1osqtu", + "height": 468, + "rightColumn": 45, + "detachFromLayout": true, + "widgetId": "pxiiwd73sn", + "canOutsideClickClose": true, + "canEscapeKeyClose": true, + "version": 2, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "width": 418 + }, + { + "widgetName": "Modal2", + "isCanvas": true, + "displayName": "Modal", + "iconSVG": "/static/media/icon.4975978e.svg", + "topRow": 15, + "bottomRow": 39, + "parentRowSpace": 10, + "type": "MODAL_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "parentColumnSpace": 15.888671875, + "leftColumn": 16, + "children": [ + { + "widgetName": "Canvas5", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 320, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 328, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "leftColumn": 0, + "dynamicBindingPathList": [], + "children": [ + { + "widgetName": "Text15Copy", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 14, + "bottomRow": 18, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "leftColumn": 4, + "dynamicBindingPathList": [], + "text": "Title", + "key": "92hnh6q5gi", + "rightColumn": 20, + "textAlign": "LEFT", + "widgetId": "3lmz9urj17", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "due", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 20, + "bottomRow": 24, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 27, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "49t2tbzqin", + "isRequired": false, + "rightColumn": 40, + "widgetId": "slrqhjfhmg", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.due}}" + }, + { + "widgetName": "Text13", + "rightColumn": 27, + "textAlign": "LEFT", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "widgetId": "ovmh5o8gk4", + "topRow": 1, + "bottomRow": 5, + "isVisible": true, + "fontStyle": "BOLD", + "type": "TEXT_WIDGET", + "textColor": "#231F20", + "version": 1, + "hideCard": false, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "dynamicTriggerPathList": [], + "leftColumn": 1, + "dynamicBindingPathList": [], + "fontSize": "HEADING2", + "text": "Delete this task", + "key": "6szdyqy4kw" + }, + { + "widgetName": "Button6", + "onClick": "{{delete_proposal.run(() => closeModal(), () => {})}}", + "buttonColor": "#03B365", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca02633.svg", + "topRow": 26, + "bottomRow": 30, + "type": "BUTTON_WIDGET", + "hideCard": false, + "dynamicTriggerPathList": [{ "key": "onClick" }], + "leftColumn": 45, + "dynamicBindingPathList": [], + "text": "Confirm", + "isDisabled": false, + "key": "8qgjvzg52n", + "rightColumn": 60, + "isDefaultClickDisabled": true, + "widgetId": "lkt2yqvuin", + "buttonStyle": "PRIMARY_BUTTON", + "recaptchaV2": false, + "isVisible": true, + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "buttonVariant": "PRIMARY" + }, + { + "widgetName": "assignee", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 8, + "bottomRow": 12, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 27, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "49t2tbzqin", + "isRequired": false, + "rightColumn": 58, + "widgetId": "adg0b4mlzi", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.assignee}}" + }, + { + "widgetName": "title", + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595.svg", + "topRow": 14, + "bottomRow": 18, + "parentRowSpace": 10, + "autoFocus": false, + "type": "INPUT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 27, + "dynamicBindingPathList": [{ "key": "defaultText" }], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "49t2tbzqin", + "isRequired": false, + "rightColumn": 58, + "widgetId": "birjt5sfb1", + "isVisible": true, + "label": "", + "allowCurrencyChange": false, + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "iconAlign": "left", + "defaultText": "{{Table1.selectedRow.title}}" + }, + { + "widgetName": "Text14", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 20, + "bottomRow": 24, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "leftColumn": 5, + "dynamicBindingPathList": [], + "text": "Due", + "key": "92hnh6q5gi", + "rightColumn": 21, + "textAlign": "LEFT", + "widgetId": "8ve9jkgxyz", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + }, + { + "widgetName": "Text15", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 8, + "bottomRow": 12, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "parentColumnSpace": 6.0625, + "dynamicTriggerPathList": [], + "leftColumn": 4, + "dynamicBindingPathList": [], + "text": "Assignee", + "key": "92hnh6q5gi", + "rightColumn": 20, + "textAlign": "LEFT", + "widgetId": "bi0yxies82", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "7m8cn2pecl", + "renderMode": "CANVAS", + "isLoading": false, + "fontSize": "PARAGRAPH" + } + ], + "isDisabled": false, + "key": "op9a2ii2bm", + "rightColumn": 381.328125, + "detachFromLayout": true, + "widgetId": "7m8cn2pecl", + "isVisible": true, + "version": 1, + "parentId": "zy74peeknl", + "renderMode": "CANVAS", + "isLoading": false + } + ], + "key": "72wb1osqtu", + "height": 328, + "rightColumn": 40, + "detachFromLayout": true, + "widgetId": "zy74peeknl", + "canOutsideClickClose": true, + "canEscapeKeyClose": true, + "version": 2, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "width": 506 + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "619ca92fd3208212f819cfdb", + "name": "get_data", + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ], + [ + { + "id": "619cbfd3d3208212f819d04c", + "name": "send_mail", + "pluginType": "API", + "jsonPathKeys": ["content.text", "subject.text", "to_input.text"], + "timeoutInMillisecond": 10000 + } + ] + ], + "new": false +} diff --git a/app/client/cypress/fixtures/DocumentViewerDsl.json b/app/client/cypress/fixtures/DocumentViewerDsl.json new file mode 100644 index 0000000000..08067ae4f6 --- /dev/null +++ b/app/client/cypress/fixtures/DocumentViewerDsl.json @@ -0,0 +1,45 @@ +{ + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1280, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1230, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 39, + "minHeight": 1240, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "widgetName": "DocumentViewer1", + "rightColumn": 36, + "isCanvas": false, + "displayName": "Document Viewer", + "iconSVG": "/static/media/icon.5c440258.svg", + "widgetId": "0vgeledo9u", + "topRow": 20, + "bottomRow": 60, + "parentRowSpace": 10, + "isVisible": true, + "type": "DOCUMENT_VIEWER_WIDGET", + "version": 1, + "hideCard": false, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 19.8125, + "leftColumn": 12, + "docUrl": "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + "key": "gprnooq82y" + } + ] + } \ No newline at end of file diff --git a/app/client/cypress/fixtures/chartUpdatedDsl.json b/app/client/cypress/fixtures/chartUpdatedDsl.json index 2573bb11e6..bd48114539 100644 --- a/app/client/cypress/fixtures/chartUpdatedDsl.json +++ b/app/client/cypress/fixtures/chartUpdatedDsl.json @@ -2,7 +2,7 @@ "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", - "rightColumn": 966, + "rightColumn": 776, "snapColumns": 64, "detachFromLayout": true, "widgetId": "0", @@ -13,7 +13,7 @@ "parentRowSpace": 1, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 20, + "version": 44, "minHeight": 450, "parentColumnSpace": 1, "dynamicBindingPathList": [], @@ -91,7 +91,7 @@ "widgetId": "rglduihhzk", "containerStyle": "none", "topRow": 0, - "bottomRow": 20, + "bottomRow": 450, "parentRowSpace": 1, "isVisible": true, "type": "CANVAS_WIDGET", @@ -103,32 +103,31 @@ "dynamicBindingPathList": [], "children": [ { + "widgetName": "Test", + "rightColumn": 49, + "widgetId": "snzfh3qjo8", + "topRow": 10, + "bottomRow": 43, + "parentRowSpace": 10, "isVisible": true, - "widgetName": "Chart1", - "chartType": "LINE_CHART", - "chartName": "Last week's revenue", - "allowScroll": false, + "type": "CHART_WIDGET", "version": 1, + "parentId": "rglduihhzk", + "isLoading": false, "chartData": { "q4pm3w97mo": { "seriesName": "Sales", "data": "" } }, - "xAxisName": "Last Week", "yAxisName": "Total Order Revenue $", - "type": "CHART_WIDGET", - "isLoading": false, "parentColumnSpace": 14.59375, - "parentRowSpace": 10, - "leftColumn": 20, - "rightColumn": 44, - "topRow": 16, - "bottomRow": 48, - "parentId": "rglduihhzk", - "widgetId": "snzfh3qjo8", + "dynamicTriggerPathList": [], + "chartName": "Last week's revenue", + "leftColumn": 5, "dynamicBindingPathList": [], - "dynamicTriggerPathList": [] + "xAxisName": "Last Week", + "chartType": "LINE_CHART" } ] } @@ -173,9 +172,7 @@ "parentColumnSpace": 1, "leftColumn": 0, "dynamicBindingPathList": [], - "children": [ - null - ] + "children": [] } ] } diff --git a/app/client/cypress/fixtures/datasources.json b/app/client/cypress/fixtures/datasources.json index 7b5f731a18..e9982552b8 100644 --- a/app/client/cypress/fixtures/datasources.json +++ b/app/client/cypress/fixtures/datasources.json @@ -42,5 +42,6 @@ "mockHostAddress":"fake-api.cvuydmurdlas.us-east-1.rds.amazonaws.com", "mockDatabaseName": "fakeapi", "mockDatabaseUsername": "fakeapi", - "mockDatabasePassword": "LimitedAccess123#" + "mockDatabasePassword": "LimitedAccess123#", + "readonly":"readonly" } diff --git a/app/client/cypress/fixtures/exported-app.json b/app/client/cypress/fixtures/exported-app.json new file mode 100644 index 0000000000..8426723b86 --- /dev/null +++ b/app/client/cypress/fixtures/exported-app.json @@ -0,0 +1,313 @@ +{ + "exportedApplication": { + "userPermissions": [ + "canComment:applications", + "manage:applications", + "export:applications", + "read:applications", + "publish:applications", + "makePublic:applications" + ], + "name": "2eacca10", + "isPublic": false, + "appIsExample": false, + "unreadCommentThreads": 0, + "color": "#F1DEFF", + "icon": "cat", + "new": true + }, + "datasourceList": [], + "pageList": [ + { + "userPermissions": [ + "read:pages", + "manage:pages" + ], + "gitSyncId": "61602709ae8b022ed53c23c7_2021-10-08T11:10:01.203693Z", + "unpublishedPage": { + "name": "Page1", + "layouts": [ + { + "id": "61602709ae8b022ed53c23c8", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1292, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "minHeight": 1292, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "rightColumn": 8, + "orientation": "VERTICAL", + "snapColumns": 16, + "widgetId": "mxbaasg65u", + "containerStyle": "card", + "topRow": 0, + "bottomRow": 9, + "parentRowSpace": 38, + "isVisible": true, + "type": "CONTAINER_WIDGET", + "isLoading": false, + "parentColumnSpace": 75.25, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "backgroundColor": "transparent", + "widgetName": "59gdivzv7s", + "rightColumn": 602, + "orientation": "VERTICAL", + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "bxekwxgc1i", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 342, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "image": "", + "widgetName": "Image1", + "rightColumn": 10, + "widgetId": "glksllew0g", + "topRow": 2, + "bottomRow": 5, + "parentRowSpace": 38, + "isVisible": true, + "type": "IMAGE_WIDGET", + "parentId": "bxekwxgc1i", + "isLoading": false, + "parentColumnSpace": 34.6875, + "imageShape": "RECTANGLE", + "leftColumn": 6, + "defaultImage": "https://res.cloudinary.com/drako999/image/upload/v1589196259/default.png" + } + ] + } + ] + }, + { + "backgroundColor": "#FFFFFF", + "widgetName": "Container3", + "rightColumn": 16, + "orientation": "VERTICAL", + "snapColumns": 16, + "widgetId": "i331vll2mg", + "containerStyle": "card", + "topRow": 9, + "bottomRow": 23, + "parentRowSpace": 38, + "isVisible": true, + "type": "CONTAINER_WIDGET", + "isLoading": false, + "parentColumnSpace": 75.25, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "backgroundColor": "transparent", + "widgetName": "rhfg2vf1n5", + "rightColumn": 1204, + "orientation": "VERTICAL", + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "rglduihhzk", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 532, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "widgetName": "Chart1", + "rightColumn": 8, + "allowHorizontalScroll": false, + "widgetId": "hwi9cwhg43", + "topRow": 1, + "bottomRow": 9, + "parentRowSpace": 38, + "isVisible": true, + "type": "CHART_WIDGET", + "parentId": "rglduihhzk", + "isLoading": false, + "chartData": [ + { + "seriesName": "Sales", + "data": [ + { + "x": "Mon", + "y": 10000 + }, + { + "x": "Tue", + "y": 12000 + }, + { + "x": "Wed", + "y": 32000 + }, + { + "x": "Thu", + "y": 28000 + }, + { + "x": "Fri", + "y": 14000 + }, + { + "x": "Sat", + "y": 19000 + }, + { + "x": "Sun", + "y": 36000 + } + ] + } + ], + "yAxisName": "Total Order Revenue $", + "parentColumnSpace": 71.75, + "chartName": "Sales on working days", + "leftColumn": 2, + "dynamicBindingPathList": [], + "xAxisName": "Last Week", + "chartType": "LINE_CHART" + } + ] + } + ] + }, + { + "backgroundColor": "#FFFFFF", + "widgetName": "Container4", + "rightColumn": 16, + "orientation": "VERTICAL", + "snapColumns": 16, + "widgetId": "qznzsquf70", + "containerStyle": "card", + "topRow": 0, + "bottomRow": 9, + "parentRowSpace": 38, + "isVisible": true, + "type": "CONTAINER_WIDGET", + "isLoading": false, + "parentColumnSpace": 75.25, + "dynamicBindingPathList": [], + "leftColumn": 8, + "children": [ + { + "backgroundColor": "transparent", + "widgetName": "3bn6uv0vy4", + "rightColumn": 602, + "orientation": "VERTICAL", + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "7vm5mdu8ey", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 342, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "widgetName": "Text1", + "rightColumn": 7, + "textAlign": "LEFT", + "widgetId": "9xcfqahpw2", + "topRow": 3, + "bottomRow": 4, + "parentRowSpace": 38, + "isVisible": true, + "type": "TEXT_WIDGET", + "parentId": "bxekwxgc1i", + "isLoading": false, + "parentColumnSpace": 34.6875, + "leftColumn": 3, + "text": "Label" + } + ] + } + ] + } + ] + }, + "layoutOnLoadActions": [], + "new": false + } + ], + "userPermissions": [] + }, + "publishedPage": { + "name": "Page1", + "layouts": [ + { + "id": "61602709ae8b022ed53c23c8", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1254, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 4, + "minHeight": 1292, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [] + }, + "new": false + } + ], + "userPermissions": [] + }, + "new": true + } + ], + "publishedDefaultPageName": "Page1", + "unpublishedDefaultPageName": "Page1", + "actionList": [], + "actionCollectionList": [], + "decryptedFields": {}, + "publishedLayoutmongoEscapedWidgets": {}, + "unpublishedLayoutmongoEscapedWidgets": {} +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/previewMode.json b/app/client/cypress/fixtures/previewMode.json new file mode 100644 index 0000000000..4076beeed2 --- /dev/null +++ b/app/client/cypress/fixtures/previewMode.json @@ -0,0 +1,75 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1254, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindingPathList": [], + "version": 4, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "text": "Submit", + "buttonStyle": "PRIMARY_BUTTON", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "type": "BUTTON_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 3, + "topRow": 3, + "bottomRow": 4, + "parentId": "0", + "widgetId": "p2jmk6tut5" + }, + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input1", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 4, + "rightColumn": 9, + "topRow": 0, + "bottomRow": 1, + "parentId": "0", + "widgetId": "7grthyn6qd" + }, + { + "isVisible": true, + "text": "Label", + "textStyle": "LABEL", + "textAlign": "LEFT", + "widgetName": "Text1", + "type": "TEXT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 5, + "rightColumn": 9, + "topRow": 1, + "bottomRow": 2, + "parentId": "0", + "widgetId": "xzjri1a2e6" + } + ] + } +} diff --git a/app/client/cypress/fixtures/snippingTableDsl.json b/app/client/cypress/fixtures/snippingTableDsl.json new file mode 100644 index 0000000000..68a9bd5283 --- /dev/null +++ b/app/client/cypress/fixtures/snippingTableDsl.json @@ -0,0 +1,175 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1095, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 5016, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 46, + "minHeight": 930, + "parentColumnSpace": 1, + "dynamicBindingPathList": [ + + ], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "defaultSelectedRow": "0", + "label": "Data", + "widgetName": "Table1", + "searchKey": "", + "textSize": "PARAGRAPH", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "totalRecordsCount": 0, + "defaultPageSize": 0, + "dynamicBindingPathList": [ + { + "key": "primaryColumns.step.computedValue" + }, + { + "key": "primaryColumns.task.computedValue" + }, + { + "key": "primaryColumns.status.computedValue" + }, + { + "key": "primaryColumns.action.computedValue" + } + ], + "primaryColumns": { + "step": { + "index": 0, + "width": 150, + "id": "step", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isDerived": false, + "label": "step", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.step))}}" + }, + "task": { + "index": 1, + "width": 150, + "id": "task", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isDerived": false, + "label": "task", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.task))}}" + }, + "status": { + "index": 2, + "width": 150, + "id": "status", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isDerived": false, + "label": "status", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.status))}}" + }, + "action": { + "index": 3, + "width": 150, + "id": "action", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "button", + "textSize": "PARAGRAPH", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isDisabled": false, + "isDerived": false, + "label": "action", + "onClick": "{{currentRow.step === '#1' ? showAlert('Done', 'success') : currentRow.step === '#2' ? navigateTo('https://docs.appsmith.com/core-concepts/connecting-to-data-sources/querying-a-database',undefined,'NEW_WINDOW') : navigateTo('https://docs.appsmith.com/core-concepts/displaying-data-read/display-data-tables',undefined,'NEW_WINDOW')}}", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.action))}}" + } + }, + "derivedColumns": { + }, + "tableData": [ + { + "step": "#1", + "task": "Drop a table", + "status": "✅", + "action": "" + }, + { + "step": "#2", + "task": "Create a query fetch_users with the Mock DB", + "status": "--", + "action": "" + }, + { + "step": "#3", + "task": "Bind the query using => fetch_users.data", + "status": "--", + "action": "" + } + ], + "columnSizeMap": { + "task": 245, + "step": 62, + "status": 75 + }, + "columnOrder": [ + "step", + "task", + "status", + "action" + ], + "isVisibleSearch": true, + "isVisibleFilters": true, + "isVisibleDownload": true, + "isVisiblePagination": true, + "isSortable": true, + "delimiter": ",", + "version": 3, + "type": "TABLE_WIDGET", + "hideCard": false, + "displayName": "Table", + "key": "y3v2mu6lds", + "iconSVG": "/static/media/icon.db8a9cbd.svg", + "widgetId": "tulofhk1fb", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 16.921875, + "parentRowSpace": 10, + "leftColumn": 15, + "rightColumn": 49, + "topRow": 22, + "bottomRow": 50, + "parentId": "0" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/tabdsl.json b/app/client/cypress/fixtures/tabdsl.json index 83795bf2fd..4806bab95c 100644 --- a/app/client/cypress/fixtures/tabdsl.json +++ b/app/client/cypress/fixtures/tabdsl.json @@ -25,18 +25,18 @@ "widgetName": "Tabs1", "tabs": [ { - "label": "Tab 1", + "label": "Tab1", "id": "tab1", "widgetId": "uxle0mrg8t" }, { - "label": "Tab 2", + "label": "Tab2", "id": "tab2", "widgetId": "9hy1rmqb2f" } ], "shouldShowTabs": true, - "defaultTab": "Tab 1", + "defaultTab": "Tab1", "type": "TABS_WIDGET", "isLoading": false, "parentColumnSpace": 74, @@ -51,7 +51,7 @@ { "type": "CANVAS_WIDGET", "tabId": "tab1", - "tabName": "Tab 1", + "tabName": "Tab1", "widgetId": "uxle0mrg8t", "parentId": "y6rla3dsd6", "detachFromLayout": true, @@ -69,7 +69,7 @@ { "type": "CANVAS_WIDGET", "tabId": "tab2", - "tabName": "Tab 2", + "tabName": "Tab2", "widgetId": "9hy1rmqb2f", "parentId": "y6rla3dsd6", "detachFromLayout": true, @@ -88,4 +88,4 @@ } ] } -} \ No newline at end of file +} diff --git a/app/client/cypress/fixtures/tableNewDsl.json b/app/client/cypress/fixtures/tableNewDsl.json index 51b179ed90..168cc676ed 100644 --- a/app/client/cypress/fixtures/tableNewDsl.json +++ b/app/client/cypress/fixtures/tableNewDsl.json @@ -2,149 +2,205 @@ "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", - "rightColumn": 1224, - "snapColumns": 16, + "rightColumn": 656, + "snapColumns": 64, "detachFromLayout": true, "widgetId": "0", "topRow": 0, - "bottomRow": 1280, + "bottomRow": 2860, "containerStyle": "none", - "snapRows": 33, + "snapRows": 128, "parentRowSpace": 1, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 8, + "version": 46, "minHeight": 1292, "parentColumnSpace": 1, "dynamicBindingPathList": [], "leftColumn": 0, - "children": [{ - "isVisible": true, - "label": "Data", - "widgetName": "Table1", - "searchKey": "", - "tableData": "{{\n[\n {\n \"id\": 2381224,\n \"email\": \"michael.lawson@reqres.in\",\n \"userName\": \"Michael Lawson\",\n \"productName\": \"Chicken Sandwich\",\n \"orderAmount\": 4.99\n },\n {\n \"id\": 2736212,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"userName\": \"Lindsay Ferguson\",\n \"productName\": \"Tuna Salad\",\n \"orderAmount\": 9.99\n },\n {\n \"id\": 6788734,\n \"email\": \"tobias.funke@reqres.in\",\n \"userName\": \"Tobias Funke\",\n \"productName\": \"Beef steak\",\n \"orderAmount\": 19.99\n }\n]\n}}", - "type": "TABLE_WIDGET", - "isLoading": false, - "parentColumnSpace": 74, - "parentRowSpace": 40, - "leftColumn": 2, - "rightColumn": 10, - "topRow": 12, - "bottomRow": 19, - "parentId": "0", - "widgetId": "sb070qr2ir", - "dynamicBindingPathList": [], - "primaryColumns": { - "id": { - "index": 0, - "width": 150, - "id": "id", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textColor": "#4E5D78", - "textSize": "PARAGRAPH", - "fontStyle": "NORMAL", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDerived": false, - "label": "id", - "computedValue": "" + "children": [ + { + "widgetName": "Table1", + "columnOrder": [ + "id", + "email", + "userName", + "productName", + "orderAmount" + ], + "isVisibleDownload": true, + "topRow": 48, + "bottomRow": 71, + "parentRowSpace": 10, + "isSortable": true, + "type": "TABLE_WIDGET", + "parentColumnSpace": 74, + "dynamicTriggerPathList": [], + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "tableData" + }, + { + "key": "primaryColumns.id.computedValue" + }, + { + "key": "primaryColumns.email.computedValue" + }, + { + "key": "primaryColumns.userName.computedValue" + }, + { + "key": "primaryColumns.productName.computedValue" + }, + { + "key": "primaryColumns.orderAmount.computedValue" + } + ], + "primaryColumns": { + "id": { + "index": 0, + "width": 150, + "id": "id", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "fontStyle": "REGULAR", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "id", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.id))}}" + }, + "email": { + "index": 1, + "width": 150, + "id": "email", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "fontStyle": "REGULAR", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "email", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.email))}}" + }, + "userName": { + "index": 2, + "width": 150, + "id": "userName", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "fontStyle": "REGULAR", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "userName", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.userName))}}" + }, + "productName": { + "index": 3, + "width": 150, + "id": "productName", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "fontStyle": "REGULAR", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "productName", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.productName))}}" + }, + "orderAmount": { + "index": 4, + "width": 150, + "id": "orderAmount", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "PARAGRAPH", + "fontStyle": "REGULAR", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellVisible": true, + "isDerived": false, + "label": "orderAmount", + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.orderAmount))}}" + } }, - "email": { - "index": 1, - "width": 150, - "id": "email", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textColor": "#4E5D78", - "textSize": "PARAGRAPH", - "fontStyle": "NORMAL", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDerived": false, - "label": "email", - "computedValue": "" - }, - "userName": { - "index": 2, - "width": 150, - "id": "userName", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textColor": "#4E5D78", - "textSize": "PARAGRAPH", - "fontStyle": "NORMAL", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDerived": false, - "label": "userName", - "computedValue": "" - }, - "productName": { - "index": 3, - "width": 150, - "id": "productName", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textColor": "#4E5D78", - "textSize": "PARAGRAPH", - "fontStyle": "NORMAL", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDerived": false, - "label": "productName", - "computedValue": "" - }, - "orderAmount": { - "index": 4, - "width": 150, - "id": "orderAmount", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textColor": "#4E5D78", - "textSize": "PARAGRAPH", - "fontStyle": "NORMAL", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDerived": false, - "label": "orderAmount", - "computedValue": "" + "delimiter": ",", + "migrated": true, + "derivedColumns": {}, + "rightColumn": 57, + "textSize": "PARAGRAPH", + "widgetId": "sb070qr2ir", + "isVisibleFilters": true, + "tableData": "{{\n[\n {\n \"id\": 2381224,\n \"email\": \"michael.lawson@reqres.in\",\n \"userName\": \"Michael Lawson\",\n \"productName\": \"Chicken Sandwich\",\n \"orderAmount\": 4.99\n },\n {\n \"id\": 2736212,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"userName\": \"Lindsay Ferguson\",\n \"productName\": \"Tuna Salad\",\n \"orderAmount\": 9.99\n },\n {\n \"id\": 6788734,\n \"email\": \"tobias.funke@reqres.in\",\n \"userName\": \"Tobias Funke\",\n \"productName\": \"Beef steak\",\n \"orderAmount\": 19.99\n }\n]\n}}", + "isVisible": true, + "label": "Data", + "searchKey": "", + "fontStyle": "REGULAR", + "version": 3, + "parentId": "0", + "isLoading": false, + "horizontalAlignment": "LEFT", + "isVisibleSearch": true, + "isVisiblePagination": true, + "verticalAlignment": "CENTER", + "columnSizeMap": { + "email": 261 } + }, + { + "widgetName": "Text1", + "rightColumn": 60, + "textAlign": "LEFT", + "displayName": "Text", + "widgetId": "16zkg2v0na", + "topRow": 228, + "bottomRow": 284, + "parentRowSpace": 10, + "isVisible": true, + "type": "TEXT_WIDGET", + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "hideCard": false, + "parentId": "0", + "isLoading": false, + "parentColumnSpace": 17.28125, + "dynamicTriggerPathList": [], + "leftColumn": 48, + "dynamicBindingPathList": [ + { + "key": "text" + } + ], + "fontSize": "PARAGRAPH", + "text": "{{Table1.triggeredRow.userName}}", + "key": "r76o6tqjaz" } - }, { - "widgetName": "Text1", - "displayName": "Text", - "topRow": 57, - "bottomRow": 71, - "parentRowSpace": 10, - "type": "TEXT_WIDGET", - "hideCard": false, - "parentColumnSpace": 17.28125, - "dynamicTriggerPathList": [], - "leftColumn": 12, - "dynamicBindingPathList": [{"key":"text"}], - "text": "{{Table1.triggeredRow.userName}}", - "key": "r76o6tqjaz", - "rightColumn": 15, - "textAlign": "LEFT", - "widgetId": "16zkg2v0na", - "isVisible": true, - "fontStyle": "BOLD", - "textColor": "#231F20", - "parentId": "0", - "isLoading": false, - "fontSize": "PARAGRAPH" - }] + ] } } \ No newline at end of file diff --git a/app/client/cypress/fixtures/widgetPopupDsl.json b/app/client/cypress/fixtures/widgetPopupDsl.json new file mode 100644 index 0000000000..1a5dd506b9 --- /dev/null +++ b/app/client/cypress/fixtures/widgetPopupDsl.json @@ -0,0 +1,269 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1280, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1990, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 42, + "minHeight": 1970, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "widgetName": "TreeSelect1", + "displayName": "TreeSelect", + "iconSVG": "/static/media/icon.bd99caba.svg", + "labelText": "Label", + "topRow": 14, + "bottomRow": 20.9, + "parentRowSpace": 10, + "type": "SINGLE_SELECT_TREE_WIDGET", + "hideCard": false, + "defaultOptionValue": + "GREEN" + , + "parentColumnSpace": 19.8125, + "leftColumn": 5, + "options": [ + { + "label": "Blue", + "value": "BLUE", + "children": [ + { + "label": "Dark Blue", + "value": "DARK BLUE" + }, + { + "label": "Light Blue", + "value": "LIGHT BLUE" + } + ] + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText": "Select option", + "isDisabled": false, + "key": "oe8sn5hbxk", + "isRequired": false, + "rightColumn": 18, + "widgetId": "p65c0i01ic", + "isVisible": true, + "version": 1, + "expandAll": false, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "allowClear": false + }, + { + "widgetName": "MultiTreeSelect2", + "displayName": "Multi TreeSelect", + "iconSVG": "/static/media/icon.b3e28afb.svg", + "labelText": "Label", + "topRow": 31, + "bottomRow": 37.9, + "parentRowSpace": 10, + "type": "MULTI_SELECT_TREE_WIDGET", + "hideCard": false, + "mode": "SHOW_ALL", + "defaultOptionValue": [ + "GREEN" + ], + "parentColumnSpace": 19.8125, + "leftColumn": 5, + "options": [ + { + "label": "Blue", + "value": "BLUE", + "children": [ + { + "label": "Dark Blue", + "value": "DARK BLUE" + }, + { + "label": "Light Blue", + "value": "LIGHT BLUE" + } + ] + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText": "Select option(s)", + "isDisabled": false, + "key": "oe8sn5hbxk", + "isRequired": false, + "rightColumn": 18, + "widgetId": "buewtu97rf", + "isVisible": true, + "version": 1, + "expandAll": false, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "allowClear": false + }, + { + "widgetName": "Select1", + "isFilterable": true, + "displayName": "Select", + "iconSVG": "/static/media/icon.bd99caba.svg", + "labelText": "Label", + "topRow": 50, + "bottomRow": 56.9, + "parentRowSpace": 10, + "type": "DROP_DOWN_WIDGET", + "serverSideFiltering": false, + "hideCard": false, + "defaultOptionValue": "GREEN", + "selectionType": "SINGLE_SELECT", + "parentColumnSpace": 19.8125, + "dynamicTriggerPathList": [], + "leftColumn": 5, + "dynamicBindingPathList": [], + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText": "Select option", + "isDisabled": false, + "key": "1b9j4l2c65", + "isRequired": false, + "rightColumn": 18, + "widgetId": "vnoxjpwd8a", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false + }, + { + "widgetName": "MultiSelect1", + "displayName": "MultiSelect", + "iconSVG": "/static/media/icon.a3495809.svg", + "labelText": "Label", + "topRow": 67, + "bottomRow": 73.9, + "parentRowSpace": 10, + "type": "MULTI_SELECT_WIDGET", + "serverSideFiltering": false, + "hideCard": false, + "defaultOptionValue": [ + "GREEN" + ], + "parentColumnSpace": 19.8125, + "leftColumn": 5, + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText": "Select option(s)", + "isDisabled": false, + "key": "z8p14a1gb8", + "isRequired": false, + "rightColumn": 18, + "widgetId": "c2j24v7j6b", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false + }, + { + "isCompact": false, + "widgetName": "MenuButton1", + "displayName": "Menu Button", + "iconSVG": "/static/media/icon.0341d17d.svg", + "topRow": 80, + "bottomRow": 84, + "parentRowSpace": 10, + "type": "MENU_BUTTON_WIDGET", + "hideCard": false, + "parentColumnSpace": 19.8125, + "leftColumn": 5, + "isDisabled": false, + "key": "ehx7dguolk", + "rightColumn": 18, + "menuVariant": "PRIMARY", + "widgetId": "33f9n054tq", + "menuItems": { + "menuItem1": { + "label": "First Menu Item", + "id": "menuItem1", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 0 + }, + "menuItem2": { + "label": "Second Menu Item", + "id": "menuItem2", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 1 + }, + "menuItem3": { + "label": "Third Menu Item", + "id": "menuItem3", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 2 + } + }, + "isVisible": true, + "label": "Open Menu", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "menuColor": "#03B365" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js new file mode 100644 index 0000000000..feb39f3acb --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js @@ -0,0 +1,119 @@ +const dsl = require("../../../fixtures/CMSdsl.json"); +const homePage = require("../../../locators/HomePage.json"); +const apiwidget = require("../../../locators/apiWidgetslocator.json"); +const apiEditor = require("../../../locators/ApiEditor.json"); +const appPage = require("../../../locators/CMSApplocators.json"); + +describe("Content Management System App", function() { + let orgid; + let newOrganizationName; + let appname; + let datasourceName; + + before(() => { + cy.addDsl(dsl); + }); + beforeEach(() => { + cy.startRoutesForDatasource(); + }); + + it("Create Get echo Api call", function() { + cy.NavigateToAPI_Panel(); + cy.CreateAPI("get_data"); + // creating get request using echo + cy.enterDatasourceAndPath("https://mock-api.appsmith.com/echo", "/get"); + cy.get(apiwidget.headerKey).type("info"); + cy.xpath("//span[text()='Key']").click(); + // entering the data in header + cy.get( + apiwidget.headerValue, + ).type( + '[{"due":"2021-11-23","assignee":"Dan.Wyman@hotmail.com","title":"Recusan","description":"Ut quisquam eum beatae facere eos aliquam laborum ea.","id":"1"},{"due":"2021-11-23","assignee":"Dashawn_Maggio30@gmail.com","title":"Dignissimos eaque","description":"Consequatur corrupti et possimus en.","id":"2"},{"due":"2021-11-24","assignee":"Curt50@gmail.com","title":"Voluptas explicabo","description":"Quia ratione optio et maiores.","id":"3"},{"due":"2021-11-23","assignee":"Shanna63@hotmail.com","title":"Aut omnis.","description":"Neque rerum numquam veniam voluptatum id. Aut daut.","id":"4"}]', + { parseSpecialCharSequences: false }, + ); + cy.SaveAndRunAPI(); + cy.ResponseStatusCheck("200"); + }); + it("Create Post echo Api call", function() { + cy.NavigateToAPI_Panel(); + cy.CreateAPI("send_mail"); + cy.get(apiEditor.ApiVerb).click(); + cy.xpath(appPage.selectPost).click(); + // creating post request using echo + cy.enterDatasourceAndPath("https://mock-api.appsmith.com/echo", "/post"); + cy.contains(apiEditor.bodyTab).click({ force: true }); + cy.xpath(apiwidget.postbody) + .click({ force: true }) + .clear(); + // binding the data with widgets in body tab + cy.xpath(apiwidget.postbody) + .click({ force: true }) + .focus() + .type( + '{"to":"{{to_input.text}}","subject":"{{subject.text}}","content":"{{content.text}}"}', + { parseSpecialCharSequences: false }, + ) + .type("{del}{del}{del}"); + cy.SaveAndRunAPI(); + cy.ResponseStatusCheck("201"); + }); + + it("Create Delete echo Api call", function() { + cy.NavigateToAPI_Panel(); + cy.CreateAPI("delete_proposal"); + cy.get(apiEditor.ApiVerb).click(); + cy.xpath(appPage.selectDelete).click(); + // creating delete request using echo + cy.enterDatasourceAndPath("https://mock-api.appsmith.com/echo", "/delete"); + cy.contains(apiEditor.bodyTab).click({ force: true }); + // binding the data with widgets in body tab + cy.xpath(apiwidget.postbody) + .click({ force: true }) + .focus() + .type( + '{"title":"{{title.text}}","due":"{{due.text}}","assignee":"{{assignee.text}}"}', + { parseSpecialCharSequences: false }, + ) + .type("{del}{del}{del}"); + cy.SaveAndRunAPI(); + //cy.ResponseStatusCheck("200"); + }); + it("Send mail and verify post request body", function() { + // navigating to canvas + cy.xpath(appPage.pagebutton).click(); + cy.xpath(appPage.submitButton).should("be.visible"); + cy.xpath("//div[text()='3']").click({ force: true }); + cy.xpath(appPage.mailButton).click(); + // verifying the mail to send and asserting post call's response + cy.xpath(appPage.sendMailText).should("be.visible"); + cy.xpath("//input[@value='Curt50@gmail.com']").should("be.visible"); + cy.xpath(appPage.subjectField).type("Test"); + cy.xpath(appPage.contentField) + .last() + .type("Task completed", { force: true }); + cy.xpath(appPage.confirmButton).click(); + cy.xpath(appPage.closeButton).click(); + cy.xpath(appPage.pagebutton).click(); + cy.xpath(appPage.datasourcesbutton).click(); + cy.xpath(appPage.postApi).click(); + cy.ResponseCheck("Test"); + // cy.ResponseCheck("Task completed"); + cy.ResponseCheck("Curt50@gmail.com"); + }); + + it("Delete proposal and verify delete request body", function() { + // navigating back to canvas + cy.xpath(appPage.pagebutton).click(); + cy.xpath(appPage.submitButton).should("be.visible"); + cy.xpath("//span[text()='Dan.Wyman@hotmail.com']").click({ force: true }); + // deleting the proposal and asserting delete call's response + cy.xpath(appPage.deleteButton).click(); + cy.xpath(appPage.deleteTaskText).should("be.visible"); + cy.xpath(appPage.confirmButton).click(); + cy.xpath(appPage.pagebutton).click(); + cy.xpath(appPage.datasourcesbutton).click(); + cy.xpath(appPage.deleteApi).click(); + cy.ResponseCheck("Dan.Wyman@hotmail.com"); + cy.ResponseCheck("Recusan"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js index b6a8b053a1..5f4b28ca30 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js @@ -19,17 +19,6 @@ describe("PgAdmin Clone App", function() { }); it("Add dsl and authenticate datasource", function() { - cy.NavigateToHome(); - appname = localStorage.getItem("AppName"); - cy.get(homePage.searchInput).type(appname); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(2000); - cy.get(homePage.applicationCard) - .first() - .trigger("mouseover"); - cy.get(homePage.appEditIcon) - .first() - .click({ force: true }); // authenticating datasource cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); @@ -133,7 +122,6 @@ describe("PgAdmin Clone App", function() { parseSpecialCharSequences: false, }, ); - cy.wait(3000); cy.WaitAutoSave(); cy.runQuery(); // clicking on chevron icon to go back to the datasources page @@ -176,14 +164,14 @@ describe("PgAdmin Clone App", function() { cy.xpath(appPage.addTablename) .clear() .type(Table); - cy.wait(2000); // adding column to the table cy.xpath(appPage.addColumn).click(); - cy.wait(2000); cy.xpath(appPage.columnNamefield).should("be.visible"); cy.xpath(appPage.datatypefield).should("be.visible"); cy.xpath(appPage.addTablename).type("id"); - cy.xpath(appPage.textField).click(); + cy.get(appPage.dropdownChevronDown) + .last() + .click(); cy.xpath(appPage.selectDatatype).click(); // switching on the Primary Key toggle cy.get(widgetsPage.switchWidgetInactive) @@ -194,7 +182,6 @@ describe("PgAdmin Clone App", function() { .last() .click(); cy.xpath(appPage.submitButton).click(); - cy.wait(2000); cy.xpath(appPage.addColumn).should("be.visible"); cy.xpath(appPage.submitButton).click({ force: true }); cy.xpath(appPage.closeButton).click(); @@ -206,7 +193,6 @@ describe("PgAdmin Clone App", function() { cy.xpath(appPage.viewButton) .first() .click({ force: true }); - cy.wait(2000); // deleting the table through modal cy.xpath(appPage.deleteButton) .last() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/Error_handling_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/Error_handling_spec.js new file mode 100644 index 0000000000..9d03a21d70 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/Error_handling_spec.js @@ -0,0 +1,65 @@ +const commonlocators = require("../../../../locators/commonlocators.json"); +const dsl = require("../../../../fixtures/buttonApiDsl.json"); +const widgetsPage = require("../../../../locators/Widgets.json"); +const publishPage = require("../../../../locators/publishWidgetspage.json"); + +describe("Test Create Api and Bind to Table widget", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Test_Add users api and execute api", function() { + cy.createAndFillApi(this.data.userApi, "/random"); + cy.RunAPI(); + }); + + it("Call the api without error handling", () => { + cy.SearchEntityandOpen("Button1"); + cy.get(widgetsPage.toggleOnClick) + .invoke("attr", "class") + .then((classes) => { + if (classes.includes("is-active")) { + cy.get(widgetsPage.toggleOnClick).click(); + } + }); + cy.get(widgetsPage.toggleOnClick).click(); + + cy.get(".t--property-control-onclick").then(($el) => { + cy.updateCodeInput($el, "{{Api1.run()}}"); + }); + + cy.PublishtheApp(); + + cy.get(publishPage.buttonWidget).click(); + cy.wait("@postExecute").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + + cy.get(commonlocators.toastAction) + .should("have.length", 1) + .should("contain.text", "failed to execute"); + + cy.get(publishPage.backToEditor).click({ force: true }); + }); + + it("Call the api with error handling", () => { + cy.SearchEntityandOpen("Button1"); + + cy.get(".t--property-control-onclick").then(($el) => { + cy.updateCodeInput($el, "{{Api1.run(() => {}, () => {})}}"); + }); + + cy.PublishtheApp(); + + cy.get(publishPage.buttonWidget).click(); + cy.wait("@postExecute").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + + cy.get(commonlocators.toastAction).should("not.exist"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ExecutionParams_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ExecutionParams_spec.js index c3e2ad3f79..fdcb53d7e6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ExecutionParams_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ExecutionParams_spec.js @@ -67,11 +67,11 @@ describe("API Panel Test Functionality", function() { "onclick", "{{Query1.run(undefined, undefined, { tableName: 'users' })}}", ); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - // Choose dynamic button cy.SearchEntityandOpen("DynamicButton"); + cy.wait(2000); // toggle js of onClick + cy.get(".t--property-control-onclick").scrollIntoView(); cy.get(".t--property-control-onclick") .find(".t--js-toggle") .click({ force: true }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActiveDatasource/ActiveDatasource.spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActiveDatasource/ActiveDatasource.spec.js new file mode 100644 index 0000000000..734ba2ca5e --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActiveDatasource/ActiveDatasource.spec.js @@ -0,0 +1,43 @@ +const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +const queryEditor = require("../../../../locators/QueryEditor.json"); + +let datasourceName, actionName; +describe("Google Sheet datasource test cases", function() { + before(() => { + cy.NavigateToDatasourceEditor(); + cy.get(datasourceEditor.googleSheets).click(); + cy.getPluginFormsAndCreateDatasource(); + cy.fillGoogleSheetsDatasourceForm(); + cy.get("@createDatasource").then((httpResponse) => { + datasourceName = httpResponse.response.body.data.name; + }); + cy.NavigateToApiEditor(); + cy.get( + `${datasourceEditor.datasourceCard} ${datasourceEditor.createQuerty}`, + ) + .last() + .click(); + cy.wait("@createNewApi").then((httpResponse) => { + actionName = httpResponse.response.body.data.name; + }); + cy.NavigateToApiEditor(); + }); + + it("Create a new query from the datasource editor", function() { + cy.get( + `.t--datasource-name:contains('${datasourceName}') .t--queries-for-SAAS`, + ).should("have.text", "1 query on this page"); + }); + + after(() => { + cy.get(`.t--entity-name:contains('${actionName}')`).click(); + cy.get(queryEditor.queryMoreAction).click(); + cy.get(queryEditor.deleteUsingContext).click(); + cy.wait("@deleteAction").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.deleteDatasource(datasourceName); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ApiPaneTests/API_all_sidebar_actions_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ApiPaneTests/API_all_sidebar_actions_spec.js index b718d908aa..c80df8bde7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ApiPaneTests/API_all_sidebar_actions_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ApiPaneTests/API_all_sidebar_actions_spec.js @@ -1,11 +1,15 @@ const commonlocators = require("../../../../locators/commonlocators.json"); +const testdata = require("../../../../fixtures/testdata.json"); +const apiwidget = require("../../../../locators/apiWidgetslocator.json"); describe("API Panel Test Functionality ", function() { it("Test API copy/Move/delete feature", function() { cy.log("Login Successful"); + cy.Createpage("SecondPage"); cy.NavigateToAPI_Panel(); cy.log("Navigation to API Panel screen successful"); cy.CreateAPI("FirstAPI"); + cy.enterDatasourceAndPath(testdata.baseUrl, "{{ '/random' }}"); cy.log("Creation of FirstAPI Action successful"); cy.GlobalSearchEntity("FirstAPI"); cy.xpath('//*[local-name()="g" and @id="Icon/Outline/more-vertical"]') @@ -13,13 +17,16 @@ describe("API Panel Test Functionality ", function() { .should("be.hidden") .invoke("show") .click({ force: true }); - cy.CopyAPIToHome(); + cy.copyEntityToPage("SecondPage"); cy.GlobalSearchEntity("FirstAPICopy"); // click on learn how link cy.get(".t--learn-how-apis-link").click(); // this should open in a global search modal cy.get(commonlocators.globalSearchModal); cy.get("body").click(0, 0); + cy.MoveAPIToPage("Page1"); + cy.SearchEntityandOpen("FirstAPICopy"); + cy.get(apiwidget.resourceUrl).should("contain.text", "{{ '/random' }}"); cy.DeleteAPIFromSideBar(); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js index e12c84211b..1ec1162d54 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js @@ -16,7 +16,6 @@ describe("Duplicate application", function() { cy.SearchEntityandOpen("Input1"); cy.intercept("PUT", "/api/v1/layouts/*/pages/*").as("inputUpdate"); cy.testJsontext("defaulttext", "A"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@inputUpdate").then((response) => { parentApplicationDsl = response.response.body.data.dsl; }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/ForkApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/ForkApplication_spec.js index 6e4a539eea..8ff956831a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/ForkApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/ForkApplication_spec.js @@ -16,7 +16,6 @@ describe("Fork application across orgs", function() { cy.SearchEntityandOpen("Input1"); cy.intercept("PUT", "/api/v1/layouts/*/pages/*").as("inputUpdate"); cy.testJsontext("defaulttext", "A"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@inputUpdate").then((response) => { parentApplicationDsl = response.response.body.data.dsl; }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/BindApi_withPageload_Input_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/BindApi_withPageload_Input_spec.js index 163331e028..18c141b2da 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/BindApi_withPageload_Input_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/BindApi_withPageload_Input_spec.js @@ -24,7 +24,7 @@ describe("Binding the API with pageOnLoad and input Widgets", function() { it("Input widget updated with deafult data", function() { cy.SearchEntityandOpen("Input1"); cy.get(widgetsPage.defaultInput).type("3"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -41,7 +41,7 @@ describe("Binding the API with pageOnLoad and input Widgets", function() { cy.get(widgetsPage.defaultInput).type(testdata.pageloadBinding, { parseSpecialCharSequences: false, }); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_API_with_List_Widget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_API_with_List_Widget_spec.js index a6390c9ddb..8107b7ade6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_API_with_List_Widget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_API_with_List_Widget_spec.js @@ -32,7 +32,6 @@ describe("Test Create Api and Bind to Table widget", function() { it("Test_Validate the Api data is updated on List widget", function() { cy.SearchEntityandOpen("List1"); cy.testJsontext("items", "{{Api1.data.users}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.get(".t--draggable-textwidget span").should("have.length", 8); cy.get(".t--draggable-textwidget span") @@ -55,7 +54,6 @@ describe("Test Create Api and Bind to Table widget", function() { cy.get(publishPage.backToEditor).click({ force: true }); cy.SearchEntityandOpen("List1"); cy.testJsontext("itemspacing\\(px\\)", "50"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.get(".t--draggable-textwidget span").should("have.length", 6); cy.get(".t--draggable-textwidget span") .first() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_DatePicker_Text_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_DatePicker_Text_spec.js index e24a16ac22..efd357f1ac 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_DatePicker_Text_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_DatePicker_Text_spec.js @@ -18,7 +18,6 @@ describe("Binding the Datepicker and Text Widget", function() { */ cy.openPropertyPane("textwidget"); cy.testJsontext("text", "{{DatePicker1.selectedDate}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); /** * Set the Calender for today's date in DatePicker1 @@ -57,7 +56,6 @@ describe("Binding the Datepicker and Text Widget", function() { * Bind the datepicker1 to text widget */ cy.testJsontext("text", "{{DatePicker1.defaultDate}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); /** * Fetching the date on DatePicker2 @@ -78,7 +76,6 @@ describe("Binding the Datepicker and Text Widget", function() { cy.ClearDateFooter(); cy.setDate(1, "ddd MMM DD YYYY"); cy.get(commonlocators.onDateSelectedField).click(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); /** *Validate the date in text widget diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_InputWidget_Table_Sorting_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_InputWidget_Table_Sorting_spec.js index 5098b22703..32d35bef32 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_InputWidget_Table_Sorting_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_InputWidget_Table_Sorting_spec.js @@ -12,7 +12,7 @@ describe("Binding the Table and input Widget", function() { it("Input widget test with default value from table widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputWidget + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -67,7 +67,6 @@ describe("Binding the Table and input Widget", function() { } }); cy.get(widgetsPage.defaultInput).type(testdata.sortedColumn); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Widgets.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Widgets.js index 01f26e0859..d0e11bcb0f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Widgets.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Widgets.js @@ -17,7 +17,6 @@ describe("Binding the Widgets with JSObject", function() { cy.createJSObject('return "Success";'); cy.SearchEntityandOpen("Input2"); cy.testJsontext("defaulttext", "{{JSObject1.myFun1()}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TabWidget_Input_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TabWidget_Input_spec.js index 2ae4ad53d0..bf5d67f620 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TabWidget_Input_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TabWidget_Input_spec.js @@ -14,7 +14,7 @@ describe("Binding the input Widget with tab Widget", function() { it("Input widget test with default value from tab widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.tabBinding + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js index 08e452738b..d4c82cac6f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js @@ -93,7 +93,6 @@ describe("Test Create Api and Bind to Table widget", function() { //cy.openPropertyPane("textwidget"); /** Bind the Table widget with Text widget*/ cy.testJsontext("text", "{{Table1.selectedRow.url}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.SearchEntityandOpen("Table1"); cy.testJsontext("tabledata", "{{Api2.data.users}}"); cy.callApi("Api2"); @@ -107,6 +106,5 @@ describe("Test Create Api and Bind to Table widget", function() { cy.get(publishPage.backToEditor).click({ force: true }); cy.ValidatePaginateResponseUrlData(apiPage.apiPaginationNextTest); cy.wait(5000); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableWidget_selectedRow_Input_widget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableWidget_selectedRow_Input_widget_spec.js index 8240329402..e910448660 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableWidget_selectedRow_Input_widget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableWidget_selectedRow_Input_widget_spec.js @@ -14,7 +14,6 @@ describe("Binding the table widget and input Widget", function() { it("Input widget test with default value from table widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputWidget + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -25,7 +24,6 @@ describe("Binding the table widget and input Widget", function() { it("validation of data displayed in input widgets based on selected row", function() { cy.SearchEntityandOpen("Table1"); cy.testJsontext("defaultselectedrow", "2"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.readTabledataPublish("2", "0").then((tabData) => { const tabValue = tabData; expect(tabValue).to.be.equal("6788734"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_tableApi_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_tableApi_spec.js index fb8eeb6b6e..bc32d9f9a0 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_tableApi_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_tableApi_spec.js @@ -31,7 +31,6 @@ describe("Test Create Api and Bind to Table widget", function() { cy.SearchEntityandOpen("Table1"); //cy.openPropertyPane("tablewidget"); cy.testJsontext("tabledata", "{{Api1.data.users}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); /** * readTabledata--> is to read the table contents diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Binding_Table_Widget_DefaultSearch_Input_widget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Binding_Table_Widget_DefaultSearch_Input_widget_spec.js index 8fccbd8106..602def1707 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Binding_Table_Widget_DefaultSearch_Input_widget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Binding_Table_Widget_DefaultSearch_Input_widget_spec.js @@ -12,7 +12,7 @@ describe("Binding the Table and input Widget", function() { it("Input widget test with default value from table widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputWidget + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -23,7 +23,7 @@ describe("Binding the Table and input Widget", function() { it("validation of data displayed in input widgets based on search value set", function() { cy.SearchEntityandOpen("Table1"); cy.testJsontext("defaultsearchtext", "2736212"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").isSelectRow(0); cy.readTabledataPublish("0", "0").then((tabData) => { const tabValue = tabData; diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js index 5946b3f939..174a4035f3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js @@ -21,7 +21,7 @@ describe("Binding the button Widgets and validating NavigateTo Page functionalit .contains("Navigate to") .click(); cy.enterNavigatePageName(testdata.externalPage); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(300); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/InputWidgets_NavigateTo_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/InputWidgets_NavigateTo_validation_spec.js index 55c2dfed36..e8bb7f9d68 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/InputWidgets_NavigateTo_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/InputWidgets_NavigateTo_validation_spec.js @@ -22,7 +22,7 @@ describe("Binding the multiple Widgets and validating NavigateTo Page", function .contains("Navigate to") .click(); cy.enterNavigatePageName(pageid); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(300); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Invalid_binding_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Invalid_binding_spec.js index 4ac24c45be..d4bbc3425c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Invalid_binding_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Invalid_binding_spec.js @@ -15,13 +15,11 @@ describe("Binding the multiple widgets and validating default data", function() cy.openPropertyPane("dropdownwidget"); cy.testJsontext("options", JSON.stringify(testdata.defaultdataBinding)); cy.evaluateErrorMessage(testdata.dropdownErrorMsg); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Table widget test with invalid binding value", function() { cy.openPropertyPane("tablewidget"); cy.testJsontext("tabledata", JSON.stringify(testdata.defaultdataBinding)); cy.evaluateErrorMessage(testdata.tableWidgetErrorMsg); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js index 0890b32a48..d2a2f6414a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js @@ -17,7 +17,6 @@ describe("Table Widget with Input Widget and Navigate to functionality validatio cy.openPropertyPane("tablewidget"); cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); cy.testJsontext("tabledata", JSON.stringify(testdata.TablePagination)); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Create MyPage and valdiate if its successfully created", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js index 6e63058ae3..b2e3eb57c7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js @@ -23,7 +23,6 @@ describe("Table Widget and Navigate to functionality validation", function() { .contains("Navigate to") .click(); cy.enterNavigatePageName(pageid); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Create MyPage and valdiate if its successfully created", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Table_Property_ToggleJs_With_Binding_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Table_Property_ToggleJs_With_Binding_spec.js index 9fccda65f7..e6677703d7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Table_Property_ToggleJs_With_Binding_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Table_Property_ToggleJs_With_Binding_spec.js @@ -21,7 +21,7 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingAlign); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.readTabledataValidateCSS("0", "0", "justify-content", "flex-start"); cy.readTabledataValidateCSS("1", "0", "justify-content", "flex-end"); }); @@ -29,6 +29,7 @@ describe("Table Widget property pane feature validation", function() { it("Table widget change text size and validate", function() { cy.readTabledataValidateCSS("0", "0", "font-size", "14px"); cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleTextAlign) .first() @@ -41,12 +42,13 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.selectTextSize("Heading 1"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.readTabledataValidateCSS("0", "0", "font-size", "24px"); }); it("Table widget toggle test for text size", function() { cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleTextSize) .first() @@ -54,13 +56,14 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingSize); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.readTabledataValidateCSS("0", "0", "font-size", "14px"); cy.readTabledataValidateCSS("1", "0", "font-size", "24px"); }); it("Table widget toggle test for vertical Alignment", function() { cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleTextSize) .first() @@ -73,13 +76,14 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingVerticalAlig); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.readTabledataValidateCSS("0", "0", "align-items", "flex-start"); cy.readTabledataValidateCSS("1", "0", "align-items", "flex-end"); }); it("Table widget toggle test for style Alignment", function() { cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleVerticalAlig) .first() @@ -92,13 +96,14 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingStyle); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.readTabledataValidateCSS("0", "0", "font-style", "normal"); cy.readTabledataValidateCSS("1", "0", "font-style", "italic"); }); it("Table widget toggle test for text color", function() { cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleVerticalAlig) .first() @@ -111,7 +116,7 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingTextColor); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout"); cy.readTabledataValidateCSS("0", "0", "color", "rgb(0, 128, 0)"); cy.readTabledataValidateCSS("1", "0", "color", "rgb(255, 0, 0)"); @@ -119,6 +124,7 @@ describe("Table Widget property pane feature validation", function() { it("Table widget toggle test for background color", function() { cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); cy.editColumn("id"); cy.get(widgetsPage.toggleJsColor) .first() @@ -131,7 +137,7 @@ describe("Table Widget property pane feature validation", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.toggleJsAndUpdate("tabledata", testdata.bindingTextColor); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout"); cy.readTabledataValidateCSS( "0", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js index 414f712323..6bb2d12dee 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js @@ -55,7 +55,7 @@ describe("Binding the multiple widgets and validating default data", function() it("Button widget test with on action query run", function() { cy.SearchEntityandOpen("Button1"); cy.executeDbQuery("Query1"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -66,7 +66,7 @@ describe("Binding the multiple widgets and validating default data", function() it("Input widget test with default value update with query data", function() { cy.SearchEntityandOpen("Input1"); cy.get(widgetsPage.defaultInput).type(testdata.defaultInputQuery); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Default_data_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Default_data_validation_spec.js index 6df7a1139a..5742d70371 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Default_data_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Default_data_validation_spec.js @@ -14,7 +14,7 @@ describe("Binding the multiple widgets and validating default data", function() it("Input widget test with default value from table widget", function() { cy.openPropertyPane("inputwidget"); cy.testJsontext("defaulttext", testdata.defaultInputWidget + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -26,7 +26,7 @@ describe("Binding the multiple widgets and validating default data", function() it("Dropdown widget test with default value from table widget", function() { cy.openPropertyPane("dropdownwidget"); cy.testJsontext("options", JSON.stringify(testdata.deafultDropDownWidget)); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Dependancy_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Dependancy_validation_spec.js index 01294617d8..ad14432e9a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Dependancy_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_Dependancy_validation_spec.js @@ -20,7 +20,7 @@ describe("Binding the multiple input Widget", function() { it("Cyclic depedancy error message validation", function() { cy.openPropertyPane("inputwidget"); cy.testJsontext("defaulttext", testdata.defaultMoustacheData + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -32,7 +32,7 @@ describe("Binding the multiple input Widget", function() { it("Binding input widget1 and validating", function() { cy.openPropertyPane("inputwidget"); cy.testJsontext("defaulttext", testdata.defaultdata); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -47,7 +47,7 @@ describe("Binding the multiple input Widget", function() { it("Binding second input widget with first input widget and validating", function() { cy.SearchEntityandOpen("Input2"); cy.testJsontext("defaulttext", testdata.defaultMoustacheData + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -76,7 +76,7 @@ describe("Binding the multiple input Widget", function() { it("Binding third input widget with first input widget and validating", function() { cy.SearchEntityandOpen("Input3"); cy.testJsontext("defaulttext", testdata.defaultMoustacheData + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_form_input_table_default_validation.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_form_input_table_default_validation.js index a53a0d0ea4..08788d2ce7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_form_input_table_default_validation.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widgets_form_input_table_default_validation.js @@ -14,7 +14,7 @@ describe("Binding the multiple input Widget", function() { it("Input widget test with default value from table widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputWidget + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/aTobAndbToaBasictest_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/aTobAndbToaBasictest_spec.js index da2cc8b7f7..d8e3fd5cb2 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/aTobAndbToaBasictest_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/aTobAndbToaBasictest_spec.js @@ -14,7 +14,6 @@ describe("aTob and bToa library tests ", function() { it("Input widget test with default value for atob method", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.atobInput + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -25,7 +24,6 @@ describe("aTob and bToa library tests ", function() { it("Input widget test with default value for btoa method", function() { cy.SearchEntityandOpen("Input2"); cy.testJsontext("defaulttext", testdata.btoaInput + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/loadashBasictest_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/loadashBasictest_spec.js index bef4cac06b..831dc1f145 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/loadashBasictest_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/loadashBasictest_spec.js @@ -14,7 +14,7 @@ describe("Loadash basic test with input Widget", function() { it("Input widget test with default value from another Input widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputBinding + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -25,7 +25,7 @@ describe("Loadash basic test with input Widget", function() { it("Input widget test with default value for loadash function", function() { cy.SearchEntityandOpen("Input2"); cy.testJsontext("defaulttext", testdata.loadashInput + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/momentBasictest_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/momentBasictest_spec.js index 2159c1b17c..2f88e6056d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/momentBasictest_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/momentBasictest_spec.js @@ -14,7 +14,7 @@ describe("Moment basic test with input Widget", function() { it("Input widget test with default value from another Input widget", function() { cy.SearchEntityandOpen("Input1"); cy.testJsontext("defaulttext", testdata.defaultInputBinding + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", @@ -25,7 +25,7 @@ describe("Moment basic test with input Widget", function() { it("Binding second input widget with first input widget and validating", function() { cy.SearchEntityandOpen("Input2"); cy.testJsontext("defaulttext", testdata.momentInput + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_Data_point_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_Data_point_spec.js index 83cc051d0b..08004d9648 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_Data_point_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_Data_point_spec.js @@ -17,7 +17,7 @@ describe("Chart Widget Functionality", function() { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.testJsontext("defaulttext", testdata.bindChartData + "}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_spec.js index 797a0fc37c..dfc09fcf12 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Chart_spec.js @@ -62,8 +62,6 @@ describe("Chart Widget Functionality", function() { .click({ force: true }) .type(this.data.ylabel); - //Close edit prop - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.PublishtheApp(); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Custom_Chart_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Custom_Chart_spec.js index bb85f5ed76..a9196b32f5 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Custom_Chart_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Custom_Chart_spec.js @@ -63,7 +63,7 @@ describe("Chart Widget Functionality around custom chart feature", function() { .type(this.data.ylabel); //Close edit prop - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.PublishtheApp(); }); @@ -116,7 +116,6 @@ describe("Chart Widget Functionality around custom chart feature", function() { }); //Close edit prop - cy.get(commonlocators.editPropCrossButton).click(); cy.PublishtheApp(); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js new file mode 100644 index 0000000000..935f5b01d3 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js @@ -0,0 +1,18 @@ +const dsl = require("../../../../fixtures/DocumentViewerDsl.json"); +const explorer = require("../../../../locators/explorerlocators.json"); + +describe("DocumentViewer Widget Functionality", function() { + beforeEach(() => { + cy.addDsl(dsl); + }); + + it("Add new DocumentViewer", () => { + cy.get(explorer.addWidget).click(); + cy.dragAndDropToCanvas("documentviewerwidget", { x: 300, y: 300 }); + cy.get(".t--widget-documentviewerwidget").should("exist"); + }); + + it("Open Existing DocumentViewer from Widgets list", () => { + cy.get(".t--widget-documentviewerwidget").should("exist"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Image_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Image_spec.js index 4c719f025f..7257026a17 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Image_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Image_spec.js @@ -37,7 +37,7 @@ describe("Image Widget Functionality", function() { cy.openPropertyPane("imagewidget"); //Zoom validation cy.changeZoomLevel("1x (No Zoom)"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.get(commonlocators.imgWidget) .invoke("attr", "style") .should("not.contain", "zoom-in"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Duplicate_ColumnName_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Duplicate_ColumnName_spec.js new file mode 100644 index 0000000000..1fbdb1ac60 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Duplicate_ColumnName_spec.js @@ -0,0 +1,34 @@ +const widgetsPage = require("../../../../locators/Widgets.json"); +const dsl = require("../../../../fixtures/tableNewDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("prevent duplicate column name in table", function() { + before(() => { + cy.addDsl(dsl); + }); + + //todo(yash/tolu/pawan) reverting this test for now + // it("evaluted value popup should show when focus on duplicate column input", function() { + // cy.openPropertyPane("tablewidget"); + // // Updating the column name ; "id" > "TestUpdated" + // cy.tableColumnPopertyUpdate("id", "TestUpdated"); + // // Updating the column name ; "email" > "TestUpdated" + // cy.tableColumnPopertyUpdate("email", "TestUpdated"); + // cy.wait("@updateLayout"); + // cy.get(commonlocators.evaluatedTypeTitle).should("exist"); + + // // Updating the column name ; "userName" > "TestUpdated2" + // // this will move focus of input to another column input and let popup close + // cy.tableColumnPopertyUpdate("userName", "TestUpdated2"); + + // // duplicate column's border should remain red + // cy.get("[data-rbd-draggable-id='email'] > div > div").should( + // "have.class", + // "has-duplicate-label", + // ); + // }); + + afterEach(() => { + // put your clean up code if any + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Filter_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Filter_spec.js new file mode 100644 index 0000000000..b59b5ee697 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Filter_spec.js @@ -0,0 +1,260 @@ +const widgetsPage = require("../../../../locators/Widgets.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +const publish = require("../../../../locators/publishWidgetspage.json"); +const dsl = require("../../../../fixtures/tableWidgetDsl.json"); + +describe("Table Widget Filter Functionality", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Table Widget Functionality", function() { + cy.openPropertyPane("tablewidget"); + cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.testJsontext("tabledata", JSON.stringify(this.data.TableInput)); + cy.wait("@updateLayout"); + }); + + it("Table Widget Functionality To validate download csv and download Excel", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("the value is" + tabValue); + cy.get(publish.searchInput) + .first() + .type(tabData); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + }); + cy.downloadData("Download as CSV"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(5000); + //cy.validateDownload('Table1.csv'); + cy.verifyDownload("Table1.csv"); + cy.downloadData("Download as Excel"); + cy.wait(5000); + //cy.validateDownload('Table1.xlsx'); + cy.verifyDownload("Table1.xlsx"); + cy.get(publish.searchInput) + .first() + .within(() => { + return cy.get("input").clear(); + }) + .type("7434532"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); + cy.readTabledataPublish("3", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Byron Fields"); + }); + }); + }); + + it("Table Widget Functionality To Filter The Data using does not contain", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("the value is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("does not contain") + .click(); + cy.get(publish.inputValue).type("Lindsay"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.get(widgetsPage.filterApplyBtn).click({ force: true }); + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).not.to.be.equal("Lindsay Ferguson"); + }); + cy.get(widgetsPage.filterCloseBtn).click({ force: true }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter).click(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click({ force: true }); + }); + }); + + it("Table Widget Functionality To Filter The Data using OR operator ", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("the value is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("contains") + .click(); + cy.get(publish.inputValue).type("Tobias Funke"); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.AddFilterWithOperator( + "OR", + "email", + "contains", + "tobias.funke@reqres.in", + ); + cy.wait(500); + cy.get(widgetsPage.filterApplyBtn).click({ force: true }); + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Tobias Funke"); + }); + cy.get(widgetsPage.filterCloseBtn).click({ force: true }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter) + .first() + .click({ force: true }); + cy.get(publish.removeFilter) + .last() + .click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click({ force: true }); + }); + }); + + it("Table Widget Functionality To Filter The Data using AND operator ", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("the value is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("contains") + .click(); + cy.get(publish.inputValue).type("Tobias Funke"); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.AddFilterWithOperator( + "AND", + "email", + "contains", + "tobias.funke@reqres.in", + ); + cy.wait(500); + cy.get(widgetsPage.filterApplyBtn).click({ force: true }); + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Tobias Funke"); + }); + cy.get(widgetsPage.filterCloseBtn).click({ force: true }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter) + .first() + .click({ force: true }); + cy.get(publish.removeFilter) + .last() + .click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click({ force: true }); + }); + }); + + it("Table Widget Functionality To Filter The Data using OR operator with different data ", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("the value is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("contains") + .click(); + cy.get(publish.inputValue).type("Lindsay Ferguson"); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500); + cy.AddFilterWithOperator( + "OR", + "email", + "contains", + "tobias.funke@reqres.in", + ); + cy.wait(500); + cy.get(widgetsPage.filterApplyBtn).click({ force: true }); + cy.wait(500); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + }); + cy.readTabledataPublish("1", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Tobias Funke"); + }); + cy.get(widgetsPage.filterCloseBtn).click({ force: true }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter) + .first() + .click({ force: true }); + cy.wait(500); + cy.get(publish.removeFilter) + .last() + .click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); + cy.readTabledataPublish("0", "3").then((tabData) => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click({ force: true }); + }); + }); + + afterEach(() => { + // put your clean up code if any + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_GeneralProperty_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_GeneralProperty_spec.js index 1067bcc218..ab24ef4fea 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_GeneralProperty_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_GeneralProperty_spec.js @@ -84,7 +84,6 @@ describe("Table Widget property pane feature validation", function() { cy.get(widgetsPage.underline).click({ force: true }); // Verify the font style is underline cy.readTabledataValidateCSS("1", "0", "text-decoration-line", "underline"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Test to validate vertical allignment", function() { @@ -105,14 +104,9 @@ describe("Table Widget property pane feature validation", function() { .click({ force: true }); // Verify the vertical alignment is bottom cy.readTabledataValidateCSS("1", "0", "align-items", "flex-end"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Table widget toggle test for text alignment", function() { - // Open property pane - cy.openPropertyPane("tablewidget"); - // Open property pane of column "id" - cy.editColumn("id"); // Click on text align JS cy.get(widgetsPage.toggleTextAlign) .first() @@ -122,7 +116,7 @@ describe("Table Widget property pane feature validation", function() { // Change the text align value to right for michael and left for others cy.toggleJsAndUpdate("tabledata", testdata.bindingGenAlign); // Close propert pane - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + // Verify the text michael id is right aligned cy.readTabledataValidateCSS("0", "0", "justify-content", "flex-end"); // Verify the 2nd id is left aligned @@ -132,10 +126,7 @@ describe("Table Widget property pane feature validation", function() { it("Table widget change text size and validate", function() { // Verify font size is 14px cy.readTabledataValidateCSS("0", "0", "font-size", "14px"); - // Open property pane - cy.openPropertyPane("tablewidget"); - // Open property pane - cy.editColumn("id"); + // Click on text size JS cy.get(widgetsPage.toggleTextAlign) .first() @@ -153,42 +144,46 @@ describe("Table Widget property pane feature validation", function() { // Verify the font size is 24px cy.readTabledataValidateCSS("0", "0", "font-size", "24px"); // close propert pane - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); // Verify the font size is 24px cy.readTabledataValidateCSS("0", "0", "font-size", "24px"); }); - it("Test to validate open new tab icon shows when URL type data is hovered", function() { + it("Test to validate open new tab icon shows when URL type data validate link text ", function() { // Open property pane cy.openPropertyPane("tablewidget"); + + // go back to 1st + cy.get(commonlocators.editPropBackButton).click({ force: true }); // Open email property pane cy.editColumn("email"); // Change column type to url cy.changeColumnType("URL"); - // Show the url hidden icon in front of first email + //Check all the occurance + cy.get(".link-text").should("have.length", "3"); + /* cy.get( `.t--widget-tablewidget .tbody .td[data-rowindex=1][data-colindex=1] .hidden-icon`, - ).invoke("show"); - // Verify the url icon is visible on hover over email + ) + .should("be.hidden") + .invoke("show"); cy.get( `.t--widget-tablewidget .tbody .td[data-rowindex=1][data-colindex=1] .hidden-icon`, ).should("be.visible"); - // Close property pane - cy.get(commonlocators.editPropCrossButton).click(); + */ }); it("Edit column name and test for table header changes", function() { - // Open property pane - cy.openPropertyPane("tablewidget"); + cy.get(commonlocators.editPropBackButton).click({ force: true }); // Open email property pane cy.editColumn("email"); // CHange the Column email name to Email Address cy.editColName("Email Address"); // verify changed email name is visible cy.get(".draggable-header:contains('Email Address')").should("be.visible"); - cy.get(commonlocators.editPropCrossButton).click(); + cy.get(commonlocators.editPropBackButton).click({ force: true }); }); it("Edit Row height and test table for changes", function() { cy.openPropertyPane("tablewidget"); @@ -200,7 +195,6 @@ describe("Table Widget property pane feature validation", function() { .click({ force: true }); cy.wait(1000); cy.readTabledataValidateCSS("0", "0", "height", "19px"); - cy.get(commonlocators.editPropCrossButton).click(); }); it("Test to validate text color and text background", function() { // Open property pane @@ -253,6 +247,5 @@ describe("Table Widget property pane feature validation", function() { "background", "rgb(128, 0, 128) none repeat scroll 0% 0% / auto padding-box border-box", ); - cy.get(commonlocators.editPropCrossButton).click(); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Widget_Add_button_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Widget_Add_button_spec.js index eae64c779a..a7a490633f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Widget_Add_button_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_Widget_Add_button_spec.js @@ -4,12 +4,10 @@ const publish = require("../../../../locators/publishWidgetspage.json"); const dsl = require("../../../../fixtures/tableNewDsl.json"); const pages = require("../../../../locators/Pages.json"); const testdata = require("../../../../fixtures/testdata.json"); - describe("Table Widget property pane feature validation", function() { before(() => { cy.addDsl(dsl); }); - it("Table widget with Add button test and validation", function() { cy.openPropertyPane("tablewidget"); // Open column details of "id". @@ -27,8 +25,9 @@ describe("Table Widget property pane feature validation", function() { .click(); cy.addSuccessMessage("Successful ".concat(testdata.currentRowEmail)); // Close Property pane - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - + cy.get(commonlocators.editPropBackButton).click({ + force: true, + }); // Validating the button action by clicking cy.get(widgetsPage.tableBtn) .last() @@ -45,7 +44,6 @@ describe("Table Widget property pane feature validation", function() { expect(someText).to.equal("Successful tobias.funke@reqres.in"); }); }); - it("Table Button color validation", function() { cy.openPropertyPane("tablewidget"); // Open column details of "id". @@ -58,10 +56,9 @@ describe("Table Widget property pane feature validation", function() { .clear() .type(color); // Close Property pane - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + //cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.get(widgetsPage.tableBtn).should("have.css", "background-color", color); }); - it("Table widget triggeredRow property should be accessible", function() { cy.get(commonlocators.TextInside).should("have.text", "Tobias Funke"); }); @@ -74,13 +71,13 @@ describe("Table Widget property pane feature validation", function() { cy.get(commonlocators.TextInside).should("have.text", "Tobias Funke"); }); it("Table widget add new icon button column", function() { - cy.openPropertyPane("tablewidget"); + cy.get(".t--property-pane-back-btn").click({ force: true }); // hide id column cy.makeColumnVisible("id"); cy.wait(1000); // click on Add new Column. + //cy.get(".t--property-pane-back-btn").click({ force: true }); cy.get(".t--add-column-btn").click(); - //Open New Custom Column cy.editColumn("customColumn1"); // Change Column type to icon Button @@ -109,15 +106,16 @@ describe("Table Widget property pane feature validation", function() { }); cy.deleteColumn("customColumn1"); // Close Property pane + /* cy.get(commonlocators.editPropCrossButton).click({ force: true, }); + */ }); it("Table widget add new menu button column", function() { cy.openPropertyPane("tablewidget"); // click on Add new Column. cy.get(".t--add-column-btn").click(); - //Open New Custom Column cy.editColumn("customColumn1"); // Change Column type to icon Button @@ -173,9 +171,11 @@ describe("Table Widget property pane feature validation", function() { force: true, }); // update menu item background color - cy.get(widgetsPage.backgroundcolorPickerNew).type("#FFC13D", { - force: true, - }); + cy.get(widgetsPage.backgroundcolorPickerNew) + .clear() + .type("#FFC13D", { + force: true, + }); // Go back to table property pane cy.get(".t--property-pane-back-btn").click({ force: true }); @@ -198,7 +198,6 @@ describe("Table Widget property pane feature validation", function() { // Close Property pane cy.openPropertyPane("tablewidget"); - // Click on the Menu Button cy.contains("Menu button").click({ force: true, @@ -206,25 +205,22 @@ describe("Table Widget property pane feature validation", function() { cy.wait(1000); // verify menu items background color - cy.contains("Menu Item 1").should( - "have.css", - "background-color", - "rgb(3, 179, 101)", - ); - cy.contains("Menu Item 2").should( - "have.css", - "background-color", - "rgb(255, 193, 61)", - ); - cy.contains("Menu Item 3").should( - "have.css", - "background-color", - "rgb(51, 102, 255)", - ); + cy.get(".bp3-menu-item") + .eq(0) + .should("have.css", "background-color", "rgb(3, 179, 101)"); + cy.get(".bp3-menu-item") + .eq(1) + .should("have.css", "background-color", "rgb(255, 193, 61)"); + cy.get(".bp3-menu-item") + .eq(2) + .should("have.css", "background-color", "rgb(51, 102, 255)"); + + //cy.closePropertyPane(); // disable menu item 3 - cy.openPropertyPane("tablewidget"); - cy.editColumn("customColumn1"); + //cy.openPropertyPane("tablewidget"); + + //cy.editColumn("customColumn1"); // Edit a Menu item cy.get(".t--property-pane-section-menuitems .t--edit-column-btn") .last() @@ -235,7 +231,7 @@ describe("Table Widget property pane feature validation", function() { cy.get(".t--property-control-disabled label.bp3-switch.unchecked").click({ force: true, }); - cy.closePropertyPane(); + //cy.closePropertyPane(); // Click on the Menu Button cy.contains("Menu button").click({ @@ -243,17 +239,19 @@ describe("Table Widget property pane feature validation", function() { }); cy.wait(1000); // check Menu Item 3 is disable - cy.contains("Menu Item 3").should( - "have.css", - "background-color", - "rgb(250, 250, 250)", - ); - cy.contains("Menu Item 3").should("have.class", "bp3-disabled"); + cy.get(".bp3-menu-item") + .eq(2) + .should("have.css", "background-color", "rgb(250, 250, 250)"); + cy.get(".bp3-menu-item") + .eq(2) + .should("have.class", "bp3-disabled"); // Click on the Menu Item - cy.contains("Menu Item 1").click({ - force: true, - }); + cy.get(".bp3-menu-item") + .eq(0) + .click({ + force: true, + }); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(3000); // Validating the toast message diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_spec.js index ab4cf3dd89..7bfaa1b086 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/Table_spec.js @@ -35,7 +35,6 @@ describe("Table Widget Functionality", function() { // .first() // .find("> .bp3-button-text") // .should("have.text", "{{navigateTo()}}"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Table Widget Functionality To Verify The Data", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicLayout/DynamicLayout_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicLayout/DynamicLayout_spec.js index 3b47bbf9bd..8a82359fe6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicLayout/DynamicLayout_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicLayout/DynamicLayout_spec.js @@ -8,13 +8,9 @@ const modalWidgetPage = require("../../../../locators/ModalWidget.json"); describe("Dynamic Layout Functionality", function() { it("Dynamic Layout - Change Layout", function() { - cy.get(commonlocators.canvas) - .invoke("width") - .should("be.gt", 1024); - cy.get(commonlocators.layoutControl).click(); - cy.get(commonlocators.layoutPopover) - .contains("Mobile Device") - .click({ force: true }); + cy.get(commonlocators.layoutControls) + .eq(4) + .click(); cy.get(commonlocators.canvas) .invoke("width") .should("be.eq", 450); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js index efeba1727c..56a92de28f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js @@ -46,7 +46,6 @@ describe("Entity explorer Drag and Drop widgets testcases", function() { cy.get(formWidgetsPage.formD) .scrollTo("bottom") .should("be.visible"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.get(explorer.explorerSwitchId).click(); cy.PublishtheApp(); cy.get(publish.backToEditor) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Pin_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Pin_spec.js new file mode 100644 index 0000000000..9166f014d0 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Pin_spec.js @@ -0,0 +1,22 @@ +const dsl = require("../../../../fixtures/displayWidgetDsl.json"); + +describe("Entity explorer tests related to pinning and unpinning", function() { + beforeEach(() => { + cy.addDsl(dsl); + }); + + it("checks entity explorer visibility on unpin", function() { + cy.get(".t--unpin-entity-explorer").click(); + + // after transition, the entity explorer will not be visible + cy.get("body").trigger("mousemove", { which: 1, pageX: 600, pageY: 600 }); + cy.get(".t--entity-explorer").should("not.be.visible"); + }); + + it("checks entity explorer visibility on pin", function() { + cy.get(".t--unpin-entity-explorer").click(); + cy.get(".t--pin-entity-explorer").click(); + + cy.get(".t--entity-explorer").should("be.visible"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Tab_rename_Delete_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Tab_rename_Delete_spec.js index 1b0841806c..9439b34f40 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Tab_rename_Delete_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Tab_rename_Delete_spec.js @@ -13,7 +13,7 @@ describe("Tab widget test", function() { }); it("Tab Widget Functionality To rename Tabs from entity explorer", function() { - cy.GlobalSearchEntity("Tab 1"); + cy.GlobalSearchEntity("Tab1"); cy.RenameEntity(tabname); }); @@ -27,15 +27,15 @@ describe("Tab widget test", function() { }); it("Tab Widget Functionality To delete Tabs from entity explorer", function() { - cy.GlobalSearchEntity("Tab 2"); + cy.GlobalSearchEntity("Tab2"); cy.RenameEntity(tabname); cy.validateMessage(tabname); cy.deleteEntity(); cy.get(commonlocators.entityExplorersearch) .clear({ force: true }) - .type("Tab 2", { force: true }); + .type("Tab2", { force: true }); cy.get( - commonlocators.entitySearchResult.concat("Tab 2").concat("')"), + commonlocators.entitySearchResult.concat("Tab2").concat("')"), ).should("not.exist"); }); @@ -51,7 +51,7 @@ describe("Tab widget test", function() { .click({ force: true }) .should("be.selected"); }); - + it("Tab Widget Functionality To Unchecked Visible Widget", function() { cy.get(publish.backToEditor).click(); cy.openPropertyPane("tabswidget"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js index 4c6cfaad8c..becbdea57a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js @@ -28,7 +28,6 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { cy.get(commonlocators.toastBody) .first() .contains("Copied"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); it("Delete Widget from sidebar and Undo action validation", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/Widget_Popup_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/Widget_Popup_spec.js new file mode 100644 index 0000000000..84a31770a5 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/Widget_Popup_spec.js @@ -0,0 +1,81 @@ +const formWidgetsPage = require("../../../../locators/FormWidgets.json"); +const widgetLocators = require("../../../../locators/Widgets.json"); +const dsl = require("../../../../fixtures/widgetPopupDsl.json"); + +describe("Dropdown Widget Functionality", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Verify dropdown width of Select widgets and menu button", function() { + // Select + cy.get(formWidgetsPage.dropdownWidget) + .find(widgetLocators.dropdownSingleSelect) + .invoke("outerWidth") + .should("eq", 147.1875); + cy.get(formWidgetsPage.dropdownWidget) + .find(widgetLocators.dropdownSingleSelect) + .click({ + force: true, + }); + cy.get(".select-popover-wrapper") + .invoke("outerWidth") + .should("eq", 147.1875); + + // Menu Button + cy.get(formWidgetsPage.menuButtonWidget) + .find(widgetLocators.menuButton) + .invoke("outerWidth") + .should("eq", 147.1875); + cy.get(formWidgetsPage.menuButtonWidget) + .find(widgetLocators.menuButton) + .click({ + force: true, + }); + cy.get(".menu-button-popover") + .invoke("outerWidth") + .should("eq", 147.1875); + + // MultiSelect + cy.get(formWidgetsPage.multiselectWidget) + .find(".rc-select-multiple") + .invoke("width") + .should("eq", 147.1875); + cy.get(formWidgetsPage.multiselectWidget) + .find(".rc-select-selection-search-input") + .first() + .focus({ force: true }) + .type("{uparrow}", { force: true }); + cy.get(".multi-select-dropdown") + .invoke("width") + .should("eq", 147.1875); + + //Multi tree Select + cy.get(formWidgetsPage.multiselecttreeWidget) + .find(".rc-tree-select-multiple") + .invoke("width") + .should("eq", 147.1875); + cy.get(formWidgetsPage.multiselecttreeWidget) + .find(".rc-tree-select-selection-search-input") + .first() + .focus({ force: true }) + .type("{uparrow}", { force: true }); + cy.get(".tree-multiselect-dropdown") + .invoke("outerWidth") + .should("eq", 147.1875); + + // Tree Select + cy.get(formWidgetsPage.singleselecttreeWidget) + .find(".rc-tree-select-single") + .invoke("outerWidth") + .should("eq", 147.1875); + cy.get(formWidgetsPage.singleselecttreeWidget) + .find(".rc-tree-select-selection-search-input") + .first() + .focus({ force: true }) + .type("{uparrow}", { force: true }); + cy.get(".single-tree-select-dropdown") + .invoke("outerWidth") + .should("eq", 147.1875); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Container_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Container_spec.js index fb4001c63a..7020d5fabe 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Container_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Container_spec.js @@ -50,7 +50,6 @@ describe("Container Widget Functionality", function() { .eq(0) .scrollIntoView({ easing: "linear" }) .should("be.visible"); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.PublishtheApp(); }); it("Container Widget Functionality To Verify The Colour", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js index 8078a2bf79..4955dd8717 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js @@ -43,13 +43,10 @@ describe("Tab widget test", function() { /** * @param{toggleButton Css} Assert to be checked */ - cy.togglebar(widgetsPage.Scrollbutton) - .check({ force: true }) - .should("be.checked"); + cy.togglebar(widgetsPage.Scrollbutton); cy.get(Layoutpage.tabContainer) .scrollIntoView({ easing: "linear" }) .should("be.visible"); - cy.get(commonlocators.crossbutton).click({ force: true }); cy.PublishtheApp(); }); it("Tab Widget Functionality To Select Tabs", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/FirstTimeUserOnboarding_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/FirstTimeUserOnboarding_spec.js index 99b1c1d526..df363aedfb 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/FirstTimeUserOnboarding_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/FirstTimeUserOnboarding_spec.js @@ -161,13 +161,14 @@ describe("FirstTimeUserOnboarding", function() { it("onboarding flow - should check directly opening widget pane", function() { cy.get(OnboardingLocator.introModalBuild).click(); - cy.get(OnboardingLocator.taskDatasourceBtn).should("be.visible"); cy.get(OnboardingLocator.widgetPaneTrigger).click(); cy.get(OnboardingLocator.widgetSidebar).should("be.visible"); cy.get(OnboardingLocator.dropTarget).should("be.visible"); cy.dragAndDropToCanvas("textwidget", { x: 400, y: 400 }); - cy.get(OnboardingLocator.textWidgetName).should("be.visible"); + cy.get(OnboardingLocator.textWidgetName) + .should("be.visible") + .wait(800); cy.reload(); cy.wait("@getUser"); cy.get(OnboardingLocator.statusbar).should("be.visible"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/PreviewMode_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/PreviewMode_spec.js new file mode 100644 index 0000000000..9161d94201 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/PreviewMode_spec.js @@ -0,0 +1,29 @@ +const dsl = require("../../../../fixtures/previewMode.json"); + +describe("Preview mode functionality", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("checks entity explorer and property pane visiblity", function() { + cy.get(".t--switch-preview-mode-toggle").click(); + + // in preview mode, entity explorer and property pane are not visible + cy.get(".t--entity-explorer").should("not.be.visible"); + cy.get(".t--property-pane-sidebar").should("not.be.visible"); + }); + + it("checks if widgets can be selected or not", function() { + // in preview mode, entity explorer and property pane are not visible + const selector = `.t--draggable-buttonwidget`; + cy.wait(500); + cy.get(selector) + .first() + .trigger("mouseover", { force: true }) + .wait(500); + + cy.get( + `${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`, + ).should("not.exist"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js deleted file mode 100644 index d00d7782fa..0000000000 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -const commonlocators = require("../../../../locators/commonlocators.json"); -const formWidgetsPage = require("../../../../locators/FormWidgets.json"); -const widgetsPage = require("../../../../locators/Widgets.json"); -const publish = require("../../../../locators/publishWidgetspage.json"); -const dsl = require("../../../../fixtures/newFormDsl.json"); -const formWidgetDsl = require("../../../../fixtures/formWidgetdsl.json"); -const pages = require("../../../../locators/Pages.json"); -const widgetList = [ - "inputwidget", - "dropdownwidget", - "buttonwidget", - "checkboxwidget", - "datepickerwidget", - "richtexteditorwidget", - "filepickerwidget", - "switchwidget", - "containerwidget", -]; - -//const widgetList = ["radiowidget"]; -//const widgetList = ["inputwidget"]; -describe("Widget property pane draggable", function() { - before(() => { - cy.addDsl(dsl); - }); - it("Property pane initial position same untill it is dragged", function() { - for (const testWidget of widgetList) { - cy.openPropertyPane(testWidget); - cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { - const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); - cy.get(commonlocators.collapsesection) - .first() - .click(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - cy.openPropertyPane(testWidget); - cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { - const newPropPanePosition = newPropPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); - expect(oldPropPanePosition.left).to.be.equal( - newPropPanePosition.left, - ); - }); - }); - } - }); - it("Property pane position should stay same after dragging down", () => { - for (const testWidget of widgetList) { - cy.openPropertyPane(testWidget); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: 0, clientY: 0 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { - const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - cy.openPropertyPane("containerwidget"); - cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { - const newPropPanePosition = newPropPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); - expect(oldPropPanePosition.left).to.be.equal( - newPropPanePosition.left, - ); - }); - }); - } - }); - - it("Property pane should come back into view if forced to drop out of view", () => { - for (const testWidget of widgetList) { - cy.openPropertyPane(testWidget); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: -10, clientY: -20 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { - const propPanePosition = porpPane[0].getBoundingClientRect(); - expect(propPanePosition.top).to.be.greaterThan(0); - expect(propPanePosition.left).to.be.gte(0); - }); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: 1600, clientY: 800 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { - const propPanePosition = porpPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(propPanePosition.top).to.be.lessThan( - Cypress.config().viewportHeight - propPanePosition.height, - ); - expect(propPanePosition.left).to.be.lessThan( - Cypress.config().viewportWidth - propPanePosition.width, - ); - }); - } - }); -}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js deleted file mode 100644 index 315d92a0ba..0000000000 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -const commonlocators = require("../../../../locators/commonlocators.json"); -const dsl = require("../../../../fixtures/TextTabledsl.json"); - -describe("Table Widget property pane feature validation", function() { - before(() => { - cy.addDsl(dsl); - }); - - it("Property pane initial position same untill it is dragged", function() { - cy.openPropertyPane("tablewidget"); - cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { - const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); - cy.get(commonlocators.collapsesection) - .first() - .click(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - cy.openPropertyPane("tablewidget"); - cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { - const newPropPanePosition = newPropPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); - expect(oldPropPanePosition.left).to.be.equal(newPropPanePosition.left); - }); - }); - }); - - it("Property pane position should stay same after dragging down", () => { - cy.openPropertyPane("tablewidget"); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: 400, clientY: 500 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { - const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - cy.openPropertyPane("containerwidget"); - cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { - const newPropPanePosition = newPropPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); - expect(oldPropPanePosition.left).to.be.equal(newPropPanePosition.left); - }); - }); - }); - - it("Property pane should come back into view if forced to drop out of view", () => { - cy.openPropertyPane("tablewidget"); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: -10, clientY: -20 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { - const propPanePosition = porpPane[0].getBoundingClientRect(); - expect(propPanePosition.top).to.be.greaterThan(0); - expect(propPanePosition.left).to.be.gte(0); - }); - cy.get("[data-cy=t--property-pane-drag-handle]") - .trigger("mousedown", { which: 1 }) - .trigger("mousemove", { clientX: 1600, clientY: 800 }) - .trigger("mouseup", { force: true }); - cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { - const propPanePosition = porpPane[0].getBoundingClientRect(); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); - expect(propPanePosition.top).to.be.lessThan( - Cypress.config().viewportHeight - propPanePosition.height, - ); - expect(propPanePosition.left).to.be.lessThan( - Cypress.config().viewportWidth - propPanePosition.width, - ); - }); - }); -}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiPaneTests/Api_Marketplace_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiPaneTests/Api_Marketplace_spec.js deleted file mode 100644 index e3a0dc1d6b..0000000000 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiPaneTests/Api_Marketplace_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Commenting because CE does not have marketplace anymore - * - * */ -// /// -// const testdata = require("../../../../fixtures/testdata.json"); -// const apiwidget = require("../../../locators/apiWidgetslocator.json"); -// describe("API Panel Test Functionality ", function() { -// it("Test Market place API by adding to a page", function() { -// cy.log("Login Successful"); -// cy.NavigateToAPI_Panel(); -// cy.wait("@getCategories"); -// cy.wait("@getTemplateCollections"); -// cy.wait("@get3PProviders"); -// cy.log("Navigation to API Panel screen successful"); -// cy.get(apiwidget.marketPlaceapi) -// .first() -// .click(); -// cy.wait("@get3PProviderTemplates"); -// cy.get(".apiName") -// .first() -// .invoke("text") -// .then(ApiName => { -// cy.get(apiwidget.addPageButton) -// .first() -// .click(); -// const searchApiName = ApiName.replace(/\s/g, ""); -// cy.log(searchApiName); -// cy.wait("@getActions"); -// cy.SearchAPIandClick(searchApiName); -// cy.get(apiwidget.apidocumentaionLink) -// .invoke("text") -// .then(apidocumentation => { -// cy.log(apidocumentation); -// expect(apidocumentation).to.eq("API documentation"); -// }); -// }); -// }); -// }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js index 6e95fad727..bd01306c1a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js @@ -62,7 +62,6 @@ describe("Addwidget from Query and bind with other widgets", function() { it("Input widget test with default value from table widget", () => { cy.SearchEntityandOpen("Input1"); cy.get(widgetsPage.defaultInput).type(testdata.addInputWidgetBinding); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.wait("@updateLayout").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js index 2a554c96b0..286088a1ad 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js @@ -3,7 +3,7 @@ const queryEditor = require("../../../../locators/QueryEditor.json"); let datasourceName; -describe("Add widget", function() { +describe("Add widget - Postgress DataSource", function() { beforeEach(() => { cy.startRoutesForDatasource(); cy.createPostgresDatasource(); @@ -12,7 +12,7 @@ describe("Add widget", function() { }); }); - it("Add widget", () => { + it("1. Verify 'Add to widget [Widget Suggestion]' functionality - Postgress", () => { cy.NavigateToQueryEditor(); cy.contains(".t--datasource-name", datasourceName) .find(queryLocators.createQuery) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/MongoDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js similarity index 55% rename from app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/MongoDatasource_spec.js rename to app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js index 6a22acc142..e813f868f5 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/MongoDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js @@ -1,7 +1,6 @@ const queryLocators = require("../../../../locators/QueryEditor.json"); const generatePage = require("../../../../locators/GeneratePage.json"); const datasource = require("../../../../locators/DatasourcesEditor.json"); -const queryEditor = require("../../../../locators/QueryEditor.json"); let datasourceName; @@ -22,7 +21,7 @@ describe("Create a query with a mongo datasource, run, save and then delete the }); }); - it("2. Validate Raw query command, run, save and then delete the query", function() { + it("2. Validate Raw query command, run and then delete the query", function() { cy.NavigateToActiveDSQueryPane(datasourceName); // cy.get("@getPluginForm").should( @@ -31,52 +30,46 @@ describe("Create a query with a mongo datasource, run, save and then delete the // 200, // ); - cy.xpath('//div[contains(text(),"Find Document(s)")]').click({ - force: true, - }); - cy.xpath('//div[contains(text(),"Raw")]').click({ force: true }); - cy.get(queryLocators.templateMenu).click(); - cy.get(".CodeMirror textarea") - .first() - .focus() - .type(`{"find": "listingsAndReviews","limit": 10}`, { - parseSpecialCharSequences: false, - }); + cy.validateNSelectDropdown("Commands", "Find Document(s)", "Raw"); + + cy.get(queryLocators.templateMenu).click(); + cy.typeValueNValidate('{"find": "listingsAndReviews","limit": 10}'); + + // cy.get(".CodeMirror textarea") + // .first() + // .focus() + // .type(`{"find": "listingsAndReviews","limit": 10}`, { + // parseSpecialCharSequences: false, + // }); + // cy.EvaluateCurrentValue(`{"find": "listingsAndReviews","limit": 10}`); - cy.EvaluateCurrentValue(`{"find": "listingsAndReviews","limit": 10}`); cy.runAndDeleteQuery(); }); it("3. Validate Find documents command & Run and then delete the query", function() { - //datasourceName = 'Mongo CRUD ds 09e54713' cy.NavigateToActiveDSQueryPane(datasourceName); //cy.xpath(queryLocators.findDocs).should("exist"); //Verifying update is success or below line - cy.expect(queryLocators.findDocs).to.exist; + //cy.expect(queryLocators.findDocs).to.exist; - cy.xpath(queryLocators.collectionField).type("listingsAndReviews"); - cy.EvaluateCurrentValue("listingsAndReviews"); + cy.validateNSelectDropdown("Commands", "Find Document(s)"); + + cy.typeValueNValidate("listingsAndReviews", "Collection"); cy.runQuery(); //exeute actions - 200 response is verified in this method cy.xpath(queryLocators.countText).should("have.text", "10 Records"); - cy.xpath(queryLocators.queryField).type(`{{}beds : {{}$lte: 2}}`); - cy.EvaluateCurrentValue("{beds : {$lte: 2}}"); + cy.typeValueNValidate("{beds : {$lte: 2}}", "Query"); cy.runQuery(); //exeute actions - 200 response is verified in this method cy.xpath(queryLocators.countText).should("have.text", "10 Records"); - cy.xpath(queryLocators.sortField).type("{{}number_of_reviews: -1}"); //sort descending - cy.EvaluateCurrentValue("{number_of_reviews: -1}"); + cy.typeValueNValidate("{number_of_reviews: -1}", "Sort"); //sort descending cy.runQuery(); //exeute actions - 200 response is verified in this method cy.xpath(queryLocators.countText).should("have.text", "10 Records"); - cy.xpath(queryLocators.projectionField).type( - "{{}house_rules: 1, description:1}", - ); //Projection field - cy.EvaluateCurrentValue("{house_rules: 1, description:1}"); + cy.typeValueNValidate("{house_rules: 1, description:1}", "Projection"); //Projection field cy.runQuery(); //exeute actions - 200 response is verified in this method - cy.xpath(queryLocators.limitField).type("5"); //Projection field - cy.EvaluateCurrentValue("5"); + cy.typeValueNValidate("5", "Limit"); //Limit field cy.onlyQueryRun(); cy.wait("@postExecute").then(({ response }) => { expect(response.body.data.body[0].house_rules).to.contains( @@ -84,10 +77,9 @@ describe("Create a query with a mongo datasource, run, save and then delete the "Response is not as expected for Aggregate commmand", ); }); - cy.xpath(queryLocators.countText).should("have.text", "5 Records"); - cy.xpath(queryLocators.skipField).type("2"); //Skip field - cy.EvaluateCurrentValue("2"); + + cy.typeValueNValidate("2", "Skip"); //Skip field cy.onlyQueryRun(); cy.wait("@postExecute").then(({ response }) => { @@ -103,18 +95,10 @@ describe("Create a query with a mongo datasource, run, save and then delete the it("4. Validate Count command & Run and then delete the query", function() { cy.NavigateToActiveDSQueryPane(datasourceName); - - cy.xpath('//div[contains(text(),"Find Document(s)")]').click({ - force: true, - }); - cy.xpath('//div[contains(text(),"Count")]').click({ force: true }); - - cy.xpath(queryLocators.collectionField).type("listingsAndReviews"); - cy.EvaluateCurrentValue("listingsAndReviews"); + cy.validateNSelectDropdown("Commands", "Find Document(s)", "Count"); + cy.typeValueNValidate("listingsAndReviews", "Collection"); cy.runQuery(); - - cy.xpath(queryLocators.queryField).type(`{{}beds : {{}$lte: 2}}`); - cy.EvaluateCurrentValue("{beds : {$lte: 2}}"); + cy.typeValueNValidate("{beds : {$lte: 2}}", "Query"); cy.runQuery(); //exeute actions - 200 response is verified in this method cy.deleteQueryUsingContext(); @@ -123,21 +107,10 @@ describe("Create a query with a mongo datasource, run, save and then delete the it("5. Validate Distinct command & Run and then delete the query", function() { cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath('//div[contains(text(),"Find Document(s)")]').click({ - force: true, - }); - cy.xpath('//div[contains(text(),"Distinct")]').click({ force: true }); - - cy.xpath(queryLocators.collectionField).type("listingsAndReviews"); - cy.EvaluateCurrentValue("listingsAndReviews"); - - cy.xpath(queryLocators.queryField).type(`{{}beds : {{}$lte: 2}}`); - cy.EvaluateCurrentValue("{beds : {$lte: 2}}"); - - cy.xpath(queryLocators.keyField).type(`property_type`); - cy.EvaluateCurrentValue("property_type"); - //cy.runQuery(); //exeute actions - 200 response is verified in this method - + cy.validateNSelectDropdown("Commands", "Find Document(s)", "Distinct"); + cy.typeValueNValidate("listingsAndReviews", "Collection"); + cy.typeValueNValidate("{beds : {$lte: 2}}", "Query"); + cy.typeValueNValidate("property_type", "Key"); cy.onlyQueryRun(); cy.wait("@postExecute").then(({ request, response }) => { expect(response.body.data.body.values[0]).to.eq( @@ -151,22 +124,12 @@ describe("Create a query with a mongo datasource, run, save and then delete the it("6. Validate Aggregate command & Run and then delete the query", function() { cy.NavigateToActiveDSQueryPane(datasourceName); - - cy.xpath('//div[contains(text(),"Find Document(s)")]').click({ - force: true, - }); - cy.xpath('//div[contains(text(),"Aggregate")]').click({ force: true }); - - cy.xpath(queryLocators.collectionField).type("listingsAndReviews"); - cy.EvaluateCurrentValue("listingsAndReviews"); - - cy.xpath(queryLocators.arrayOfPipelinesField).type( - `[{{} $project: {{} count: {{} $size:"$amenities" }}}]`, + cy.validateNSelectDropdown("Commands", "Find Document(s)", "Aggregate"); + cy.typeValueNValidate("listingsAndReviews", "Collection"); + cy.typeValueNValidate( + '[{ $project: { count: { $size:"$amenities" }}}]', + "Array of Pipelines", ); - cy.EvaluateCurrentValue('[{ $project: { count: { $size:"$amenities" }}}]'); - - //cy.runQuery(); //exeute actions - 200 response is verified in this method - cy.onlyQueryRun(); cy.wait("@postExecute").then(({ request, response }) => { // cy.log(request.method + ": is req.method") @@ -224,13 +187,86 @@ describe("Create a query with a mongo datasource, run, save and then delete the 409, ); - cy.xpath(generatePage.mongonewPageEntityMenu) - .first() - .click({ force: true }); - cy.xpath(generatePage.deleteMenuItem).click(); + cy.deleteEntitybyName("ListingsAndReviews"); }); - it("9. Delete the datasource after NewPage deletion is success", () => { + it("9. Bug 7399: Validate Form based & Raw command based templates", function() { + let id; + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "Find Document(s)"); + cy.xpath(queryLocators.mongoFormFind).click({ force: true }); + cy.xpath("//div[text()='Find']") + .click() + .wait(100); //wait for Find form to open + cy.EvaluatFieldValue("Collection").then((colData) => { + colData = colData.replace("{", "").replace("}", ""); + cy.log("Collection value is fieldData: " + colData); + cy.wrap(colData).as("colData"); + }); + cy.EvaluatFieldValue("Query").then((queryData) => { + queryData = queryData.replace("{", "").replace("}", ""); + id = queryData; + cy.log("Query value is : " + queryData); + cy.wrap(queryData).as("queryData"); + }); + cy.EvaluatFieldValue("Sort").then((sortData) => { + sortData = sortData.replace("{", "").replace("}", ""); + cy.log("Sort value is : " + sortData); + cy.wrap(sortData).as("sortData"); + }); + cy.EvaluatFieldValue("Limit").then((limitData) => { + limitData = limitData.replace("{", "").replace("}", ""); + cy.log("Limit value is : " + limitData); + cy.wrap(limitData).as("limitData"); + }); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0]._id).to.eq( + id + .split(":")[1] + .trim() + .replace(/['"]+/g, ""), + ); + }); + + cy.validateNSelectDropdown("Commands", "Find Document(s)", "Raw"); + cy.EvaluatFieldValue().then((rawData) => { + rawData = rawData.replace("{", "").replace("}", ""); + cy.log("rawData value is : " + rawData); + cy.wrap(rawData).as("rawData"); + }); + + cy.all( + cy.get("@colData"), + cy.get("@queryData"), + cy.get("@sortData"), + cy.get("@limitData"), + cy.get("@rawData"), + ).then((values) => { + expect(values[4].trim()).to.contain(values[0].trim()); + expect(values[4].trim()).to.contain(values[1].trim()); + expect(values[4].trim()).to.contain(values[2].trim()); + expect(values[4].trim()).to.contain(values[3].trim()); + }); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0]._id).to.eq( + id + .split(":")[1] + .trim() + .replace(/['"]+/g, ""), + ); + }); + + cy.deleteQueryUsingContext(); + cy.deleteEntitybyName("Query1"); + }); + + it("10. Delete the datasource after NewPage deletion is success", () => { cy.NavigateToQueryEditor(); cy.NavigateToActiveTab(); cy.contains(".t--datasource-name", datasourceName).click(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/PostgresCRUDOps_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js similarity index 82% rename from app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/PostgresCRUDOps_spec.js rename to app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js index b212b7b42a..c51aa941f6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/PostgresCRUDOps_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js @@ -30,19 +30,13 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", it("2. Create & runs existing table data and deletes the query", () => { cy.NavigateToActiveDSQueryPane(datasourceName); - cy.get(queryLocators.templateMenu).click(); - cy.get(".CodeMirror textarea") - .first() - .focus() - .type("select * from users limit 10"); - - cy.EvaluateCurrentValue("select * from users limit 10"); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate("select * from users limit 10"); cy.runAndDeleteQuery(); }); it("3. Create new CRUD Table and populate", () => { cy.NavigateToActiveDSQueryPane(datasourceName); - cy.get(queryLocators.templateMenu).click(); let tableCreateQuery = `CREATE TABLE public.users_crud ( id integer NOT NULL, @@ -53,7 +47,7 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", address text, role text); - insert into public.users_crud (id, name, status, gender, email, address, role) values + insert into public.users_crud (id, name, status, gender, email, address, role) values (1, 'CRUD User1', 'PENDING', 'Male', 'cruduser1@ihg.com', '19624 Scofield Way', 'User'), (2, 'CRUD User2', 'IN PROGRESS', 'Male','cruduser2@ihg.com', '19624 Scofield Way', 'Editor'), (3, 'CRUD User3', 'APPROVED', 'Female','cruduser3@ihg.com', '19624 Scofield Way', 'Admin'), @@ -85,20 +79,25 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", (29, 'CRUD User29', 'IN PROGRESS', 'Male','cruduser29@ihg.com', '19624 Scofield Way', 'Editor'), (30, 'CRUD User30', 'APPROVED', 'Female','cruduser30@ihg.com', '19624 Scofield Way', 'Admin');`; + cy.get(queryLocators.templateMenu).click({ force: true }); + //cy.typeValueNValidate(tableCreateQuery);//Since type method is slow for such big text - using paste! + cy.get(".CodeMirror textarea").paste(tableCreateQuery); cy.get(".CodeMirror textarea").focus(); cy.EvaluateCurrentValue(tableCreateQuery); + cy.runAndDeleteQuery(); //exeute actions - 200 response is verified in this method }); it("4. Validate Select record from Postgress datasource", () => { let selectQuery = "select * from public.users_crud"; cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath(queryLocators.querySelect).click(); - cy.get(queryLocators.codeTextArea).type("{cmd+a}{del}"); - cy.get(queryLocators.codeTextArea).paste(selectQuery); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate(selectQuery); + + // cy.xpath(queryLocators.codeTextArea).paste(selectQuery); + //cy.EvaluateCurrentValue(selectQuery); - cy.EvaluateCurrentValue(selectQuery); cy.runAndDeleteQuery(); //exeute actions - 200 response is verified in this method }); @@ -106,12 +105,8 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", let insertQuery = "INSERT INTO public.users_crud (id, name, gender, email) VALUES (31, 'CRUD User11','Male','cruduser31@ihg.com');"; cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath(queryLocators.queryCreate).click(); - cy.get(queryLocators.codeTextArea).type("{cmd+a}{del}"); - //.type("{selectall}{del}"); - - cy.get(queryLocators.codeTextArea).paste(insertQuery); - cy.EvaluateCurrentValue(insertQuery); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate(insertQuery); cy.runAndDeleteQuery(); }); @@ -119,22 +114,16 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", let updateQuery = "UPDATE public.users_crud SET status = 'PENDING', role = 'Viewer' WHERE id = 31;"; cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath(queryLocators.queryUpdate).click(); - cy.get(queryLocators.codeTextArea).type("{cmd+a}{del}"); - cy.get(queryLocators.codeTextArea).paste(updateQuery); - - cy.EvaluateCurrentValue(updateQuery); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate(updateQuery); cy.runAndDeleteQuery(); }); it("7. Validate Delete record from Postgress datasource", () => { let deleteQuery = "DELETE FROM public.users_crud WHERE id = 31;"; cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath(queryLocators.queryDelete).click(); - cy.get(queryLocators.codeTextArea).type("{cmd+a}{del}"); - cy.get(queryLocators.codeTextArea).paste(deleteQuery); - - cy.EvaluateCurrentValue(deleteQuery); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate(deleteQuery); cy.runAndDeleteQuery(); }); @@ -167,8 +156,12 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", cy.ClickGotIt(); //Verifying Update from UI - cy.xpath(generatePage.selectRowinTable).click(); + cy.xpath(generatePage.selectRowinTable) + .scrollIntoView() + .should("be.visible") + .click({ force: true }); cy.xpath(generatePage.currentStatusField) + .scrollIntoView() .clear() .click() .type("APPROVED"); @@ -185,7 +178,10 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", //.should("have.nested.property", "response.body.data.request.requestParams.Query.value",); - cy.xpath(generatePage.selectRowinTable).click(); + cy.xpath(generatePage.selectRowinTable) + .scrollIntoView() + .should("be.visible") + .click({ force: true }); cy.xpath(generatePage.currentStatusField).should("have.value", "APPROVED"); //Verifying update is success //verifying Insert from UI @@ -197,14 +193,19 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", cy.xpath(generatePage.emailField) .type("curduser31@ihg.com") .wait(1000); //Waiting for Submit button to get enabled - cy.get(generatePage.submitBtn).click(); + cy.get(generatePage.submitBtn) + .first() + .click(); cy.xpath(generatePage.sortByDropdown).click(); //Sorting by descending to verify newly added record - also sorting is verified cy.xpath(generatePage.descending).click(); cy.xpath(generatePage.currentNameField).should("have.value", "CRUD User31"); //Verifying Addition is success //Verifying Delete from UI - cy.xpath(generatePage.deleteofSelectedRow).click(); + cy.xpath(generatePage.deleteofSelectedRow) + .scrollIntoView() + .should("be.visible") + .click({ force: true }); cy.get(generatePage.confirmBtn) .click() .wait(2000); //Wait for update call to be success @@ -215,7 +216,9 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", 200, ); - cy.xpath(generatePage.currentNameField).should("have.value", "CRUD User30"); //Verifying Deletion of id # 31 is success + cy.xpath(generatePage.currentNameField) + .scrollIntoView() + .should("have.value", "CRUD User30"); //Verifying Deletion of id # 31 is success }); it("9. Validate Deletion of the Newly Created Page", () => { @@ -229,30 +232,22 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", "response.body.responseMeta.status", 409, ); - - cy.xpath(generatePage.postgressnewPageEntityMenu) - .first() - .click({ force: true }); - cy.xpath(generatePage.deleteMenuItem).click(); + cy.deleteEntitybyName("Public.users_crud"); }); it("10. Validate Drop of the Newly Created Table from Postgress datasource", () => { let deleteTblQuery = "DROP TABLE public.users_crud;"; cy.NavigateToActiveDSQueryPane(datasourceName); - cy.xpath(queryLocators.queryDelete).click(); - cy.get(queryLocators.codeTextArea).type("{cmd+a}{del}"); - - cy.get(queryLocators.codeTextArea).paste(deleteTblQuery); - - cy.EvaluateCurrentValue(deleteTblQuery); + cy.get(queryLocators.templateMenu).click({ force: true }); + cy.typeValueNValidate(deleteTblQuery); cy.runAndDeleteQuery(); }); it("11. Deletes the datasource", () => { cy.NavigateToQueryEditor(); cy.NavigateToActiveTab(); - cy.contains(".t--datasource-name", datasourceName).click(); - cy.get(".t--delete-datasource").click(); + cy.contains(".t--datasource-name", datasourceName).click({ force: true }); + cy.get(".t--delete-datasource").click({ force: true }); cy.wait("@deleteDatasource").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/S3_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/S3_spec.js new file mode 100644 index 0000000000..f7b54887b0 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/S3_spec.js @@ -0,0 +1,505 @@ +const queryLocators = require("../../../../locators/QueryEditor.json"); +const datasource = require("../../../../locators/DatasourcesEditor.json"); +const generatePage = require("../../../../locators/GeneratePage.json"); +const dsl = require("../../../../fixtures/snippingTableDsl.json"); + +let datasourceName; + +describe("Validate CRUD queries for Amazon S3 along with UI flow verifications", function() { + beforeEach(() => { + cy.startRoutesForDatasource(); + }); + + it("1. Creates a new Amazon S3 datasource", function() { + cy.NavigateToDatasourceEditor(); + cy.get(datasource.AmazonS3) + .click({ force: true }) + .wait(1000); + + cy.generateUUID().then((uid) => { + datasourceName = `Amazon S3 CRUD ds ${uid}`; + cy.renameDatasource(datasourceName); + cy.wrap(datasourceName).as("dSName"); + }); + + cy.fillAmazonS3DatasourceForm(); + cy.testSaveDatasource(); + }); + + it("2. Validate List Files in bucket (all existing files) command, run and then delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").should(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Mandatory parameter 'Bucket Name' is missing.", + ); + }); + cy.typeValueNValidate("AutoTest", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body.split("(")[0].trim()).to.be.oneOf([ + "The specified bucket does not exist", + "The specified bucket is not valid.", + ]); + }); + + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.runAndDeleteQuery(); //exeute actions - 200 response is verified in this method + }); + + it("3. Validate Create a new file in bucket command, Verify possible error msgs, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown( + "Commands", + "List files in bucket", + "Create a new file", + ); + + cy.onlyQueryRun(); + cy.wait("@postExecute").should(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Mandatory parameter 'Bucket Name' is missing.", + ); + }); + cy.typeValueNValidate("AutoTest", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Required parameter 'File Path' is missing.", + ); + }); + cy.typeValueNValidate("AutoFile", "File Path"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.eq( + "Unable to parse content. Expected to receive an object with `data` and `type`", + ); + }); + + cy.typeValueNValidate("Hi", "Content"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.eq( + "Unable to parse content. Expected to receive an object with `data` and `type`", + ); + }); + + cy.typeValueNValidate( + '{"data": "Hi, this is Automation script adding File!"}', + "Content", + ); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "File content is not base64 encoded.", + ); + }); + cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + //expect(['The specified bucket does not exist', 'The specified bucket is not valid.']).to.include(response.body.data.body) + expect(response.body.data.body.split("(")[0].trim()).to.be.oneOf([ + "The specified bucket does not exist", + "The specified bucket is not valid.", + ]); + }); + + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + }); + + cy.deleteQueryUsingContext(); + }); + + it("4. Validate Read file command, Verify possible error msgs, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket", "Read file"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").should(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Mandatory parameter 'Bucket Name' is missing.", + ); + }); + cy.typeValueNValidate("AutoTest", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Required parameter 'File Path' is missing.", + ); + }); + cy.typeValueNValidate("Auto", "File Path"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body.split("(")[0].trim()).to.be.oneOf([ + "The specified bucket does not exist", + "The specified bucket is not valid.", + ]); + }); + + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contain( + "The specified key does not exist.", + ); + }); + + cy.typeValueNValidate("Autofile", "File Path"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contain( + "The specified key does not exist.", + ); + }); + + cy.typeValueNValidate("AutoFile", "File Path"); + cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body.fileData).to.not.eq( + "Hi, this is Automation script adding File!", + ); + }); + + cy.validateNSelectDropdown("Base64 Encode File - Yes/No", "Yes", "No"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body.fileData).to.eq( + "Hi, this is Automation script adding File!", + ); + }); + + cy.deleteQueryUsingContext(); + }); + + it("5. Validate List Files in bucket command for new file, Verify possible error msgs, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").should(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Mandatory parameter 'Bucket Name' is missing.", + ); + }); + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + + cy.typeValueNValidate("Auto", "Prefix"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0].fileName).to.contains("Auto"); + expect(response.body.data.body[0].url).to.exist; + }); + + cy.typeValueNValidate("AutoFile", "Prefix"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0].fileName).to.contains("Auto"); + expect(response.body.data.body[0].url).to.exist; + expect(response.body.data.body[0].signedUrl).not.to.exist; + }); + + cy.validateNSelectDropdown("Generate Signed URL", "No", "Yes"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0].fileName).to.contains("Auto"); + expect(response.body.data.body[0].signedUrl).to.exist; + expect(response.body.data.body[0].url).to.exist; + }); + + cy.validateNSelectDropdown("Generate Un-signed URL", "Yes", "No"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body[0].fileName).to.contains("Auto"); + expect(response.body.data.body[0].signedUrl).to.exist; + expect(response.body.data.body[0].url).to.not.exist; + }); + + cy.deleteQueryUsingContext(); + }); + + it("6. Validate Delete file command for new file, Verify possible error msgs, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown( + "Commands", + "List files in bucket", + "Delete file", + ); + + cy.onlyQueryRun(); + cy.wait("@postExecute").should(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Mandatory parameter 'Bucket Name' is missing.", + ); + }); + cy.typeValueNValidate("AutoTest", "Bucket Name"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body).to.contains( + "Required parameter 'File Path' is missing.", + ); + }); + cy.typeValueNValidate("Auto", "File Path"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(false); + expect(response.body.data.body.split("(")[0].trim()).to.be.oneOf([ + "The specified bucket does not exist", + "The specified bucket is not valid.", + ]); + }); + + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.typeValueNValidate("AutoFile", "File Path"); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body.status).to.eq("File deleted successfully"); + }); + + cy.deleteQueryUsingContext(); + }); + + it("7. Validate List Files in bucket command after new file is deleted, Verify possible error msgs, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket"); + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.typeValueNValidate("Auto", "Prefix"); + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + expect(response.body.data.body.length).to.eq(0); //checking that body is empty array + }); + + cy.deleteQueryUsingContext(); + }); + + it("8. Validate Create a new file in bucket for UI Operations, run & delete the query", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown( + "Commands", + "List files in bucket", + "Create a new file", + ); + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.typeValueNValidate("CRUDNewPageFile", "File Path"); + cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary"); + cy.typeValueNValidate( + '{"data": "Hi, this is Automation script adding file for S3 CRUD New Page validation!"}', + "Content", + ); + + cy.get(queryLocators.settings).click(); + cy.xpath(queryLocators.queryTimeout) + .clear() + .type(30000); + cy.get(queryLocators.query).click(); + + cy.onlyQueryRun(); + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + }); + cy.deleteQueryUsingContext(); + }); + + it("9. Verify Search, Delete operations from NewPage UI created in S3 ds", function() { + // cy.wrap(Cypress.automation('remote:debugger:protocol', { + // command: 'Browser.grantPermissions', + // params: { + // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], + // // make the permission tighter by allowing the current origin only + // // like "http://localhost:56978" + // origin: window.location.origin, + // }, + // })) + + cy.NavigateToDSGeneratePage(datasourceName); + + //Verifying List of Files from UI + cy.get(generatePage.selectTableDropdown).click(); + cy.get(generatePage.dropdownOption) + .contains("assets-test.appsmith.com") + .scrollIntoView() + .should("be.visible") + .click(); + cy.get(generatePage.generatePageFormSubmitBtn).click(); + + cy.wait("@replaceLayoutWithCRUDPage").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + + cy.wait("@getActions"); + + cy.wait("@postExecute").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); //This verifies the Select on the table, ie page is created fine + + cy.ClickGotIt(); + + //Verifying Searching File from UI + cy.xpath(queryLocators.searchFilefield) + .type("CRUD") + .wait(500); //for search to finish + expect( + cy.xpath( + "//div[@data-cy='overlay-comments-wrapper']//span[text()='CRUDNewPageFile']", + ), + ).to.exist; + + cy.xpath( + "//div[@data-cy='overlay-comments-wrapper']//span[text()='CRUDNewPageFile']", + ).scrollIntoView(); + + //Verifying CopyFile URL icon from UI - Browser pop up appearing + // cy.xpath(queryLocators.copyURLicon).click() + // cy.window().its('navigator.clipboard').invoke('readText').should('contain', 'CRUDNewPageFile') + + //Verifying DeleteFile icon from UI + cy.xpath(queryLocators.deleteFileicon).click(); + expect( + cy.xpath("//span[text()='Are you sure you want to delete the file?']"), + ).to.exist; //verify Delete File dialog appears + cy.clickButton("Confirm").wait(1000); //wait for Dlete operation to be successfull + + cy.wait("@postExecute").then(({ response }) => { + expect(response.body.data.isExecutionSuccess).to.eq(true); + }); + + cy.get("span:contains('CRUDNewPageFile')").should("not.exist"); //verify Deletion of file is success from UI also + }); + + it("10. Validate Deletion of the Newly Created Page", () => { + cy.NavigateToQueryEditor(); + cy.NavigateToActiveTab(); + cy.contains(".t--datasource-name", datasourceName).click(); + cy.get(".t--delete-datasource").click(); + + cy.wait("@deleteDatasource").should( + "have.nested.property", + "response.body.responseMeta.status", + 409, + ); + + cy.deleteEntitybyName("Assets-test.appsmith.com"); + }); + + it("11. Verify 'Add to widget [Widget Suggestion]' functionality - S3", () => { + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket"); + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.runQuery(); + + cy.xpath(queryLocators.suggestedWidgetDropdown) + .click() + .wait(1000); + cy.wait("@updateLayout").then(({ response }) => { + expect(response.body.data.dsl.children[0].type).to.eq("DROP_DOWN_WIDGET"); + }); + cy.xpath(queryLocators.Query1) + .click() + .wait(2000); + + cy.get(queryLocators.suggestedTableWidget) + .click() + .wait(1000); + cy.wait("@updateLayout").then(({ response }) => { + expect(response.body.data.dsl.children[1].type).to.eq("TABLE_WIDGET"); + }); + cy.xpath(queryLocators.Query1) + .click() + .wait(2000); + + cy.xpath(queryLocators.suggestedWidgetText) + .click() + .wait(1000); + cy.wait("@updateLayout").then(({ response }) => { + expect(response.body.data.dsl.children[2].type).to.eq("TEXT_WIDGET"); + }); + cy.xpath(queryLocators.Query1) + .click() + .wait(2000); + + cy.deleteQueryUsingContext(); + }); + + it("12. Verify 'Connect Widget [snipping]' functionality - S3 ", () => { + cy.addDsl(dsl); + cy.NavigateToActiveDSQueryPane(datasourceName); + cy.validateNSelectDropdown("Commands", "List files in bucket"); + cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name"); + cy.runQuery(); + cy.clickButton("Select Widget"); + cy.xpath(queryLocators.snipeableTable).click(); + + cy.wait("@updateLayout").then(({ response }) => { + expect(response.body.data.dsl.children[0].widgetName).to.eq("Table1"); + expect(response.body.data.messages[0]).to.contain( + "will be executed automatically on page load", + ); + }); + + cy.xpath(queryLocators.Query1) + .click() + .wait(2000); + cy.deleteQueryUsingContext(); + + cy.deleteEntitybyName("Table1"); + }); + + it("11. Deletes the datasource", () => { + cy.NavigateToQueryEditor(); + cy.NavigateToActiveTab(); + cy.contains(".t--datasource-name", datasourceName).click(); + cy.get(".t--delete-datasource").click(); + cy.wait("@deleteDatasource").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + }); +}); diff --git a/app/client/cypress/locators/CMSApplocators.json b/app/client/cypress/locators/CMSApplocators.json new file mode 100644 index 0000000000..dbac2b27b1 --- /dev/null +++ b/app/client/cypress/locators/CMSApplocators.json @@ -0,0 +1,18 @@ + { + "selectPost":"//span[text()='POST']", + "selectDelete":"//span[text()='DELETE']", + "pagebutton":"//div[text()='Page1']", + "submitButton":"//span[text()='Submit New Proposal']", + "mailButton":"//span[text()='Mail']", + "sendMailText":"//span[text()='Send Mail']", + "inputMail":"//input[@value='Curt50@gmail.com']", + "subjectField":"(//div[@class='bp3-input-group']//input)[6]", + "contentField":"//textarea[@class='bp3-input']", + "confirmButton":"//span[text()='Confirm']", + "closeButton":"//span[text()='Close']", + "datasourcesbutton":"//div[text()='Datasources']", + "postApi":"//div[text()='send_mail']", + "deleteApi":"//div[text()='delete_proposal']", + "deleteButton":"//span[text()='Delete Proposal']", + "deleteTaskText":"//span[text()='Delete this task']" +} \ No newline at end of file diff --git a/app/client/cypress/locators/DatasourcesEditor.json b/app/client/cypress/locators/DatasourcesEditor.json index 60d0ba99e9..2d23656567 100644 --- a/app/client/cypress/locators/DatasourcesEditor.json +++ b/app/client/cypress/locators/DatasourcesEditor.json @@ -25,7 +25,9 @@ "datasourceTitle": ".t--edit-datasource-name .bp3-editable-text-content", "defaultDatabaseName": "input[name='datasourceConfiguration.connection.defaultDatabaseName']", "datasourceConfigurationProperty":"input[name='datasourceConfiguration.properties[0]']", + "googleSheets":".t--plugin-name:contains('Google Sheets')", "selConnectionType": "[data-cy='datasourceConfiguration.connection.type']", + "scope":"[data-cy='datasourceConfiguration.authentication.scopeString']", "Mysql": ".t--plugin-name:contains('Mysql')", "ElasticSearch": ".t--plugin-name:contains('ElasticSearch')", "DynamoDB": ".t--plugin-name:contains('DynamoDB')", diff --git a/app/client/cypress/locators/FormWidgets.json b/app/client/cypress/locators/FormWidgets.json index 6086ec1c34..1f2b0bc8fc 100644 --- a/app/client/cypress/locators/FormWidgets.json +++ b/app/client/cypress/locators/FormWidgets.json @@ -1,6 +1,7 @@ { "checkboxWidget": ".t--draggable-checkboxwidget", "dropdownWidget": ".t--draggable-dropdownwidget", + "menuButtonWidget": ".t--draggable-menubuttonwidget", "multiselectWidget": ".t--draggable-multiselectwidget", "multiselecttreeWidget": ".t--draggable-multiselecttreewidget", "singleselecttreeWidget": ".t--draggable-singleselecttreewidget", diff --git a/app/client/cypress/locators/GeneratePage.json b/app/client/cypress/locators/GeneratePage.json index 5e56c9221a..02e3effc12 100644 --- a/app/client/cypress/locators/GeneratePage.json +++ b/app/client/cypress/locators/GeneratePage.json @@ -23,8 +23,5 @@ "currentNameField": "//div[@type='FORM_WIDGET']//span[text()='name:']//ancestor::div[contains(@class,'t--widget-textwidget')]/preceding-sibling::div[contains(@class, 't--widget-inputwidget')][1]//input", "deleteofSelectedRow": "//div[@class='tr selected-row']//span[text()='Delete']", "confirmBtn": "span:contains('Confirm')", - "postgressnewPageEntityMenu": "//div[text()='Public.users_crud']/ancestor::div[contains(@class, 't--entity page')]//span[contains(@class, 'entity-context-menu')]//div", - "deleteMenuItem": "//div[text()='Delete']/parent::a[contains(@class, 'single-select')]", - "mongonewPageEntityMenu": "//div[text()='ListingsAndReviews']/ancestor::div[contains(@class, 't--entity page')]//span[contains(@class, 'entity-context-menu')]//div" - + "deleteMenuItem": "//div[text()='Delete']/parent::a[contains(@class, 'single-select')]" } \ No newline at end of file diff --git a/app/client/cypress/locators/QueryEditor.json b/app/client/cypress/locators/QueryEditor.json index c22d6f99e6..a029ed2c41 100644 --- a/app/client/cypress/locators/QueryEditor.json +++ b/app/client/cypress/locators/QueryEditor.json @@ -21,19 +21,15 @@ "queryCreate": "//div[contains(@class, 't--template-menu')]//div[text()='Create']", "queryUpdate": "//div[contains(@class, 't--template-menu')]//div[text()='Update']", "queryDelete": "//div[contains(@class, 't--template-menu')]//div[text()='Delete']", - "codeTextArea": "div.CodeMirror-code", - "findDocs" : "//div[text()='Find Document(s)']", - "collectionField": "//p[text()='Collection']/following-sibling::div//div[@class='CodeMirror-code']", - "queryField": "//p[text()='Query']/following-sibling::div//div[@class='CodeMirror-code']", - "sortField": "//p[text()='Sort']/following-sibling::div//div[@class='CodeMirror-code']", - "projectionField": "//p[text()='Projection']/following-sibling::div//div[@class='CodeMirror-code']", - "limitField": "//p[text()='Limit']/following-sibling::div//div[@class='CodeMirror-code']", - "skipField": "//p[text()='Skip']/following-sibling::div//div[@class='CodeMirror-code']", - "keyField": "//p[text()='Key']/following-sibling::div//div[@class='CodeMirror-code']", - "arrayOfPipelinesField": "//p[text()='Array of Pipelines']/following-sibling::div//div[@class='CodeMirror-code']", - "countText": "//span[contains(@class, 'cs-text')][text()='Result:']/span" - - - - + "codeTextArea": "//div[@class='CodeMirror-code']//span/span", + "countText": "//span[contains(@class, 'cs-text')][text()='Result:']/span", + "searchFilefield": "//input[@placeholder='Search File Prefix']", + "copyURLicon": "//button/span[@icon='link']", + "deleteFileicon": "//button/span[@icon='trash']", + "snipeableTable":"//input[@type='search']", + "Query1": "//div[contains(@class, 't--entity-name')][text()='Query1']", + "suggestedWidgetDropdown": "//div[contains(@class, 't--suggested-widget-DROP_DOWN_WIDGET')]", + "suggestedWidgetText": "//div[contains(@class, 't--suggested-widget-TEXT_WIDGET')]", + "queryTimeout": "//input[@name='actionConfiguration.timeoutInMillisecond']", + "mongoFormFind": "//div[text()='listingsAndReviews']/ancestor::div/following-sibling::div[contains(@class, 'entity-context-menu')]" } diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index 844f80e12a..1fe77de173 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -12,7 +12,7 @@ "buttonBackground": ".sc-ecQjpJ > div > .bp3-button", "copyWidget": ".t--copy-widget svg", "removeWidget": ".t--delete-widget svg", - "propertypaneText": ".t--propertypane .bp3-panel-stack-view", + "propertypaneText": ".t--propertypane .t--property-pane-view", "formButtonWidget": ".t--widget-formbuttonwidget", "dropdownwidget": ".t--widget-dropdownwidget", "textWidget": ".t--draggable-textwidget", @@ -60,6 +60,7 @@ "autoPlay": ".t--property-control-autoplay > .bp3-control > .bp3-control-indicator", "defaultOption": ".t--property-control-defaultoption .CodeMirror-code", "dropdownSingleSelect": ".bp3-popover-target > div > .bp3-button", + "menuButton": ".bp3-popover2-target", "defaultSingleSelectValue": ".bp3-popover-target > div > .bp3-button > .bp3-button-text", "widgetBtn": ".t--widget-buttonwidget button", "widgetBtnText": ".t--widget-buttonwidget button .bp3-button-text", diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index 6757297088..1071d26b69 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -2,6 +2,7 @@ "editIcon": ".t--widget-propertypane-toggle", "helpIcon": ".t--widget-help-control", "editPropCrossButton": ".t--property-pane-close-btn", + "editPropBackButton": ".t--property-pane-back-btn", "deleteWidgetIcon": ".t--widget-delete-control", "dropdownSelectButton": ".t--open-dropdown-Select-Action", "crossbutton": ".t--property-pane-close-btn", @@ -65,7 +66,7 @@ "selectMenuItem": ".bp3-menu li>a>div", "evaluatedTypeTitle": ".t--CodeEditor-evaluatedValue > p:first-of-type", "evaluatedType": ".t--CodeEditor-evaluatedValue > div:first-of-type pre", - "evaluatedCurrentValue": ".t--CodeEditor-evaluatedValue > div:last-of-type pre", + "evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre", "entityExplorersearch": "#entity-explorer-search", "entitySearchResult": ".t--entity-name:contains('", "saveStatusContainer": ".t--save-status-container", @@ -95,7 +96,7 @@ "selectedColType": ".t--property-control-columntype button span", "collapsesection": ".t--property-pane-section-collapse-general .bp3-icon", "selectTab": ".t--tab-Tab", - "layoutControl": ".t--layout-control-wrapper", + "layoutControls": ".t--layout-control-wrapper button", "layoutPopover": ".bp3-popover.bp3-minimal.layout-control", "canvas": ".t--canvas-artboard", "deflautSelectedRow": ".t--property-control-defaultselectedrow textarea", diff --git a/app/client/cypress/locators/publishWidgetspage.json b/app/client/cypress/locators/publishWidgetspage.json index 35e419820d..db8943f523 100644 --- a/app/client/cypress/locators/publishWidgetspage.json +++ b/app/client/cypress/locators/publishWidgetspage.json @@ -44,5 +44,9 @@ "pageInfo": ".bp3-heading", "inputGrp": ".bp3-input-group input", "datePickerNew": ".t--widget-datepickerwidget2", - "tab": ".t--tab-Tab" + "tab": ".t--tab-Tab", + "downloadOption": ".t--table-download-data-option", + "addFilter": ".t--add-filter-btn", + "operatorsDropdown": ".t--table-filter-operators-dropdown", + "attributesDropdown": ".t--table-filter-columns-dropdown" } \ No newline at end of file diff --git a/app/client/cypress/plugins/index.js b/app/client/cypress/plugins/index.js index 340a8cdedb..c65001ea24 100644 --- a/app/client/cypress/plugins/index.js +++ b/app/client/cypress/plugins/index.js @@ -5,6 +5,7 @@ const path = require("path"); const dotenv = require("dotenv"); const chalk = require("chalk"); const cypressLogToOutput = require("cypress-log-to-output"); +const { isFileExist } = require("cy-verify-downloads"); // *********************************************************** // This example plugins/index.js can be used to load plugins @@ -30,13 +31,19 @@ module.exports = (on, config) => { } return false; }); +}; + +module.exports = (on, config) => { + on("task", { + isFileExist, + }); // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config on("before:browser:launch", (browser = {}, launchOptions) => { /* - Uncomment below to get console log printed in cypress output - */ + Uncomment below to get console log printed in cypress output + */ launchOptions.args = cypressLogToOutput.browserLaunchHandler( browser, diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 034d02dc61..7bcd179b1b 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -2,7 +2,9 @@ /* eslint-disable cypress/no-unnecessary-waiting */ /* eslint-disable cypress/no-assigning-return-values */ +require("cy-verify-downloads").addCustomCommand(); require("cypress-file-upload"); + const dayjs = require("dayjs"); const loginPage = require("../locators/LoginPage.json"); @@ -27,8 +29,10 @@ const generatePage = require("../locators/GeneratePage.json"); const jsEditorLocators = require("../locators/JSEditor.json"); const queryLocators = require("../locators/QueryEditor.json"); const welcomePage = require("../locators/welcomePage.json"); +const publishWidgetspage = require("../locators/publishWidgetspage.json"); let pageidcopy = " "; +const chainStart = Symbol(); export const initLocalstorage = () => { cy.window().then((window) => { @@ -51,8 +55,13 @@ Cypress.Commands.add("renameOrg", (orgName, newOrgName) => { .click({ force: true }); cy.get(homePage.renameOrgInput) .should("be.visible") - .type(newOrgName) - .type("{enter}"); + .type(newOrgName); + cy.get(commonlocators.homeIcon).click({ force: true }); + cy.wait("@updateOrganization").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); cy.contains(newOrgName); }); @@ -75,6 +84,49 @@ Cypress.Commands.add( }, ); +Cypress.Commands.add("downloadData", (filetype) => { + cy.get(publishWidgetspage.downloadBtn).click({ force: true }); + + cy.get(publishWidgetspage.downloadOption) + .contains(filetype) + .click({ force: true }); +}); + +Cypress.Commands.add("validateDownload", (fileName) => { + const downloadedFilename = Cypress.config("downloadsFolder") + .concat("/") + .concat(fileName); + cy.readFile(downloadedFilename, "binary", { + timeout: 15000, + }).should((buffer) => expect(buffer.length).to.be.gt(100)); +}); + +Cypress.Commands.add( + "AddFilterWithOperator", + (operator, option, condition, value) => { + cy.get(publishWidgetspage.addFilter).click(); + cy.get(publishWidgetspage.operatorsDropdown).click({ force: true }); + cy.get(publishWidgetspage.attributeValue) + .contains(operator) + .click({ force: true }); + cy.get(publishWidgetspage.attributesDropdown) + .last() + .click({ force: true }); + cy.get(publishWidgetspage.attributeValue) + .contains(option) + .click({ force: true }); + cy.get(publishWidgetspage.conditionDropdown) + .last() + .click({ force: true }); + cy.get(publishWidgetspage.attributeValue) + .contains(condition) + .click({ force: true }); + cy.get(publishWidgetspage.inputValue) + .last() + .type(value); + }, +); + Cypress.Commands.add("navigateToOrgSettings", (orgName) => { cy.get(homePage.orgList.concat(orgName).concat(")")) .scrollIntoView() @@ -85,7 +137,6 @@ Cypress.Commands.add("navigateToOrgSettings", (orgName) => { .find(homePage.optionsIcon) .click({ force: true }); cy.xpath(homePage.MemberSettings).click({ force: true }); - cy.wait("@getOrganisation"); cy.wait("@getRoles").should( "have.nested.property", "response.body.responseMeta.status", @@ -138,7 +189,7 @@ Cypress.Commands.add("CheckShareIcon", (orgName, count) => { Cypress.Commands.add("stubPostHeaderReq", () => { cy.intercept("POST", "/api/v1/users/invite", (req) => { - req.headers.origin = "Cypress"; + req.headers["origin"] = "Cypress"; }).as("mockPostInvite"); }); @@ -192,7 +243,6 @@ Cypress.Commands.add("deleteUserFromOrg", (orgName, email) => { .find(homePage.optionsIcon) .click({ force: true }); cy.xpath(homePage.MemberSettings).click({ force: true }); - cy.wait("@getOrganisation"); cy.wait("@getRoles").should( "have.nested.property", "response.body.responseMeta.status", @@ -369,17 +419,17 @@ Cypress.Commands.add("firestoreDatasourceForm", () => { cy.get(datasourceEditor.datasourceConfigUrl).type( datasourceFormData["database-url"], ); - cy.get(datasourceEditor.projectID).type(datasourceFormData.projectID); + cy.get(datasourceEditor.projectID).type(datasourceFormData["projectID"]); cy.get(datasourceEditor.serviceAccCredential) .clear() - .type(datasourceFormData.serviceAccCredentials); + .type(datasourceFormData["serviceAccCredentials"]); }); Cypress.Commands.add("amazonDatasourceForm", () => { - cy.get(datasourceEditor.projectID).type(datasourceFormData.access_key); + cy.get(datasourceEditor.projectID).type(datasourceFormData["access_key"]); cy.get(datasourceEditor.serviceAccCredential) .clear() - .type(datasourceFormData.secret_key); + .type(datasourceFormData["secret_key"]); }); Cypress.Commands.add("DeleteApp", (appName) => { @@ -506,12 +556,10 @@ Cypress.Commands.add("LogOut", () => { Cypress.Commands.add("NavigateToHome", () => { cy.get(commonlocators.homeIcon).click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(1000); - cy.wait("@applications").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); + cy.wait(3000); + cy.wait("@applications"); + cy.get(".t--applications-container .createnew").should("be.visible"); + cy.get(".t--applications-container .createnew").should("be.enabled"); }); Cypress.Commands.add("NavigateToWidgets", (pageName) => { @@ -582,6 +630,7 @@ Cypress.Commands.add("ResponseTextCheck", (textTocheck) => { Cypress.Commands.add("NavigateToAPI_Panel", () => { cy.get(pages.addEntityAPI) + .last() .should("be.visible") .click({ force: true }); cy.get(pages.integrationCreateNew) @@ -1153,7 +1202,6 @@ Cypress.Commands.add("AddActionWithModal", () => { .click({ force: true }); cy.get(modalWidgetPage.selectModal).click(); cy.get(modalWidgetPage.createModalButton).click({ force: true }); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); }); Cypress.Commands.add("createModal", (ModalName) => { @@ -1173,7 +1221,6 @@ Cypress.Commands.add("createModal", (ModalName) => { modalWidgetPage.modalName, modalWidgetPage.modalName, ); - cy.get(commonlocators.editPropCrossButton).click({ force: true }); //changing the Model label cy.get(modalWidgetPage.modalWidget + " " + widgetsPage.textWidget) @@ -1281,7 +1328,9 @@ Cypress.Commands.add("EvaluateCurrentValue", (currentValue) => { cy.get(commonlocators.evaluatedCurrentValue) .first() .should("be.visible") - .contains(currentValue); + .then(($text) => { + expect($text.text()).to.eq(currentValue); + }); }); Cypress.Commands.add("PublishtheApp", () => { @@ -2089,9 +2138,16 @@ Cypress.Commands.add("NavigateToActiveTab", () => { Cypress.Commands.add("NavigateToActiveDSQueryPane", (datasourceName) => { cy.NavigateToQueryEditor(); cy.NavigateToActiveTab(); - cy.contains(".t--datasource-name", datasourceName) - .find(queryLocators.createQuery) - .click(); + + cy.get(datasource.datasourceCard) + .contains(datasourceName) + .scrollIntoView() + .should("be.visible") + .closest(datasource.datasourceCard) + .within(() => { + cy.get(queryLocators.createQuery).click({ force: true }); + }) + .wait(2000); //for the specified page to load }); Cypress.Commands.add("NavigateToDSGeneratePage", (datasourceName) => { @@ -2105,7 +2161,8 @@ Cypress.Commands.add("NavigateToDSGeneratePage", (datasourceName) => { .closest(datasource.datasourceCard) .within(() => { cy.get(datasource.datasourceCardGeneratePageBtn).click(); - }); + }) + .wait(2000); //for the specified page to load }); Cypress.Commands.add("ClickGotIt", () => { @@ -2141,6 +2198,10 @@ Cypress.Commands.add("testSaveDatasource", () => { cy.testDatasource(); }); +Cypress.Commands.add("fillGoogleSheetsDatasourceForm", () => { + cy.get(datasourceEditor["scope"]).click(); +}); + Cypress.Commands.add( "fillMongoDatasourceForm", (shouldAddTrailingSpaces = false) => { @@ -2151,23 +2212,24 @@ Cypress.Commands.add( ? datasourceFormData["mongo-defaultDatabaseName"] + " " : datasourceFormData["mongo-defaultDatabaseName"]; - cy.get(datasourceEditor.host).type(hostAddress); + cy.get(datasourceEditor["host"]).type(hostAddress); //cy.get(datasourceEditor.port).type(datasourceFormData["mongo-port"]); - cy.get(datasourceEditor.selConnectionType).click(); + //cy.get(datasourceEditor["port"]).type(datasourceFormData["mongo-port"]); + cy.get(datasourceEditor["selConnectionType"]).click(); cy.contains(datasourceFormData["connection-type"]).click(); - cy.get(datasourceEditor.defaultDatabaseName).type(databaseName); + cy.get(datasourceEditor["defaultDatabaseName"]).type(databaseName); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.get(datasourceEditor.databaseName) + cy.get(datasourceEditor["databaseName"]) .clear() .type(datasourceFormData["mongo-databaseName"]); - cy.get(datasourceEditor.username).type( + cy.get(datasourceEditor["username"]).type( datasourceFormData["mongo-username"], ); - cy.get(datasourceEditor.password).type( + cy.get(datasourceEditor["password"]).type( datasourceFormData["mongo-password"], ); - cy.get(datasourceEditor.authenticationAuthtype).click(); + cy.get(datasourceEditor["authenticationAuthtype"]).click(); cy.contains(datasourceFormData["mongo-authenticationAuthtype"]).click({ force: true, }); @@ -2308,32 +2370,32 @@ Cypress.Commands.add( "fillUsersMockDatasourceForm", (shouldAddTrailingSpaces = false) => { const userMockDatabaseName = shouldAddTrailingSpaces - ? `${datasourceFormData.mockDatabaseName + " "}` - : datasourceFormData.mockDatabaseName; + ? `${datasourceFormData["mockDatabaseName"] + " "}` + : datasourceFormData["mockDatabaseName"]; const userMockHostAddress = shouldAddTrailingSpaces - ? `${datasourceFormData.mockHostAddress + " "}` - : datasourceFormData.mockHostAddress; + ? `${datasourceFormData["mockHostAddress"] + " "}` + : datasourceFormData["mockHostAddress"]; const userMockDatabaseUsername = shouldAddTrailingSpaces - ? `${datasourceFormData.mockDatabaseUsername + " "}` - : datasourceFormData.mockDatabaseUsername; + ? `${datasourceFormData["mockDatabaseUsername"] + " "}` + : datasourceFormData["mockDatabaseUsername"]; - cy.get(datasourceEditor.host) + cy.get(datasourceEditor["host"]) .clear() .type(userMockHostAddress); - cy.get(datasourceEditor.databaseName) + cy.get(datasourceEditor["databaseName"]) .clear() .type(userMockDatabaseName); - cy.get(datasourceEditor.sectionAuthentication).click(); + cy.get(datasourceEditor["sectionAuthentication"]).click(); - cy.get(datasourceEditor.password) + cy.get(datasourceEditor["password"]) .clear() - .type(datasourceFormData.mockDatabasePassword); + .type(datasourceFormData["mockDatabasePassword"]); - cy.get(datasourceEditor.username) + cy.get(datasourceEditor["username"]) .clear() .type(userMockDatabaseUsername); }, @@ -2362,9 +2424,7 @@ Cypress.Commands.add("deleteDatasource", (datasourceName) => { }); Cypress.Commands.add("runQuery", () => { - cy.xpath(queryEditor.runQuery) - .last() - .click(); + cy.onlyQueryRun(); cy.wait("@postExecute").should( "have.nested.property", "response.body.responseMeta.status", @@ -2375,7 +2435,7 @@ Cypress.Commands.add("runQuery", () => { Cypress.Commands.add("onlyQueryRun", () => { cy.xpath(queryEditor.runQuery) .last() - .click(); + .click({ force: true }); }); Cypress.Commands.add("hoverAndClick", () => { @@ -2544,7 +2604,7 @@ Cypress.Commands.add("changeButtonColor", (buttonColor) => { }); Cypress.Commands.add("closePropertyPane", () => { - cy.get(commonlocators.editPropCrossButton).click({ force: true }); + cy.get(commonlocators.canvas).click({ force: true }); }); Cypress.Commands.add("onClickActions", (forSuccess, forFailure, endp) => { @@ -2669,9 +2729,9 @@ Cypress.Commands.add("createAndFillApi", (url, parameters) => { }); Cypress.Commands.add("isSelectRow", (index) => { - cy.get( - '.tbody .td[data-rowindex="' + index + '"][data-colindex="' + 0 + '"]', - ).click({ force: true }); + cy.get('.tbody .td[data-rowindex="' + index + '"][data-colindex="' + 0 + '"]') + .first() + .click({ force: true }); }); Cypress.Commands.add("readTabledata", (rowNum, colNum) => { @@ -3037,6 +3097,7 @@ Cypress.Commands.add("createJSObject", (JSCode) => { }); Cypress.Commands.add("createSuperUser", () => { + cy.wait(1000); cy.get(welcomePage.getStarted).should("be.visible"); cy.get(welcomePage.getStarted).should("not.be.disabled"); cy.get(welcomePage.getStarted).click(); @@ -3210,3 +3271,128 @@ Cypress.Commands.add( }); }, ); + +Cypress.Commands.add( + "validateNSelectDropdown", + (ddTitle, currentValue, newValue) => { + let toChange = false; + cy.xpath('//div[contains(text(),"' + currentValue + '")]').should( + "exist", + currentValue + " dropdown value not present", + ); + if (newValue) toChange = true; + if (toChange) { + cy.xpath( + "//p[text()='" + ddTitle + "']/following-sibling::div/div", + ).click(); //to expand the dropdown + cy.xpath('//div[contains(text(),"' + newValue + '")]') + .last() + .click({ force: true }); //to select the new value + } + }, +); + +Cypress.Commands.add("typeValueNValidate", (valueToType, fieldName = "") => { + if (fieldName) { + cy.xpath("//p[text()='" + fieldName + "']/following-sibling::div").then( + ($field) => { + cy.updateCodeInput($field, valueToType); + }, + ); + } else { + cy.xpath("//div[@class='CodeEditorTarget']").then(($field) => { + cy.updateCodeInput($field, valueToType); + }); + } + + cy.EvaluateCurrentValue(valueToType); + + // cy.xpath("//p[text()='" + fieldName + "']/following-sibling::div//div[@class='CodeMirror-code']//span/span").should((fieldValue) => { + // textF = fieldValue.innerText + // fieldValue.innerText = "" + // }).then(() => { + // cy.log("current field value is : '" + textF + "'") + // }) +}); + +Cypress.Commands.add("clickButton", (btnVisibleText) => { + cy.xpath("//span[text()='" + btnVisibleText + "']/parent::button").click({ + force: true, + }); +}); + +Cypress.Commands.add("deleteEntitybyName", (entityNameinLeftSidebar) => { + cy.xpath( + "//div[text()='" + + entityNameinLeftSidebar + + "']/ancestor::div[contains(@class, 't--entity')]//span[contains(@class, 'entity-context-menu')]//div", + ) + .first() + .click({ force: true }); + + cy.xpath(generatePage.deleteMenuItem).click(); +}); + +Cypress.Commands.add( + "EvaluatFieldValue", + (fieldName = "", currentValue = "") => { + let toValidate = false; + if (currentValue) toValidate = true; + + if (fieldName) { + cy.xpath( + "//p[text()='" + + fieldName + + "']/following-sibling::div//div[@class='CodeMirror-code']", + ).click(); + } else { + cy.xpath("//div[@class='CodeMirror-code']").click(); + } + + cy.wait(2000); + const val = cy + .get(commonlocators.evaluatedCurrentValue) + .first() + .should("be.visible") + .invoke("text"); + + if (toValidate) expect(val).to.eq(currentValue); + + return val; + }, +); + +cy.all = function(...commands) { + const _ = Cypress._; + const chain = cy.wrap(null, { log: false }); + const stopCommand = _.find(cy.queue.commands, { + attributes: { chainerId: chain.chainerId }, + }); + const startCommand = _.find(cy.queue.commands, { + attributes: { chainerId: commands[0].chainerId }, + }); + const p = chain.then(() => { + return _(commands) + .map((cmd) => { + return cmd[chainStart] + ? cmd[chainStart].attributes + : _.find(cy.queue.commands, { + attributes: { chainerId: cmd.chainerId }, + }).attributes; + }) + .concat(stopCommand.attributes) + .slice(1) + .flatMap((cmd) => { + return cmd.prev.get("subject"); + }) + .value(); + }); + p[chainStart] = startCommand; + return p; +}; + +// Cypress.Commands.overwrite("type", (originalFn, element, text, options) => { +// const clearedText = '{selectall}{backspace}'+`${text}`; + +// return originalFn(element, clearedText, options); +// }); diff --git a/app/client/index.tsx b/app/client/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/client/jest.config.js b/app/client/jest.config.js index f390ccddc2..b14ad020e2 100644 --- a/app/client/jest.config.js +++ b/app/client/jest.config.js @@ -15,7 +15,7 @@ module.exports = { moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"], moduleDirectories: ["node_modules", "src", "test"], transformIgnorePatterns: [ - "/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github|lodash-es|@draft-js-plugins)", + "/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github|lodash-es|@draft-js-plugins|react-documents)", ], moduleNameMapper: { "\\.(css|less)$": "/test/__mocks__/styleMock.js", diff --git a/app/client/package.json b/app/client/package.json index b1ae129e84..032b879eb5 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -39,6 +39,7 @@ "axios": "^0.21.1", "caniuse-lite": "^1.0.30001208", "chance": "^1.1.3", + "classnames": "^2.3.1", "codemirror": "^5.59.2", "copy-to-clipboard": "^3.3.1", "core-js": "^3.9.1", @@ -50,6 +51,7 @@ "draft-js": "^0.11.7", "emoji-mart": "^3.0.1", "eslint": "^7.11.0", + "exceljs-lightweight": "^1.14.0", "fast-deep-equal": "^3.1.1", "fast-xml-parser": "^3.17.5", "flow-bin": "^0.148.0", @@ -73,6 +75,7 @@ "lodash-move": "^1.1.1", "loglevel": "^1.7.1", "lottie-web": "^5.7.4", + "mammoth": "^1.4.19", "marked": "^2.0.0", "memoize-one": "^5.2.1", "moment": "^2.24.0", @@ -96,6 +99,7 @@ "react-dnd": "^9.3.4", "react-dnd-html5-backend": "^9.3.4", "react-dnd-touch-backend": "^9.4.0", + "react-documents": "^1.0.4", "react-dom": "^16.7.0", "react-google-maps": "^9.4.5", "react-google-recaptcha": "^2.1.0", @@ -236,6 +240,7 @@ "@types/toposort": "^2.0.3", "@typescript-eslint/eslint-plugin": "^4.15.0", "@typescript-eslint/parser": "^4.15.0", + "autoprefixer": "^9", "babel-plugin-module-resolver": "^4.1.0", "babel-plugin-styled-components": "^1.10.7", "cra-bundle-analyzer": "^0.1.0", @@ -245,6 +250,7 @@ "cypress-multi-reporters": "^1.2.4", "cypress-real-events": "^1.5.1", "cypress-xpath": "^1.4.0", + "cy-verify-downloads": "^0.0.5", "dotenv": "^8.1.0", "eslint": "^7.11.0", "eslint-config-prettier": "^6.12.0", @@ -265,6 +271,7 @@ "msw": "^0.28.0", "patch-package": "^6.4.7", "plop": "^2.7.4", + "postcss": "^7", "postinstall-postinstall": "^2.1.0", "raw-loader": "^4.0.2", "react-docgen-typescript-loader": "^3.6.0", @@ -273,6 +280,8 @@ "redux-devtools": "^3.5.0", "redux-devtools-extension": "^2.13.8", "redux-mock-store": "^1.5.4", + "storybook-addon-designs": "^5.4.0", + "tailwindcss": "npm:@tailwindcss/postcss7-compat", "ts-jest": "^26.5.4", "webpack-merge": "^4.2.2", "workbox-webpack-plugin": "^5.1.2" diff --git a/app/client/src/actions/controlActions.tsx b/app/client/src/actions/controlActions.tsx index 98dc32e945..7a0dc02760 100644 --- a/app/client/src/actions/controlActions.tsx +++ b/app/client/src/actions/controlActions.tsx @@ -85,7 +85,7 @@ export interface UpdateWidgetPropertyPayload { shouldReplay?: boolean; } -export interface UpdateCanvasLayout { +export interface UpdateCanvasLayoutPayload { width: number; height: number; } diff --git a/app/client/src/actions/editorActions.ts b/app/client/src/actions/editorActions.ts new file mode 100644 index 0000000000..64fc2472ee --- /dev/null +++ b/app/client/src/actions/editorActions.ts @@ -0,0 +1,33 @@ +import { ReduxActionTypes } from "constants/ReduxActionConstants"; + +/** + * action that sets preview mode + * + * @param payload + * @returns + */ + +export const setPreviewModeAction = (payload: boolean) => ({ + type: ReduxActionTypes.SET_PREVIEW_MODE, + payload, +}); + +/** + * action that update canvas layout + * + * @param width + * @param height + * @returns + */ +export const updateCanvasLayoutAction = ( + width: number, + height: number | undefined, +) => { + return { + type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT, + payload: { + height, + width, + }, + }; +}; diff --git a/app/client/src/actions/explorerActions.ts b/app/client/src/actions/explorerActions.ts index 4ccaaa134d..b29ee82f7e 100644 --- a/app/client/src/actions/explorerActions.ts +++ b/app/client/src/actions/explorerActions.ts @@ -8,3 +8,47 @@ export const initExplorerEntityNameEdit = (actionId: string) => { }, }; }; + +/** + * action that make explorer pin/unpin + * + * @param shouldPin + * @returns + */ +export const setExplorerPinnedAction = (shouldPin: boolean) => { + return { + type: ReduxActionTypes.SET_EXPLORER_PINNED, + payload: { + shouldPin, + }, + }; +}; + +/** + * action that make explorer active/inactive + * NOTE: active state is used to slide the sidebar in unpinned state on hover. + * + * @param shouldPin + * @returns + */ +export const setExplorerActiveAction = (active: boolean) => { + return { + type: ReduxActionTypes.SET_EXPLORER_ACTIVE, + payload: active, + }; +}; + +/** + * action that updates explorer width + * + * @param shouldPin + * @returns + */ +export const updateExplorerWidthAction = (width: number | undefined) => { + return { + type: ReduxActionTypes.UPDATE_EXPLORER_WIDTH, + payload: { + width, + }, + }; +}; diff --git a/app/client/src/actions/propertyPaneActions.test.ts b/app/client/src/actions/propertyPaneActions.test.ts deleted file mode 100644 index 5914e6c8b8..0000000000 --- a/app/client/src/actions/propertyPaneActions.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as actions from "./propertyPaneActions"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; - -describe("property pane action actions", () => { - it("should create an action hide Property Pane", () => { - const expectedAction = { - type: ReduxActionTypes.HIDE_PROPERTY_PANE, - }; - expect(actions.hidePropertyPane()).toEqual(expectedAction); - }); -}); diff --git a/app/client/src/actions/propertyPaneActions.ts b/app/client/src/actions/propertyPaneActions.ts index 57551ab0cf..e4092e661e 100644 --- a/app/client/src/actions/propertyPaneActions.ts +++ b/app/client/src/actions/propertyPaneActions.ts @@ -9,12 +9,6 @@ export const updateWidgetName = (widgetId: string, newName: string) => { }; }; -export const hidePropertyPane = () => { - return { - type: ReduxActionTypes.HIDE_PROPERTY_PANE, - }; -}; - export const bindDataToWidget = (payload: { widgetId: string }) => { return { type: ReduxActionTypes.BIND_DATA_TO_WIDGET, diff --git a/app/client/src/assets/icons/ads/double-arrow-left.svg b/app/client/src/assets/icons/ads/double-arrow-left.svg new file mode 100644 index 0000000000..8b90f27ea1 --- /dev/null +++ b/app/client/src/assets/icons/ads/double-arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/ads/double-arrow-right.svg b/app/client/src/assets/icons/ads/double-arrow-right.svg new file mode 100644 index 0000000000..6a50b9ceef --- /dev/null +++ b/app/client/src/assets/icons/ads/double-arrow-right.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/client/src/assets/icons/comments/pin_3.svg b/app/client/src/assets/icons/comments/pin_3.svg index 59101bf219..ec83d1f7af 100644 --- a/app/client/src/assets/icons/comments/pin_3.svg +++ b/app/client/src/assets/icons/comments/pin_3.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + diff --git a/app/client/src/assets/icons/comments/unpin.svg b/app/client/src/assets/icons/comments/unpin.svg index 0f1c9866c7..5e1bb12667 100644 --- a/app/client/src/assets/icons/comments/unpin.svg +++ b/app/client/src/assets/icons/comments/unpin.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + diff --git a/app/client/src/assets/icons/control/copy.svg b/app/client/src/assets/icons/control/copy.svg index 4af7dfe517..68509d2844 100644 --- a/app/client/src/assets/icons/control/copy.svg +++ b/app/client/src/assets/icons/control/copy.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + diff --git a/app/client/src/assets/icons/control/help.svg b/app/client/src/assets/icons/control/help.svg index 1dbe805af1..a7af34f265 100644 --- a/app/client/src/assets/icons/control/help.svg +++ b/app/client/src/assets/icons/control/help.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/app/client/src/assets/icons/form/trash.svg b/app/client/src/assets/icons/form/trash.svg index 6ae5e921f8..44f5601df3 100755 --- a/app/client/src/assets/icons/form/trash.svg +++ b/app/client/src/assets/icons/form/trash.svg @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + diff --git a/app/client/src/assets/icons/header/hamburger.svg b/app/client/src/assets/icons/header/hamburger.svg new file mode 100644 index 0000000000..abf743e9fc --- /dev/null +++ b/app/client/src/assets/icons/header/hamburger.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/styles/index.css b/app/client/src/assets/styles/index.css new file mode 100644 index 0000000000..f35fbb7fcb --- /dev/null +++ b/app/client/src/assets/styles/index.css @@ -0,0 +1,48 @@ +@import './tailwind.css'; + +/** +* --------------------------------------------------------------------------------------------------- +* general +* --------------------------------------------------------------------------------------------------- +*/ +body, html { + @apply overflow-x-hidden; +} + + +/** +* --------------------------------------------------------------------------------------------------- +* blueprint specific css overrides +* --------------------------------------------------------------------------------------------------- +*/ +.bp3-panel-stack-view { + background: none; +} + + +/** +* --------------------------------------------------------------------------------------------------- +* custom css +* --------------------------------------------------------------------------------------------------- +*/ + +* { + scrollbar-width: thin; +} + +::-webkit-scrollbar { + height: 6px; + width: 6px; +} + +::-webkit-scrollbar-track { + @apply bg-white; +} + +::-webkit-scrollbar-thumb { + @apply bg-transparent; +} + +:hover::-webkit-scrollbar-thumb { + @apply bg-gray-300; +} diff --git a/app/client/src/assets/styles/tailwind.css b/app/client/src/assets/styles/tailwind.css new file mode 100644 index 0000000000..b5c61c9567 --- /dev/null +++ b/app/client/src/assets/styles/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/app/client/src/comments/AppComments/AppComments.tsx b/app/client/src/comments/AppComments/AppComments.tsx index c78e9df639..cfb37cda93 100644 --- a/app/client/src/comments/AppComments/AppComments.tsx +++ b/app/client/src/comments/AppComments/AppComments.tsx @@ -1,20 +1,22 @@ +import { tailwindLayers } from "constants/Layers"; import React from "react"; import { useSelector } from "react-redux"; import { commentModeSelector } from "selectors/commentsSelectors"; import AppCommentsHeader from "./AppCommentsHeader"; import AppCommentThreads from "./AppCommentThreads"; -import Container from "./Container"; -function AppComments(props: { isInline?: boolean }) { +function AppComments() { const isCommentMode = useSelector(commentModeSelector); if (!isCommentMode) return null; return ( - +
- +
); } diff --git a/app/client/src/comments/AppComments/AppCommentsHeader.tsx b/app/client/src/comments/AppComments/AppCommentsHeader.tsx index ca05440e40..728ef0d96c 100644 --- a/app/client/src/comments/AppComments/AppCommentsHeader.tsx +++ b/app/client/src/comments/AppComments/AppCommentsHeader.tsx @@ -18,8 +18,6 @@ const Header = styled.div` align-items: center; border-bottom: 1px solid ${(props) => props.theme.colors.comments.appCommentsHeaderBorder}; - border-right: 1px solid - ${(props) => props.theme.colors.comments.appCommentsHeaderBorder}; `; function AppCommentsHeader() { diff --git a/app/client/src/comments/CommentThread/CommentThread.tsx b/app/client/src/comments/CommentThread/CommentThread.tsx index 435ebfb1d4..d99dc697dc 100644 --- a/app/client/src/comments/CommentThread/CommentThread.tsx +++ b/app/client/src/comments/CommentThread/CommentThread.tsx @@ -42,7 +42,7 @@ const ThreadContainer = styled(animated.div).withConfig({ pinned?: boolean; maxHeight: string; }>` - width: 280px; + width: ${(props) => (props.inline ? "280px" : "100%")}; max-width: 100%; background-color: ${(props) => props.inline diff --git a/app/client/src/components/ads/DialogComponent.tsx b/app/client/src/components/ads/DialogComponent.tsx index 110a31bc41..455d632f87 100644 --- a/app/client/src/components/ads/DialogComponent.tsx +++ b/app/client/src/components/ads/DialogComponent.tsx @@ -94,10 +94,6 @@ const HeaderIconWrapper = styled.div<{ bgColor?: string }>` background: ${(props) => props.bgColor || props.theme.colors.modal.iconBg}; `; -const TriggerWrapper = styled.div` - margin-right: 4px; -`; - type DialogComponentProps = { isOpen?: boolean; canOutsideClickClose?: boolean; @@ -153,7 +149,7 @@ export function DialogComponent(props: DialogComponentProps) { return ( <> {props.trigger && ( - { setIsOpen(true); @@ -161,7 +157,7 @@ export function DialogComponent(props: DialogComponentProps) { style={{ zIndex: props.triggerZIndex }} > {props.trigger} - + )} void; toggleVisibility?: (index: number) => void; onEdit?: (index: number) => void; + updateFocus?: (index: number, isFocused: boolean) => void; }; interface DroppableComponentProps { @@ -23,6 +24,7 @@ interface DroppableComponentProps { toggleVisibility?: (index: number) => void; updateItems: (items: Array>) => void; onEdit?: (index: number) => void; + updateFocus?: (index: number, isFocused: boolean) => void; } export class DroppableComponent extends React.Component< @@ -46,6 +48,7 @@ export class DroppableComponent extends React.Component< id: item.id, label: item.label, isVisible: item.isVisible, + isDuplicateLabel: item.isDuplicateLabel, }; } @@ -60,11 +63,13 @@ export class DroppableComponent extends React.Component< onEdit, renderComponent, toggleVisibility, + updateFocus, updateOption, } = this.props; return renderComponent({ deleteOption, + updateFocus, updateOption, toggleVisibility, onEdit, diff --git a/app/client/src/components/ads/EditableTextSubComponent.tsx b/app/client/src/components/ads/EditableTextSubComponent.tsx index 0fbb39be1b..e6d8d591e5 100644 --- a/app/client/src/components/ads/EditableTextSubComponent.tsx +++ b/app/client/src/components/ads/EditableTextSubComponent.tsx @@ -101,16 +101,6 @@ const TextContainer = styled.div<{ display: none; } - &&& - .${BlueprintClasses.EDITABLE_TEXT_CONTENT}, - &&& - .${BlueprintClasses.EDITABLE_TEXT_INPUT} { - font-size: ${(props) => props.theme.typography.p1.fontSize}px; - line-height: ${(props) => props.theme.typography.p1.lineHeight}px; - letter-spacing: ${(props) => props.theme.typography.p1.letterSpacing}px; - font-weight: ${(props) => props.theme.typography.p1.fontWeight}; - } - &&& .${BlueprintClasses.EDITABLE_TEXT_CONTENT} { cursor: pointer; color: ${(props) => props.theme.colors.editableText.color}; @@ -119,6 +109,7 @@ const TextContainer = styled.div<{ ${(props) => (props.isEditing ? "display: none" : "display: block")}; width: fit-content !important; min-width: auto !important; + line-height: inherit !important; } &&& .${BlueprintClasses.EDITABLE_TEXT_CONTENT}:hover { @@ -142,9 +133,6 @@ const TextContainer = styled.div<{ &&& .${BlueprintClasses.EDITABLE_TEXT} { overflow: hidden; - height: ${(props) => props.theme.spaces[14] + 1}px; - padding: ${(props) => props.theme.spaces[4]}px - ${(props) => props.theme.spaces[5]}px; background-color: ${(props) => props.bgColor}; width: calc(100% - 40px); } diff --git a/app/client/src/components/ads/EmojiReactions.tsx b/app/client/src/components/ads/EmojiReactions.tsx index 6694fa669b..6f50f80ee0 100644 --- a/app/client/src/components/ads/EmojiReactions.tsx +++ b/app/client/src/components/ads/EmojiReactions.tsx @@ -37,7 +37,7 @@ const Bubble = styled.div<{ active?: boolean }>` : "transparent"}; border-radius: ${(props) => `${props.theme.radii[4]}px`}; - margin-right: ${(props) => `${props.theme.radii[1]}px`}; + margin-right: ${(props) => `${props.theme.radii[1]}px`}; & span.emoji { /* diff --git a/app/client/src/components/ads/TextInput.tsx b/app/client/src/components/ads/TextInput.tsx index b47a9eb7e5..755a27f8a1 100644 --- a/app/client/src/components/ads/TextInput.tsx +++ b/app/client/src/components/ads/TextInput.tsx @@ -213,8 +213,9 @@ const InputWrapper = styled.div<{ width: ${(props) => props.fill ? "100%" : props.width ? props.width : "260px"}; height: ${(props) => props.height || "36px"}; - border: 1.2px solid ${(props) => - props.noBorder ? "transparent" : props.inputStyle.borderColor}; + border: 1.2px solid + ${(props) => + props.noBorder ? "transparent" : props.inputStyle.borderColor}; background-color: ${(props) => props.inputStyle.bgColor}; color: ${(props) => props.inputStyle.color}; ${(props) => diff --git a/app/client/src/components/ads/Toast.tsx b/app/client/src/components/ads/Toast.tsx index 8343675fdc..77484d5fc4 100644 --- a/app/client/src/components/ads/Toast.tsx +++ b/app/client/src/components/ads/Toast.tsx @@ -1,5 +1,10 @@ import React from "react"; -import { CommonComponentProps, Classes, Variant } from "./common"; +import { + CommonComponentProps, + Classes, + Variant, + ToastTypeOptions, +} from "./common"; import styled from "styled-components"; import Icon, { IconSize } from "./Icon"; import Text, { TextType } from "./Text"; @@ -207,7 +212,8 @@ export const Toaster = { } if (config.variant && !Object.values(Variant).includes(config.variant)) { log.error( - "Toast type needs to be a one of " + Object.values(Variant).join(", "), + "Toast type needs to be a one of " + + Object.values(ToastTypeOptions).join(", "), ); return; } diff --git a/app/client/src/components/ads/TreeDropdown.tsx b/app/client/src/components/ads/TreeDropdown.tsx index 099fb97b61..9d933ea55e 100644 --- a/app/client/src/components/ads/TreeDropdown.tsx +++ b/app/client/src/components/ads/TreeDropdown.tsx @@ -58,7 +58,7 @@ const StyledMenu = styled(Menu)` .${Classes.MENU_ITEM} { border-radius: 0px; font-size: 14px; - line-height: ${(props) => props.theme.typography.p2.lineHeight}px; + line-height: ${(props) => props.theme.typography.p1.lineHeight}px; display: flex; align-items: center; height: 30px; diff --git a/app/client/src/components/ads/common.tsx b/app/client/src/components/ads/common.tsx index 34fabc7ae7..0230e4740e 100644 --- a/app/client/src/components/ads/common.tsx +++ b/app/client/src/components/ads/common.tsx @@ -75,6 +75,13 @@ export enum Variant { danger = "danger", } +export enum ToastTypeOptions { + success = "success", + info = "info", + warning = "warning", + error = "error", +} + export const ToastVariant = (type: any) => { let variant: Variant; switch (type) { diff --git a/app/client/src/components/editorComponents/ActionRightPane/index.tsx b/app/client/src/components/editorComponents/ActionRightPane/index.tsx index a029818a41..baae274c65 100644 --- a/app/client/src/components/editorComponents/ActionRightPane/index.tsx +++ b/app/client/src/components/editorComponents/ActionRightPane/index.tsx @@ -39,8 +39,8 @@ const SideBar = styled.div` overflow: auto; height: 100%; width: 100%; - -webkit-animation: slide-left 0.2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; - animation: slide-left 0.2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; + -webkit-animation: slide-left 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + animation: slide-left 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; & > div { margin-top: ${(props) => props.theme.spaces[11]}px; @@ -85,7 +85,6 @@ const SideBar = styled.div` transform: translateX(0); } } - `; const Label = styled.span` diff --git a/app/client/src/components/editorComponents/Button.tsx b/app/client/src/components/editorComponents/Button.tsx index 1aea692683..cd28854156 100644 --- a/app/client/src/components/editorComponents/Button.tsx +++ b/app/client/src/components/editorComponents/Button.tsx @@ -26,10 +26,6 @@ const outline = css` const buttonStyles = css>` ${BlueprintButtonIntentsCSS} &&&& { - padding: ${(props) => - props.filled || props.outline - ? props.theme.spaces[2] + "px " + props.theme.spaces[3] + "px" - : 0}; border-radius: 0; background: ${(props) => props.filled || props.outline ? "inherit" : "transparent"}; diff --git a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx index 7e703e4300..bdc7a53221 100644 --- a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx @@ -163,7 +163,11 @@ function CollapseToggle(props: { isOpen: boolean }) { } function copyContent(content: any) { - copy(content); + const stringifiedContent = _.isString(content) + ? content + : JSON.stringify(content, null, 2); + + copy(stringifiedContent); Toaster.show({ text: `Evaluated value copied to clipboard`, variant: Variant.success, diff --git a/app/client/src/components/editorComponents/CodeEditor/hintHelpers.ts b/app/client/src/components/editorComponents/CodeEditor/hintHelpers.ts index e37ea7b591..d1ecf40019 100644 --- a/app/client/src/components/editorComponents/CodeEditor/hintHelpers.ts +++ b/app/client/src/components/editorComponents/CodeEditor/hintHelpers.ts @@ -11,6 +11,8 @@ export const bindingHint: HintHelper = (editor, dataTree, customDataTree) => { if (customDataTree) { const customTreeDef = customTreeTypeDefCreator(customDataTree); TernServer.updateDef("customDataTree", customTreeDef); + } else { + TernServer.updateDef("customDataTree", {}); } editor.setOption("extraKeys", { diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx index 1d161c4188..48a9bc286c 100644 --- a/app/client/src/components/editorComponents/CodeEditor/index.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx @@ -299,6 +299,9 @@ class CodeEditor extends Component { } componentWillUnmount() { + // return if component unmounts before editor is created + if (!this.editor) return; + this.editor.off("beforeChange", this.handleBeforeChange); this.editor.off("change", _.debounce(this.handleChange, 600)); this.editor.off("keyup", this.handleAutocompleteKeyup); diff --git a/app/client/src/components/editorComponents/Debugger/DebugCTA.tsx b/app/client/src/components/editorComponents/Debugger/DebugCTA.tsx index afb83057ae..3db99e2883 100644 --- a/app/client/src/components/editorComponents/Debugger/DebugCTA.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebugCTA.tsx @@ -87,25 +87,26 @@ export function EvaluatedValueDebugButton(props: { } const StyledButton = styled(Button)` - && { - width: fit-content; - margin-top: 4px; - text-transform: none; - height: 26px; - ${(props) => getTypographyByKey(props, "p2")} - .${Classes.ICON} { - margin-right: 5px; - } - &:hover { + && { + width: fit-content; + margin-top: 4px; + text-transform: none; + height: 26px; + ${(props) => getTypographyByKey(props, "p2")} .${Classes.ICON} { margin-right: 5px; } - } + &:hover { + .${Classes.ICON} { + margin-right: 5px; + } + } - svg, svg path{ - fill: ${Colors.WHITE}; + svg, + svg path { + fill: ${Colors.WHITE}; + } } - } `; type DebugCTAProps = { diff --git a/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx b/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx index 0a65ded022..77bb462577 100644 --- a/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx @@ -27,7 +27,7 @@ const Container = styled.div` height: 25%; min-height: ${TABS_HEADER_HEIGHT}px; background-color: ${(props) => props.theme.colors.debugger.background}; - border: 1px solid ${Colors.ALTO}; + border-top: 1px solid ${Colors.ALTO}; ul.react-tabs__tab-list { padding: 0px ${(props) => props.theme.spaces[12]}px; diff --git a/app/client/src/components/editorComponents/Debugger/LogItem.tsx b/app/client/src/components/editorComponents/Debugger/LogItem.tsx index 3d809db4bc..237cd32f03 100644 --- a/app/client/src/components/editorComponents/Debugger/LogItem.tsx +++ b/app/client/src/components/editorComponents/Debugger/LogItem.tsx @@ -100,7 +100,6 @@ const Wrapper = styled.div<{ collapsed: boolean }>` .debugger-toggle { ${(props) => props.collapsed && `transform: rotate(-90deg);`} margin-left: -5px; - margin-top: } .debugger-label { diff --git a/app/client/src/components/editorComponents/Debugger/Resizer/index.tsx b/app/client/src/components/editorComponents/Debugger/Resizer/index.tsx index 98ed9f4cb8..a5b2030049 100644 --- a/app/client/src/components/editorComponents/Debugger/Resizer/index.tsx +++ b/app/client/src/components/editorComponents/Debugger/Resizer/index.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, RefObject } from "react"; import styled, { css } from "styled-components"; export const ResizerCSS = css` - width: calc(100vw - ${(props) => props.theme.sidebarWidth}); + width: 100%; z-index: ${Layers.debugger}; position: relative; `; diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 3b9abcb9f2..d0867b5993 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -10,7 +10,10 @@ import { useWidgetDragResize, } from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; -import { snipingModeSelector } from "selectors/editorSelectors"; +import { + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; const DraggableWrapper = styled.div` @@ -52,13 +55,15 @@ export const canDrag = ( props: any, isCommentMode: boolean, isSnipingMode: boolean, + isPreviewMode: boolean, ) => { return ( !isResizingOrDragging && !isDraggingDisabled && !props?.dragDisabled && !isCommentMode && - !isSnipingMode + !isSnipingMode && + !isPreviewMode ); }; @@ -68,6 +73,7 @@ function DraggableComponent(props: DraggableComponentProps) { const isCommentMode = useSelector(commentModeSelector); const isSnipingMode = useSelector(snipingModeSelector); + const isPreviewMode = useSelector(previewModeSelector); // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean const { setDraggingCanvas, setDraggingState } = useWidgetDragResize(); @@ -141,6 +147,7 @@ function DraggableComponent(props: DraggableComponentProps) { props, isCommentMode, isSnipingMode, + isPreviewMode, ); const className = `${classNameForTesting}`; const draggableRef = useRef(null); diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 68be1ae2d2..6302b0e14d 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -45,22 +45,11 @@ const StyledDropTarget = styled.div` z-index: 1; `; -const StyledOnboardingWrapper = styled.div` - position: fixed; - left: 50%; - top: 50vh; -`; -const StyledOnboardingMessage = styled.h2` - color: #ccc; -`; - function Onboarding() { return ( - - - Drag and drop a widget here - - +

+ Drag and drop a widget here +

); } @@ -166,7 +155,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { : "100%"; const boxShadow = (isResizing || isDragging) && props.widgetId === MAIN_CONTAINER_WIDGET_ID - ? "0px 0px 0px 1px #DDDDDD" + ? "inset 0px 0px 0px 1px #DDDDDD" : "0px 0px 0px 1px transparent"; const dropTargetRef = useRef(null); diff --git a/app/client/src/components/editorComponents/DropTargetUtils.ts b/app/client/src/components/editorComponents/DropTargetUtils.ts index 9b9b07e25f..1896e5671d 100644 --- a/app/client/src/components/editorComponents/DropTargetUtils.ts +++ b/app/client/src/components/editorComponents/DropTargetUtils.ts @@ -1,4 +1,4 @@ -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults } from "constants/WidgetConstants"; export const calculateDropTargetRows = ( diff --git a/app/client/src/components/editorComponents/EditorContextProvider.tsx b/app/client/src/components/editorComponents/EditorContextProvider.tsx index 941458e7cf..e5a6fa569c 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.tsx @@ -13,7 +13,7 @@ import { } from "actions/controlActions"; import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionConstants"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { resetChildrenMetaProperty, diff --git a/app/client/src/components/editorComponents/GlobalSearch/SnippetRefinements.tsx b/app/client/src/components/editorComponents/GlobalSearch/SnippetRefinements.tsx index 43b1b5a06c..ee2b7ab85f 100644 --- a/app/client/src/components/editorComponents/GlobalSearch/SnippetRefinements.tsx +++ b/app/client/src/components/editorComponents/GlobalSearch/SnippetRefinements.tsx @@ -19,6 +19,8 @@ const RefinementListContainer = styled.div` justify-content: flex-start; flex-wrap: wrap; .refinement-pill { + display: flex; + align-items: center; margin: 2px 5px 0; padding: 5px; color: ${(props) => props.theme.colors.globalSearch.primaryTextColor}; diff --git a/app/client/src/components/editorComponents/GlobalSearch/SnippetsFilter.tsx b/app/client/src/components/editorComponents/GlobalSearch/SnippetsFilter.tsx index 359df04300..287749bb38 100644 --- a/app/client/src/components/editorComponents/GlobalSearch/SnippetsFilter.tsx +++ b/app/client/src/components/editorComponents/GlobalSearch/SnippetsFilter.tsx @@ -160,14 +160,19 @@ function SnippetsFilter({ refinements, snippetsEmpty }: any) { showFilter={showSnippetFilter} snippetsEmpty={snippetsEmpty} > -
diff --git a/app/client/src/components/editorComponents/JSResponseView.tsx b/app/client/src/components/editorComponents/JSResponseView.tsx index 7758aaf978..cf7e6b09a7 100644 --- a/app/client/src/components/editorComponents/JSResponseView.tsx +++ b/app/client/src/components/editorComponents/JSResponseView.tsx @@ -214,7 +214,7 @@ function JSResponseView(props: Props) { {sortedActionList && !sortedActionList?.length ? ( - + {createMessage(EMPTY_JS_OBJECT)} ) : ( @@ -248,7 +248,7 @@ function JSResponseView(props: Props) { ) : !responses.hasOwnProperty(selectActionId) ? ( - + {EMPTY_RESPONSE_FIRST_HALF()} {EMPTY_RESPONSE_LAST_HALF()} diff --git a/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx b/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx new file mode 100644 index 0000000000..dd9b67c7a6 --- /dev/null +++ b/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx @@ -0,0 +1,116 @@ +import { get, compact } from "lodash"; +import classNames from "classnames"; +import * as Sentry from "@sentry/react"; +import { useSelector } from "react-redux"; +import React, { memo, useEffect, useRef, useMemo } from "react"; + +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; +import { getSelectedWidgets } from "selectors/ui"; +import { tailwindLayers } from "constants/Layers"; +import WidgetPropertyPane from "pages/Editor/PropertyPane"; +import { previewModeSelector } from "selectors/editorSelectors"; +import CanvasPropertyPane from "pages/Editor/CanvasPropertyPane"; +import useHorizontalResize from "utils/hooks/useHorizontalResize"; +import { commentModeSelector } from "selectors/commentsSelectors"; +import { getIsDraggingForSelection } from "selectors/canvasSelectors"; +import MultiSelectPropertyPane from "pages/Editor/MultiSelectPropertyPane"; +import { getWidgets } from "sagas/selectors"; + +type Props = { + width: number; + onDragEnd?: () => void; + onWidthChange: (width: number) => void; +}; + +export const PropertyPaneSidebar = memo((props: Props) => { + const sidebarRef = useRef(null); + const { + onMouseDown, + onMouseUp, + onTouchStart, + resizing, + } = useHorizontalResize( + sidebarRef, + props.onWidthChange, + props.onDragEnd, + true, + ); + const canvasWidgets = useSelector(getWidgets); + const isPreviewMode = useSelector(previewModeSelector); + const isCommentMode = useSelector(commentModeSelector); + const selectedWidgetIds = useSelector(getSelectedWidgets); + const selectedWidgets = useMemo( + () => + compact( + selectedWidgetIds.map((widgetId) => get(canvasWidgets, widgetId)), + ), + [canvasWidgets, selectedWidgetIds], + ); + const isDraggingForSelection = useSelector(getIsDraggingForSelection); + + PerformanceTracker.startTracking(PerformanceTransactionName.SIDE_BAR_MOUNT); + useEffect(() => { + PerformanceTracker.stopTracking(); + }); + + /** + * renders the property pane: + * 1. if no widget is selected -> CanvasPropertyPane + * 2. if more than one widget is selected -> MultiWidgetPropertyPane + * 3. if user is dragging for selection -> CanvasPropertyPane + * 4. if only one widget is selected -> WidgetPropertyPane + */ + const propertyPane = useMemo(() => { + switch (true) { + case selectedWidgets.length == 0: + return ; + case selectedWidgets.length > 1: + return ; + case selectedWidgets.length === 1: + return ; + default: + return ; + } + }, [selectedWidgets.length, isDraggingForSelection]); + + return ( +
+ {/* RESIZOR */} +
+
+
+ {/* PROPERTY PANE */} +
+
+ {propertyPane} +
+
+
+ ); +}); + +PropertyPaneSidebar.displayName = "PropertyPaneSidebar"; + +export default Sentry.withProfiler(PropertyPaneSidebar); diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 9615d565e9..db2e41d463 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -38,7 +38,10 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { scrollElementIntoParentCanvasView } from "utils/helpers"; import { getNearestParentCanvas } from "utils/generators"; -import { getOccupiedSpaces } from "selectors/editorSelectors"; +import { + getOccupiedSpaces, + previewModeSelector, +} from "selectors/editorSelectors"; import { commentModeSelector } from "selectors/commentsSelectors"; import { snipingModeSelector } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; @@ -63,6 +66,7 @@ export const ResizableComponent = memo(function ResizableComponent( const isCommentMode = useSelector(commentModeSelector); const isSnipingMode = useSelector(snipingModeSelector); + const isPreviewMode = useSelector(previewModeSelector); const showPropertyPane = useShowPropertyPane(); const showTableFilterPane = useShowTableFilterPane(); @@ -318,7 +322,8 @@ export const ResizableComponent = memo(function ResizableComponent( isWidgetFocused && !props.resizeDisabled && !isCommentMode && - !isSnipingMode; + !isSnipingMode && + !isPreviewMode; const isMultiSelectedWidget = selectedWidgets && selectedWidgets.length > 1 && diff --git a/app/client/src/components/editorComponents/Sidebar.tsx b/app/client/src/components/editorComponents/Sidebar.tsx index dd2e0d4288..a7abd6ee62 100644 --- a/app/client/src/components/editorComponents/Sidebar.tsx +++ b/app/client/src/components/editorComponents/Sidebar.tsx @@ -1,65 +1,78 @@ -import React, { memo, useEffect, useState } from "react"; -import styled from "styled-components"; -import ExplorerSidebar from "pages/Editor/Explorer"; -import { PanelStack, Classes } from "@blueprintjs/core"; -import { Colors } from "constants/Colors"; +import React, { + memo, + useEffect, + useRef, + useCallback, + useState, + useMemo, +} from "react"; +import classNames from "classnames"; +import history from "utils/history"; import * as Sentry from "@sentry/react"; +import { PanelStack } from "@blueprintjs/core"; +import { useDispatch, useSelector } from "react-redux"; + import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { Layers } from "constants/Layers"; -import { useSelector } from "store"; -import { - getFirstTimeUserOnboardingComplete, - getIsFirstTimeUserOnboardingEnabled, -} from "selectors/onboardingSelectors"; -import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar"; -import Switcher from "components/ads/Switcher"; -import { useDispatch } from "react-redux"; -import { forceOpenWidgetPanel } from "actions/widgetSidebarActions"; -import { AppState } from "reducers"; import { getCurrentApplicationId, getCurrentPageId, } from "selectors/editorSelectors"; -import { BUILDER_PAGE_URL } from "constants/routes"; -import history from "utils/history"; - +import { AppState } from "reducers"; +import { + getFirstTimeUserOnboardingComplete, + getIsFirstTimeUserOnboardingEnabled, +} from "selectors/onboardingSelectors"; +import Explorer from "pages/Editor/Explorer"; +import Switcher from "components/ads/Switcher"; import { trimQueryString } from "utils/helpers"; +import { BUILDER_PAGE_URL } from "constants/routes"; +import AppComments from "comments/AppComments/AppComments"; +import { + setExplorerActiveAction, + setExplorerPinnedAction, +} from "actions/explorerActions"; +import { + getExplorerActive, + getExplorerPinned, +} from "selectors/explorerSelector"; +import { tailwindLayers } from "constants/Layers"; +import TooltipComponent from "components/ads/Tooltip"; +import { previewModeSelector } from "selectors/editorSelectors"; +import useHorizontalResize from "utils/hooks/useHorizontalResize"; +import { forceOpenWidgetPanel } from "actions/widgetSidebarActions"; +import { ReactComponent as PinIcon } from "assets/icons/ads/double-arrow-left.svg"; import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions"; +import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar"; +import { createMessage, ENTITY_EXPLORER_TITLE } from "constants/messages"; -const SidebarWrapper = styled.div<{ inOnboarding: boolean }>` - background-color: ${Colors.WHITE}; - padding: 0; - width: ${(props) => props.theme.sidebarWidth}; - z-index: ${Layers.sideBar}; - box-shadow: 1px 0px 0px ${Colors.MERCURY_1}; - color: ${(props) => props.theme.colors.textOnWhiteBG}; - overflow-y: auto; - & .${Classes.PANEL_STACK} { - height: ${(props) => - props.inOnboarding - ? `calc(100% - ${props.theme.onboarding.statusBarHeight}px - 48px)` - : `calc(100% - 48px)`}; - .${Classes.PANEL_STACK_VIEW} { - background: none; - } - } -`; +type Props = { + width: number; + onWidthChange?: (width: number) => void; + onDragEnd?: () => void; +}; -const SwitchWrapper = styled.div` - padding: 8px; -`; - -const initialPanel = { component: ExplorerSidebar }; - -export const Sidebar = memo(() => { +export const EntityExplorerSidebar = memo((props: Props) => { + let tooltipTimeout: number; const dispatch = useDispatch(); - const applicationId = useSelector(getCurrentApplicationId); + const active = useSelector(getExplorerActive); const pageId = useSelector(getCurrentPageId); + const sidebarRef = useRef(null); + const pinned = useSelector(getExplorerPinned); + const isPreviewMode = useSelector(previewModeSelector); + const applicationId = useSelector(getCurrentApplicationId); + const enableFirstTimeUserOnboarding = useSelector( + getIsFirstTimeUserOnboardingEnabled, + ); const isFirstTimeUserOnboardingEnabled = useSelector( getIsFirstTimeUserOnboardingEnabled, ); + const resizer = useHorizontalResize( + sidebarRef, + props.onWidthChange, + props.onDragEnd, + ); const switches = [ { id: "explorer", @@ -92,14 +105,10 @@ export const Sidebar = memo(() => { }, ]; const [activeSwitch, setActiveSwitch] = useState(switches[0]); - + const [tooltipIsOpen, setTooltipIsOpen] = useState(false); const isForceOpenWidgetPanel = useSelector( (state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel, ); - - const enableFirstTimeUserOnboarding = useSelector( - getIsFirstTimeUserOnboardingEnabled, - ); const isFirstTimeUserOnboardingComplete = useSelector( getFirstTimeUserOnboardingComplete, ); @@ -116,24 +125,185 @@ export const Sidebar = memo(() => { } }, [isForceOpenWidgetPanel]); - return ( - { + document.addEventListener("mousemove", onMouseMove); + + return () => { + document.removeEventListener("mousemove", onMouseMove); + }; + }, [active, pinned, resizer.resizing]); + + /** + * passing the event to touch move on mouse move + * + * @param event + */ + const onMouseMove = (event: MouseEvent) => { + const eventWithTouches = Object.assign({}, event, { + touches: [{ clientX: event.clientX, clientY: event.clientY }], + }); + onTouchMove(eventWithTouches); + }; + + /** + * calculate the new width based on the pixel moved + * + * @param event + */ + const onTouchMove = ( + event: + | TouchEvent + | (MouseEvent & { touches: { clientX: number; clientY: number }[] }), + ) => { + const currentX = event.touches[0].clientX; + + // only calculate the following in unpin mode + if (!pinned) { + if (active) { + // if user cursor is out of the entity explorer width ( with some extra window = 20px ), make the + // entity explorer inactive. Also, 20px here is to increase the window in which a user can drag the resizer + if (currentX >= props.width + 20 && !resizer.resizing) { + dispatch(setExplorerActiveAction(false)); + } + } else { + // check if user cursor is at extreme left when the explorer is inactive, if yes, make the explorer active + if (currentX <= 5) { + dispatch(setExplorerActiveAction(true)); + } } + } + }; + + /** + * toggles the pinned state of sidebar + */ + const onPin = useCallback(() => { + dispatch(setExplorerPinnedAction(!pinned)); + }, [pinned, dispatch, setExplorerPinnedAction]); + + /** + * on hover of resizer, show tooltip + */ + const onHoverResizer = useCallback(() => { + tooltipTimeout = setTimeout(() => { + setTooltipIsOpen(true); + }, 250); + }, [setTooltipIsOpen]); + + /** + * on hover end of resizer, hide tooltip + */ + const onHoverEndResizer = useCallback(() => { + clearTimeout(tooltipTimeout); + setTooltipIsOpen(false); + }, [setTooltipIsOpen]); + + /** + * resizer left position + */ + const resizerLeft = useMemo(() => { + return !pinned && !active ? 0 : props.width; + }, [pinned, active, props.width]); + + return ( +
- {(enableFirstTimeUserOnboarding || isFirstTimeUserOnboardingComplete) && ( - - )} - - - - - + {/* SIDEBAR */} +
+ {(enableFirstTimeUserOnboarding || + isFirstTimeUserOnboardingComplete) && } + {/* ENTITY EXPLORE HEADER */} +
+

+ {createMessage(ENTITY_EXPLORER_TITLE)} +

+
+ + Close sidebar + Ctrl + / +
+ } + > + + +
+
+ {/* SWITCHER */} +
+ +
+ + +
+ {/* RESIZER */} +
+
+ + Drag to resize +
+ } + hoverOpenDelay={200} + isOpen={tooltipIsOpen && !resizer.resizing} + position="right" + > +
+ +
+
+
); }); -Sidebar.displayName = "Sidebar"; +EntityExplorerSidebar.displayName = "EntityExplorerSidebar"; -export default Sentry.withProfiler(Sidebar); +export default Sentry.withProfiler(EntityExplorerSidebar); diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx index dacc2308e8..20aff66a90 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx @@ -93,7 +93,7 @@ const getStyles = ( }; case Activities.SELECTED: return { - background: Colors.OUTER_SPACE, + background: Colors.JAFFA_DARK, color: Colors.WHITE, }; } diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index d6941823a2..036ce46e52 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -4,10 +4,7 @@ import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { PropertyPaneReduxState } from "reducers/uiReducers/propertyPaneReducer"; import SettingsControl, { Activities } from "./SettingsControl"; -import { - useShowPropertyPane, - useShowTableFilterPane, -} from "utils/hooks/dragResizeHooks"; +import { useShowTableFilterPane } from "utils/hooks/dragResizeHooks"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { WidgetType } from "constants/WidgetConstants"; import PerformanceTracker, { @@ -18,7 +15,10 @@ import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import WidgetFactory from "utils/WidgetFactory"; const WidgetTypes = WidgetFactory.widgetTypes; -import { snipingModeSelector } from "selectors/editorSelectors"; +import { + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; import { bindDataToWidget } from "../../../actions/propertyPaneActions"; import { hideErrors } from "selectors/debuggerSelectors"; import { commentModeSelector } from "../../../selectors/commentsSelectors"; @@ -56,10 +56,10 @@ type WidgetNameComponentProps = { }; export function WidgetNameComponent(props: WidgetNameComponentProps) { - const showPropertyPane = useShowPropertyPane(); const dispatch = useDispatch(); const isCommentMode = useSelector(commentModeSelector); const isSnipingMode = useSelector(snipingModeSelector); + const isPreviewMode = useSelector(previewModeSelector); const showTableFilterPane = useShowTableFilterPane(); // Dispatch hook handy to set a widget as focused/selected const { selectWidget } = useWidgetSelection(); @@ -111,14 +111,12 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { }); // hide table filter pane if open isTableFilterPaneVisible && showTableFilterPane && showTableFilterPane(); - showPropertyPane && showPropertyPane(props.widgetId, undefined, true); selectWidget && selectWidget(props.widgetId); } else { AnalyticsUtil.logEvent("PROPERTY_PANE_CLOSE_CLICK", { widgetType: props.type, widgetId: props.widgetId, }); - showPropertyPane && showPropertyPane(); } e.preventDefault(); @@ -135,6 +133,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { const shouldShowWidgetName = () => { return ( !isCommentMode && + !isPreviewMode && !isMultiSelectedWidget && (isSnipingMode ? focusedWidget === props.widgetId diff --git a/app/client/src/components/propertyControls/ButtonListControl.tsx b/app/client/src/components/propertyControls/ButtonListControl.tsx index c6f1f8a30c..496e30e5ef 100644 --- a/app/client/src/components/propertyControls/ButtonListControl.tsx +++ b/app/client/src/components/propertyControls/ButtonListControl.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useEffect, useState } from "react"; import BaseControl, { ControlProps } from "./BaseControl"; import { - StyledInputGroup, StyledPropertyPaneButton, StyledDragIcon, StyledDeleteIcon, StyledEditIcon, + StyledOptionControlInputGroup, } from "./StyledControls"; import styled from "constants/DefaultTheme"; import { generateReactKey } from "utils/generators"; @@ -34,27 +34,6 @@ const ButtonListWrapper = styled.div` flex-direction: column; `; -const StyledOptionControlInputGroup = styled(StyledInputGroup)` - margin-right: 2px; - margin-bottom: 2px; - width: 100%; - padding-left: 30px; - padding-right: 60px; - text-overflow: ellipsis; - &&& { - input { - border: none; - color: ${(props) => props.theme.colors.propertyPane.radioGroupText}; - background: ${(props) => props.theme.colors.propertyPane.radioGroupBg}; - &:focus { - border: none; - color: ${(props) => props.theme.colors.textOnDarkBG}; - background: ${(props) => props.theme.colors.paneInputBG}; - } - } - } -`; - const AddNewButton = styled(StyledPropertyPaneButton)` justify-content: center; flex-grow: 1; diff --git a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx index 567e026a33..5ab0d7d765 100644 --- a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx +++ b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx @@ -1,4 +1,9 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState, Component } from "react"; +import { AppState } from "reducers"; +import { connect } from "react-redux"; +import { Placement } from "popper.js"; +import * as Sentry from "@sentry/react"; +import _ from "lodash"; import BaseControl, { ControlProps } from "./BaseControl"; import { StyledDragIcon, @@ -10,22 +15,38 @@ import { StyledOptionControlInputGroup, } from "./StyledControls"; import styled from "constants/DefaultTheme"; +import { Indices } from "constants/Layers"; import { DroppableComponent } from "components/ads/DraggableListComponent"; -import { ColumnProperties } from "widgets/TableWidget/component/Constants"; +import { Size, Category } from "components/ads/Button"; import EmptyDataState from "components/utils/EmptyDataState"; -import { getNextEntityName } from "utils/AppsmithUtils"; +import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup"; +import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; +import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; +import { ColumnProperties } from "widgets/TableWidget/component/Constants"; import { getDefaultColumnProperties, getTableStyles, } from "widgets/TableWidget/component/TableUtilities"; -import { debounce } from "lodash"; -import { Size, Category } from "components/ads/Button"; import { reorderColumns } from "widgets/TableWidget/component/TableHelpers"; +import { DataTree } from "entities/DataTree/dataTreeFactory"; +import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors"; +import { + EvaluationError, + getEvalErrorPath, + getEvalValuePath, + PropertyEvaluationErrorType, +} from "utils/DynamicBindingUtils"; +import { getNextEntityName } from "utils/AppsmithUtils"; +import { Colors } from "constants/Colors"; +import { noop } from "utils/AppsmithUtils"; const ItemWrapper = styled.div` display: flex; justify-content: flex-start; align-items: center; + &.has-duplicate-label > div:nth-child(2) { + border: 1px solid ${Colors.DANGER_SOLID}; + } `; const TabsWrapper = styled.div` @@ -44,13 +65,33 @@ const AddColumnButton = styled(StyledPropertyPaneButton)` } `; +interface ReduxStateProps { + dynamicData: DataTree; + datasources: any; +} + +type EvaluatedValuePopupWrapperProps = ReduxStateProps & { + isFocused: boolean; + theme: EditorTheme; + popperPlacement?: Placement; + popperZIndex?: Indices; + dataTreePath?: string; + evaluatedValue?: any; + expected?: CodeEditorExpected; + hideEvaluatedValue?: boolean; + useValidationMessage?: boolean; + children: JSX.Element; +}; + type RenderComponentProps = { index: number; item: { label: string; isDerived?: boolean; isVisible?: boolean; + isDuplicateLabel?: boolean; }; + updateFocus?: (index: number, isFocused: boolean) => void; updateOption: (index: number, value: string) => void; onEdit?: (index: number) => void; deleteOption: (index: number) => void; @@ -84,10 +125,12 @@ function ColumnControlComponent(props: RenderComponentProps) { item, onEdit, toggleVisibility, + updateFocus, updateOption, } = props; const [visibility, setVisibility] = useState(item.isVisible); - const debouncedUpdate = debounce(updateOption, 1000); + const debouncedUpdate = _.debounce(updateOption, 1000); + const debouncedFocus = updateFocus ? _.debounce(updateFocus, 400) : noop; const onChange = useCallback( (index: number, value: string) => { setValue(value); @@ -96,11 +139,19 @@ function ColumnControlComponent(props: RenderComponentProps) { [updateOption], ); - const onFocus = () => setEditing(true); - const onBlur = () => setEditing(false); + const onFocus = () => { + setEditing(true); + debouncedFocus(index, true); + }; + const onBlur = () => { + setEditing(false); + debouncedFocus(index, false); + }; return ( - + { +type State = { + focusedIndex: number | null; + duplicateColumnIds: string[]; +}; + +class PrimaryColumnsControl extends BaseControl { + constructor(props: ControlProps) { + super(props); + + const columns: Record = props.propertyValue || {}; + const columnOrder = Object.keys(columns); + const reorderedColumns = reorderColumns(columns, columnOrder); + const tableColumnLabels = _.map(reorderedColumns, "label"); + const duplicateColumnIds = []; + + for (let index = 0; index < tableColumnLabels.length; index++) { + const currLabel = tableColumnLabels[index] as string; + const duplicateValueIndex = tableColumnLabels.indexOf(currLabel); + if (duplicateValueIndex !== index) { + // get column id from columnOrder index + duplicateColumnIds.push(reorderedColumns[columnOrder[index]].id); + } + } + + this.state = { + focusedIndex: null, + duplicateColumnIds, + }; + } + render() { // Get columns from widget properties const columns: Record = @@ -183,22 +264,38 @@ class PrimaryColumnsControl extends BaseControl { isVisible: column.isVisible, isDerived: column.isDerived, index: column.index, + isDuplicateLabel: _.includes( + this.state.duplicateColumnIds, + column.id, + ), }; }, ); + const column: ColumnProperties | undefined = Object.values( + reorderedColumns, + ).find( + (column: ColumnProperties) => column.index === this.state.focusedIndex, + ); + // show popup on duplicate column label input focused + const isFocused = + !_.isNull(this.state.focusedIndex) && + _.includes(this.state.duplicateColumnIds, column?.id); return ( - + + + { propertiesToDelete.push(`columnOrder[${columnOrderIndex}]`); this.deleteProperties(propertiesToDelete); + // if column deleted, clean up duplicateIndexes + let duplicateColumnIds = [...this.state.duplicateColumnIds]; + duplicateColumnIds = duplicateColumnIds.filter( + (id) => id !== originalColumn.id, + ); + this.setState({ duplicateColumnIds }); } }; @@ -317,12 +420,118 @@ class PrimaryColumnsControl extends BaseControl { `${this.props.propertyName}.${originalColumn.id}.label`, updatedLabel, ); + // check entered label is unique or duplicate + const tableColumnLabels = _.map(columns, "label"); + let duplicateColumnIds = [...this.state.duplicateColumnIds]; + // if duplicate, add into array + if (_.includes(tableColumnLabels, updatedLabel)) { + duplicateColumnIds.push(originalColumn.id); + this.setState({ duplicateColumnIds }); + } else { + duplicateColumnIds = duplicateColumnIds.filter( + (id) => id !== originalColumn.id, + ); + this.setState({ duplicateColumnIds }); + } } }; + updateFocus = (index: number, isFocused: boolean) => { + this.setState({ focusedIndex: isFocused ? index : null }); + }; + static getControlType() { return "PRIMARY_COLUMNS"; } } export default PrimaryColumnsControl; + +/** + * wrapper component on dragable primary columns + * render popup if primary column labels are not unique + * show unique name error in PRIMARY_COLUMNS + */ +class EvaluatedValuePopupWrapperClass extends Component< + EvaluatedValuePopupWrapperProps +> { + getPropertyValidation = ( + dataTree: DataTree, + dataTreePath?: string, + ): { + isInvalid: boolean; + errors: EvaluationError[]; + pathEvaluatedValue: unknown; + } => { + if (!dataTreePath) { + return { + isInvalid: false, + errors: [], + pathEvaluatedValue: undefined, + }; + } + + const errors = _.get( + dataTree, + getEvalErrorPath(dataTreePath), + [], + ) as EvaluationError[]; + + const filteredLintErrors = errors.filter( + (error) => error.errorType !== PropertyEvaluationErrorType.LINT, + ); + + const pathEvaluatedValue = _.get(dataTree, getEvalValuePath(dataTreePath)); + + return { + isInvalid: filteredLintErrors.length > 0, + errors: filteredLintErrors, + pathEvaluatedValue, + }; + }; + + render = () => { + const { + dataTreePath, + dynamicData, + evaluatedValue, + expected, + hideEvaluatedValue, + useValidationMessage, + } = this.props; + const { + errors, + isInvalid, + pathEvaluatedValue, + } = this.getPropertyValidation(dynamicData, dataTreePath); + let evaluated = evaluatedValue; + if (dataTreePath) { + evaluated = pathEvaluatedValue; + } + + return ( + + {this.props.children} + + ); + }; +} +const mapStateToProps = (state: AppState): ReduxStateProps => ({ + dynamicData: getDataTreeForAutocomplete(state), + datasources: state.entities.datasources, +}); + +const EvaluatedValuePopupWrapper = Sentry.withProfiler( + connect(mapStateToProps)(EvaluatedValuePopupWrapperClass), +); diff --git a/app/client/src/constants/AppConstants.ts b/app/client/src/constants/AppConstants.ts index 90485b568e..e75a8c8fe9 100644 --- a/app/client/src/constants/AppConstants.ts +++ b/app/client/src/constants/AppConstants.ts @@ -4,6 +4,8 @@ export const CANVAS_DEFAULT_HEIGHT_PX = 1292; export const CANVAS_DEFAULT_GRID_HEIGHT_PX = 1; export const CANVAS_DEFAULT_GRID_WIDTH_PX = 1; export const CANVAS_BACKGROUND_COLOR = "#FFFFFF"; +export const DEFAULT_ENTITY_EXPLORER_WIDTH = 256; +export const DEFAULT_PROPERTY_PANE_WIDTH = 256; const APP_STORE_NAMESPACE = "APPSMITH_LOCAL_STORE"; diff --git a/app/client/src/constants/editorConstants.tsx b/app/client/src/constants/CanvasEditorConstants.tsx similarity index 51% rename from app/client/src/constants/editorConstants.tsx rename to app/client/src/constants/CanvasEditorConstants.tsx index 3762df9faf..b8db735cb2 100644 --- a/app/client/src/constants/editorConstants.tsx +++ b/app/client/src/constants/CanvasEditorConstants.tsx @@ -6,3 +6,10 @@ export type OccupiedSpace = { id: string; parentId?: string; }; + +export const zIndexLayers = { + PROPERTY_PANE: "z-3", + ENTITY_EXPLORER: "z-3", + RESIZER: "z-4", + APP_COMMENTS: "z-7", +}; diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 2d73335f75..c839a9c74c 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -143,6 +143,7 @@ export const Colors = { GREY_8: "#716E6E", GREY_9: "#4B4848", GREY_10: "#090707", + GREY_11: "#9F9F9F", PRIMARY_ORANGE: "#F86A2B", diff --git a/app/client/src/constants/Layers.tsx b/app/client/src/constants/Layers.tsx index e885a77061..dffb96df75 100644 --- a/app/client/src/constants/Layers.tsx +++ b/app/client/src/constants/Layers.tsx @@ -59,4 +59,11 @@ export const Layers = { concurrentEditorWarning: Indices.Layer2, }; +export const tailwindLayers = { + propertyPane: "z-3", + entityExplorer: "z-3", + resizer: "z-4", + appComments: "z-7", +}; + export const LayersContext = React.createContext(Layers); diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 19c1949947..0b601efc94 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -625,6 +625,10 @@ export const ReduxActionTypes = { TOGGLE_RELEASE_NOTES: "TOGGLE_RELEASE_NOTES", FETCH_RELEASES: "FETCH_RELEASES", RESTART_SERVER_POLL: "RESTART_SERVER_POLL", + SET_EXPLORER_PINNED: "SET_EXPLORER_PINNED", + SET_EXPLORER_ACTIVE: "SET_EXPLORER_ACTIVE", + SET_PREVIEW_MODE: "SET_PREVIEW_MODE", + UPDATE_EXPLORER_WIDTH: "UPDATE_EXPLORER_WIDTH", FIRST_TIME_USER_ONBOARDING_INIT: "FIRST_TIME_USER_ONBOARDING_INIT", SET_USER_ROLE_USECASE: "SET_USER_ROLE_USECASE", UPDATE_JS_ACTION_BODY: "UPDATE_JS_ACTION_BODY", diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 318af30687..8f3d1339cf 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -48,13 +48,13 @@ export const CSSUnits: { [id: string]: CSSUnit } = { RELATIVE_PARENT: "%", }; -interface LayoutConfig { +export interface LayoutConfig { minWidth: number; maxWidth: number; } type LayoutConfigurations = Record; -export const DefaultLayoutType: SupportedLayouts = "DESKTOP"; +export const DefaultLayoutType: SupportedLayouts = "FLUID"; export const layoutConfigurations: LayoutConfigurations = { TABLET_LARGE: { minWidth: 960, @@ -69,7 +69,7 @@ export const layoutConfigurations: LayoutConfigurations = { FLUID: { minWidth: -1, maxWidth: -1 }, }; -export const LATEST_PAGE_VERSION = 45; +export const LATEST_PAGE_VERSION = 46; export const GridDefaults = { DEFAULT_CELL_SIZE: 1, diff --git a/app/client/src/constants/defs/moment.json b/app/client/src/constants/defs/moment.json index de5060af9c..3adf0222e7 100644 --- a/app/client/src/constants/defs/moment.json +++ b/app/client/src/constants/defs/moment.json @@ -1,5 +1,617 @@ { "!name": "LIB/moment", + "!define":{ + "Moment": { + "startOf": { + "!type": "fn(unitOfTime: ?) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/start-of/", + "!doc": "Mutates the original moment by setting it to the start of a unit of time" + }, + "endOf": { + "!type": "fn(unitOfTime: ?) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/end-of/", + "!doc": "Mutates the original moment by setting it to the end of a unit of time" + }, + "isValid": { + "!type": "fn() -> bool", + "!url": "https://momentjs.com/docs/#/parsing/is-valid/", + "!doc": "Check whether the Moment considers the date invalid" + }, + "invalidAt": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/parsing/is-valid/", + "!doc": "Returns which date unit overflowed" + }, + "hasAlignedHourOffset": { + "!type": "fn(other?: MomentInput) -> boolean" + }, + "creationData": { + "!type": "fn() -> MomentCreationData", + "!url": "https://momentjs.com/docs/#/parsing/creation-data/", + "!doc": "After a moment object is created, all of the inputs can be accessed with creationData() method" + }, + "parsingFlags": { + "!type": "fn() -> MomentParsingFlags", + "!url": "https://momentjs.com/docs/#/parsing/is-valid/", + "!doc": "You can check the metrics used by #isValid using moment#parsingFlags, which returns an object" + }, + "add": { + "!type": "fn(amount?: DurationInputArg1, unit?: DurationInputArg2) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/add/", + "!doc": "Mutates the original moment by adding time." + }, + "subtract": { + "!type": "fn(amount?: DurationInputArg1, unit?: DurationInputArg2) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/subtract/", + "!doc": "Mutates the original moment by subtracting time" + }, + "calender": { + "!type": "fn(fntime?: MomentInput, formats?: CalendarSpec) -> string", + "!url": "https://momentjs.com/docs/#/displaying/calendar-time/", + "!doc": "Calendar time displays time relative to a given referenceDay (defaults to the start of today)" + }, + "valueOf": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/displaying/unix-timestamp-milliseconds/", + "!doc": "Unix timestamp in milliseconds" + }, + "local": { + "!type": "fn(keepLocalTime?: boolean) -> Moment", + "!doc": "Current date/time in local mode" + }, + "isLocal": { + "!type": "fn() -> bool" + }, + "utc": { + "!type": "fn(keepLocalTime?: boolean) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/utc/", + "!doc": "Current date/time in UTC mode" + }, + "isUTC": { + "!type": "fn() -> bool" + }, + "parseZone": { + "!type": "fn() -> Moment" + }, + "format": { + "!type": "fn(format?: string) -> string", + "!url": "https://momentjs.com/docs/#/displaying/format/", + "!doc": "Takes a string of tokens and replaces them with their corresponding values" + }, + "isAfter": { + "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-after/", + "!doc": "Check if a moment is after another moment." + }, + "isSame": { + "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-same/", + "!doc": "Check if a moment is the same as another moment." + }, + "isBefore": { + "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-before/", + "!doc": "Check if a moment is before another moment" + }, + "fromNow": { + "!type": "fn(withoutSuffix?: bool) -> string", + "!url": "https://momentjs.com/docs/#/displaying/fromnow/", + "!doc": "Get relative time" + }, + "from": { + "!type": "fn(inp: MomentInput, suffix?: boolean) -> string", + "!url": "https://momentjs.com/docs/#/displaying/from/", + "!doc": "Returns a moment in relation to a time other than now" + }, + "to": { + "!type": "fn(inp: MomentInput, suffix?: boolean) -> string", + "!url": "https://momentjs.com/docs/#/displaying/to/", + "!doc": "Display a moment in relation to a time other than now" + }, + "toNow": { + "!type": "fn(withoutPrefix?: boolean) -> string", + "!url": "https://momentjs.com/docs/#/displaying/tonow/", + "!doc": "Display relative time" + }, + "diff": { + "!type": "fn(b: MomentInput, unitOfTime?: unitOfTime.Diff, precise?: boolean) -> number", + "!url": "https://momentjs.com/docs/#/displaying/difference/", + "!doc": "Get the difference in milliseconds" + }, + "toArray": { + "!type": "fn() -> [number]", + "!url": "https://momentjs.com/docs/#/displaying/as-array/", + "!doc": "Returns an array that mirrors the parameters from new Date()" + }, + "clone": { + "!type": "fn() -> Moment", + "!url": "https://momentjs.com/docs/#/parsing/moment-clone/", + "!doc": "Returns the clone of a moment," + }, + "year": { + "!type": "fn(y: number) -> Moment", + "!url": "https://momentjs.com/docs/#/get-set/year/", + "!doc": "Gets or sets the year" + }, + "quarter": { + "!type": "fn() -> number|fn(q: number) -> Moment", + "!url": "https://momentjs.com/docs/#/get-set/quarter/", + "!doc": "Gets or sets the quarter" + }, + "quarters": { + "!type": "fn() -> number|fn(q: number) -> Moment", + "!url": "https://momentjs.com/docs/#/get-set/quarter/", + "!doc": "Gets or sets the quarter" + }, + "month": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/month/", + "!doc": "Gets or sets the month" + }, + "day": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/day/", + "!doc": "Gets or sets the day of the week." + }, + "days": { + "!type": "fn(d?: number|string) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/day/" + }, + "date": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/date/", + "!doc": "Gets or sets the day of the month." + }, + "hour": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/hour/", + "!doc": "Gets or sets the hour." + }, + "hours": { + "!type": "fn(h?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/hour/", + "!doc": "Gets or sets the hour." + }, + "minute": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/minute/", + "!doc": "Gets or sets the minutes." + }, + "minutes": { + "!type": "fn(m?: number) -> Moment|number", + "!url": "https://momentjs.com/docs/#/get-set/minute/", + "!doc": "Gets or sets the minutes." + }, + "second": { + "!type": "fn([s])", + "!url": "https://momentjs.com/docs/#/get-set/second/", + "!doc": "Gets or sets the seconds." + }, + "seconds": { + "!type": "fn([s])", + "!url": "https://momentjs.com/docs/#/get-set/second/", + "!doc": "Gets or sets the seconds." + }, + "millisecond": { + "!type": "fn([ms])", + "!url": "https://momentjs.com/docs/#/get-set/millisecond/", + "!doc": "Gets or sets the milliseconds." + }, + "milliseconds": { + "!type": "fn([ms])", + "!url": "https://momentjs.com/docs/#/get-set/millisecond/", + "!doc": "Gets or sets the milliseconds." + }, + "weekday": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/weekday/", + "!doc": "Gets or sets the day of the week according to the locale" + }, + "isoWeekday": { + "!type": "fn(d?: number|string) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/iso-weekday/", + "!doc": "Gets or sets the ISO day of the week with 1 being Monday and 7 being Sunday" + }, + "weekYear": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/week-year/", + "!doc": "Gets or sets the week-year according to the locale" + }, + "isoWeekYear": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/iso-week-year/", + "!doc": "Gets or sets the ISO week-year" + }, + "week": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/week/", + "!doc": "Gets or sets the week of the year" + }, + "weeks": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/week/", + "!doc": "Gets or sets the week of the year" + }, + "isoWeek": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/iso-week/", + "!doc": "Gets or sets the ISO week of the year" + }, + "isoWeeks": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/iso-week/", + "!doc": "Gets or sets the ISO week of the year" + }, + "weeksInYear": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/weeks-in-year/", + "!doc": "Gets the number of weeks according to locale in the current moment's year" + }, + "weeksInWeekYear": { + "!type": "fn() -> number" + }, + "isoWeeksInYear": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/get-set/iso-weeks-in-year/", + "!doc": "Gets the number of weeks in the current moment's year, according to ISO weeks" + }, + "isoWeeksInISOWeekYear": { + "!type": "fn() -> number" + }, + "dayOfYear": { + "!type": "fn(d?: number) -> number|Moment", + "!url": "https://momentjs.com/docs/#/get-set/day-of-year/", + "!doc": "Gets or sets the day of the year" + }, + "get": { + "!type": "fn(unit: ?) -> number", + "!url": "https://momentjs.com/docs/#/get-set/get/", + "!doc": "String getter" + }, + "set": { + "!type": "fn(unit: ?, value: number) -> Moment", + "!url": "https://momentjs.com/docs/#/get-set/set/", + "!doc": "Generic setter, accepting unit as first argument, and value as second" + }, + "toDate": { + "!type": "fn()", + "!url": "https://momentjs.com/docs/#/displaying/as-javascript-date/", + "!doc": "Get a copy of the native Date object that Moment.js wraps" + }, + "toISOString": { + "!type": "fn(keepOffset?: boolean) -> string", + "!url": "https://momentjs.com/docs/#/displaying/as-iso-string/", + "!doc": "Formats a string to the ISO8601 standard" + }, + "inspect": { + "!type": "fn() -> string", + "!url": "https://momentjs.com/docs/#/displaying/inspect/", + "!doc": "Returns a machine readable string, that can be evaluated to produce the same moment" + }, + "toJSON": { + "!type": "fn() -> string", + "!url": "https://momentjs.com/docs/#/displaying/as-json/", + "!doc": "When serializing an object to JSON, if there is a Moment object, it will be represented as an ISO8601 string, adjusted to UTC" + }, + "unix": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/displaying/unix-timestamp/", + "!doc": "Outputs a Unix timestamp (the number of seconds since the Unix Epoch)" + }, + "isLeapYear": { + "!type": "fn() -> bool", + "!url": "https://momentjs.com/docs/#/query/is-leap-year/", + "!doc": "Returns true if that year is a leap year, and false if it is not" + }, + "zone": { + "!type": "fn(b: number|string) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/timezone-offset/", + "!doc": "Get the time zone offset in minutes" + }, + "utcOffset": { + "!type": "fn(b?: number|string, keepLocalTime?: boolean) -> Moment", + "!url": "https://momentjs.com/docs/#/manipulating/utc-offset/", + "!doc": "Get or set the UTC offset in minutes" + }, + "isUtcOffset": { + "!type": "fn() -> boolean" + }, + "daysInMonth": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/displaying/days-in-month/", + "!doc": "Get the number of days in the current month" + }, + "isDST": { + "!type": "fn() -> boolean", + "!url": "https://momentjs.com/docs/#/query/is-daylight-saving-time/", + "!doc": "Get the number of days in the current month" + }, + "zoneAbbr": { + "!type": "fn() -> string" + }, + "zoneName": { + "!type": "fn() -> string" + }, + "isSameOrAfter": { + "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-same-or-after/", + "!doc": "Check if a moment is after or the same as another moment" + }, + "isSameOrBefore": { + "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-same-or-before/", + "!doc": "Check if a moment is before or the same as another moment" + }, + "isBetween": { + "!type": "fn(a: MomentInput, b: MomentInput, granularity?: ?, inclusivity?: ?) -> bool", + "!url": "https://momentjs.com/docs/#/query/is-between/", + "!doc": "Check if a moment is between two other moments," + }, + "locale": { + "!type": "fn(locale: LocaleSpecifier) -> string|Moment", + "!url": "https://momentjs.com/docs/#/i18n/instance-locale/", + "!doc": "A global locale configuration can be problematic when passing around moments that may need to be formatted into different locale" + }, + "localeData": { + "!type": "fn() -> Locale" + }, + "toObject": { + "!type": "fn() -> MomentObjectOutput", + "!url": "https://momentjs.com/docs/#/displaying/as-object/", + "!doc": "Returns an object containing year, month, day-of-month, hour, minute, seconds, milliseconds" + } + }, + "Duration": { + "clone": { + "!type": "fn() -> Duration", + "!url": "https://momentjs.com/docs/#/durations/clone/", + "!doc": "Create a clone of a duration" + }, + "humanize": { + "!type": "fn(argWithSuffix?: boolean, argThresholds?: argThresholdOpts) -> string", + "!url": "https://momentjs.com/docs/#/durations/humanize/" + }, + "abs": { + "!type": "fn() -> Duration" + }, + "as": { + "!type": "fn(units: ?) -> number", + "!url": "https://momentjs.com/docs/#/durations/as/" + }, + "get": { + "!type": "fn(units: ?) -> number", + "!url": "https://momentjs.com/docs/#/durations/get/", + "!doc": "Alternate to Duration#x() getters" + }, + "milliseconds": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/milliseconds/", + "!doc": "Get the number of milliseconds in a duration" + }, + "asMilliseconds": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/milliseconds/", + "!doc": "Get the length of the duration in milliseconds" + }, + "seconds": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/seconds/", + "!doc": "Get the number of seconds in a duration" + }, + "asSeconds": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/seconds/", + "!doc": "Get the length of the duration in seconds" + }, + "minutes": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/minutes/", + "!doc": "Gets the minutes (0 - 59)" + }, + "asMinutes": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/minutes/", + "!doc": "Gets the length of the duration in minutes" + }, + "hours": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/hours/", + "!doc": "Gets the hours (0 - 23)" + }, + "asHours": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/hours/", + "!doc": "Gets the length of the duration in hours" + }, + "days": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/days/", + "!doc": "Gets the days (0 - 30)" + }, + "asDays": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/days/", + "!doc": "Gets the length of the duration in days" + }, + "weeks": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/weeks/", + "!doc": "Gets the weeks (0 - 4)" + }, + "asWeeks": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/weeks/", + "!doc": "Gets the length of the duration in weeks" + }, + "months": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/months/", + "!doc": "Gets the months (0 - 11)." + }, + "asMonths": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/months/", + "!doc": "Gets the length of the duration in months" + }, + "years": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/years/", + "!doc": "Gets the years" + }, + "asYears": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/durations/years/", + "!doc": "Gets the length of the duration in years" + }, + "add": { + "!type": "fn(inp?: DurationInputArg1, unit?: DurationInputArg2) -> Duration", + "!url": "https://momentjs.com/docs/#/durations/add/", + "!doc": "Mutates the original duration by adding time" + }, + "subtract": { + "!type": "fn(inp?: DurationInputArg1, unit?: DurationInputArg2) -> Duration", + "!url": "https://momentjs.com/docs/#/durations/subtract/", + "!doc": "Mutates the original duration by subtracting time" + }, + "locale": { + "!type": "fn(locale: LocaleSpecifier) -> Duration", + "!url": "https://momentjs.com/docs/#/durations/locale/", + "!doc": "Get or set the locale of a duration" + }, + "localeData": { + "!type": "fn() -> Locale" + }, + "toISOString": { + "!type": "fn() -> string", + "!url": "https://momentjs.com/docs/#/durations/as-iso-string/", + "!doc": "Returns duration in string as specified by ISO 8601 standard" + }, + "toJSON": { + "!type": "fn() -> string", + "!url": "https://momentjs.com/docs/#/durations/as-json/", + "!doc": "When serializing a duration object to JSON, it will be represented as an ISO8601 string" + }, + "isValid": { + "!type": "fn() -> boolean" + } + }, + "Locale": { + "calendar": { + "!type": "fn(key?: CalendarKey, m?: Moment, now?: Moment) -> string", + "!url": "https://momentjs.com/docs/#/customization/calendar/" + }, + "longDateFormat": { + "!type": "fn(key: LongDateFormatKey) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns the full format of abbreviated date-time formats LT, L, LL and so on" + }, + "invalidDate": { + "!type": "fn() -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns a translation of 'Invalid date'" + }, + "ordinal": { + "!type": "fn(n: number) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Convert number to ordinal string 1 -> 1st" + }, + "preparse": { + "!type": "fn(inp: string) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Called before parsing on every input string" + }, + "postformat": { + "!type": "fn(inp: string) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Called after formatting on every string" + }, + "relativeTime": { + "!type": "fn(n: number, withoutSuffix: boolean, key: RelativeTimeKey, isFuture: boolean) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns relative time string, key is on of 's', 'm', 'mm', 'h', 'hh', 'd', 'dd', 'M', 'MM', 'y', 'yy'" + }, + "pastFuture": { + "!type": "fn(diff: number, absRelTime: string) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Convert relTime string to past or future string depending on diff" + }, + "set": { + "!type": "fn(config: Object) -> void", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Convert relTime string to past or future string depending on diff" + }, + "months": { + "!type": "fn(m?: Moment, format?: string) -> string|[string]", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Full month name of m" + }, + "monthsShort": { + "!type": "fn(m?: Moment, format?: string) -> string|[string]", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Short month name of m" + }, + "monthsParse": { + "!type": "fn(monthName: string, format: string, strict: bool) -> number", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns month id (0 to 11) of input" + }, + "monthsRegex": { + "!type": "fn(strict: bool) -> RegExp" + }, + "monthsShortRegex": { + "!type": "fn(strict: boolean) -> RegExp" + }, + "week": { + "!type": "fn(m: Moment) -> number", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "returns week-of-year of m" + }, + "firstDayOfYear": { + "!type": "fn() -> number", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "0-15 Used to determine first week of the year" + }, + "weekdays": { + "!type": "fn(m: Moment, format?: string) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Full weekday name of m" + }, + "weekdaysMin": { + "!type": "fn(m: Moment, format?: string) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "min weekday name of m" + }, + "weekdaysShort": { + "!type": "fn(m: Moment) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Short weekday name of m" + }, + "weekdaysParse": { + "!type": "fn(weekdayName: string, format: string, strict: boolean) -> number", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns weekday id (0 to 6) of input" + }, + "weekdaysRegex": { + "!type": "fn(strict: bool) -> RegExp" + }, + "weekdaysShortRegex": { + "!type": "fn(strict: bool) -> RegExp" + }, + "weekdaysMinRegex": { + "!type": "fn(strict: bool) -> RegExp" + }, + "isPM": { + "!type": "fn(input: string) -> bool", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns true if input represents PM" + }, + "meridiem": { + "!type": "fn(hour: number, minute: number, isLower: bool) -> string", + "!url": "https://momentjs.com/docs/#/i18n/locale-data/", + "!doc": "Returns am/pm string for particular time-of-day in upper/lower case" + } + } + }, "moment": { "!type": "fn(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean) -> Moment", "!url": "https://momentjs.com/docs/#/parsing/", @@ -116,615 +728,6 @@ "!type": "fn(input: string) -> number", "!url": "https://momentjs.com/docs/#/parsing/string-format/" } - }, - "Moment": { - "startOf": { - "!type": "fn(unitOfTime: ?) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/start-of/", - "!doc": "Mutates the original moment by setting it to the start of a unit of time" - }, - "endOf": { - "!type": "fn(unitOfTime: ?) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/end-of/", - "!doc": "Mutates the original moment by setting it to the end of a unit of time" - }, - "isValid": { - "!type": "fn() -> bool", - "!url": "https://momentjs.com/docs/#/parsing/is-valid/", - "!doc": "Check whether the Moment considers the date invalid" - }, - "invalidAt": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/parsing/is-valid/", - "!doc": "Returns which date unit overflowed" - }, - "hasAlignedHourOffset": { - "!type": "fn(other?: MomentInput) -> boolean" - }, - "creationData": { - "!type": "fn() -> MomentCreationData", - "!url": "https://momentjs.com/docs/#/parsing/creation-data/", - "!doc": "After a moment object is created, all of the inputs can be accessed with creationData() method" - }, - "parsingFlags": { - "!type": "fn() -> MomentParsingFlags", - "!url": "https://momentjs.com/docs/#/parsing/is-valid/", - "!doc": "You can check the metrics used by #isValid using moment#parsingFlags, which returns an object" - }, - "add": { - "!type": "fn(amount?: DurationInputArg1, unit?: DurationInputArg2) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/add/", - "!doc": "Mutates the original moment by adding time." - }, - "subtract": { - "!type": "fn(amount?: DurationInputArg1, unit?: DurationInputArg2) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/subtract/", - "!doc": "Mutates the original moment by subtracting time" - }, - "calender": { - "!type": "fn(fntime?: MomentInput, formats?: CalendarSpec) -> string", - "!url": "https://momentjs.com/docs/#/displaying/calendar-time/", - "!doc": "Calendar time displays time relative to a given referenceDay (defaults to the start of today)" - }, - "valueOf": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/displaying/unix-timestamp-milliseconds/", - "!doc": "Unix timestamp in milliseconds" - }, - "local": { - "!type": "fn(keepLocalTime?: boolean) -> Moment", - "!doc": "Current date/time in local mode" - }, - "isLocal": { - "!type": "fn() -> bool" - }, - "utc": { - "!type": "fn(keepLocalTime?: boolean) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/utc/", - "!doc": "Current date/time in UTC mode" - }, - "isUTC": { - "!type": "fn() -> bool" - }, - "parseZone": { - "!type": "fn() -> Moment" - }, - "format": { - "!type": "fn(format?: string) -> string", - "!url": "https://momentjs.com/docs/#/displaying/format/", - "!doc": "Takes a string of tokens and replaces them with their corresponding values" - }, - "isAfter": { - "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-after/", - "!doc": "Check if a moment is after another moment." - }, - "isSame": { - "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-same/", - "!doc": "Check if a moment is the same as another moment." - }, - "isBefore": { - "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-before/", - "!doc": "Check if a moment is before another moment" - }, - "fromNow": { - "!type": "fn(withoutSuffix?: bool) -> string", - "!url": "https://momentjs.com/docs/#/displaying/fromnow/", - "!doc": "Get relative time" - }, - "from": { - "!type": "fn(inp: MomentInput, suffix?: boolean) -> string", - "!url": "https://momentjs.com/docs/#/displaying/from/", - "!doc": "Returns a moment in relation to a time other than now" - }, - "to": { - "!type": "fn(inp: MomentInput, suffix?: boolean) -> string", - "!url": "https://momentjs.com/docs/#/displaying/to/", - "!doc": "Display a moment in relation to a time other than now" - }, - "toNow": { - "!type": "fn(withoutPrefix?: boolean) -> string", - "!url": "https://momentjs.com/docs/#/displaying/tonow/", - "!doc": "Display relative time" - }, - "diff": { - "!type": "fn(b: MomentInput, unitOfTime?: unitOfTime.Diff, precise?: boolean) -> number", - "!url": "https://momentjs.com/docs/#/displaying/difference/", - "!doc": "Get the difference in milliseconds" - }, - "toArray": { - "!type": "fn() -> [number]", - "!url": "https://momentjs.com/docs/#/displaying/as-array/", - "!doc": "Returns an array that mirrors the parameters from new Date()" - }, - "clone": { - "!type": "fn() -> Moment", - "!url": "https://momentjs.com/docs/#/parsing/moment-clone/", - "!doc": "Returns the clone of a moment," - }, - "year": { - "!type": "fn(y: number) -> Moment", - "!url": "https://momentjs.com/docs/#/get-set/year/", - "!doc": "Gets or sets the year" - }, - "quarter": { - "!type": "fn() -> number|fn(q: number) -> Moment", - "!url": "https://momentjs.com/docs/#/get-set/quarter/", - "!doc": "Gets or sets the quarter" - }, - "quarters": { - "!type": "fn() -> number|fn(q: number) -> Moment", - "!url": "https://momentjs.com/docs/#/get-set/quarter/", - "!doc": "Gets or sets the quarter" - }, - "month": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/month/", - "!doc": "Gets or sets the month" - }, - "day": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/day/", - "!doc": "Gets or sets the day of the week." - }, - "days": { - "!type": "fn(d?: number|string) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/day/" - }, - "date": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/date/", - "!doc": "Gets or sets the day of the month." - }, - "hour": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/hour/", - "!doc": "Gets or sets the hour." - }, - "hours": { - "!type": "fn(h?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/hour/", - "!doc": "Gets or sets the hour." - }, - "minute": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/minute/", - "!doc": "Gets or sets the minutes." - }, - "minutes": { - "!type": "fn(m?: number) -> Moment|number", - "!url": "https://momentjs.com/docs/#/get-set/minute/", - "!doc": "Gets or sets the minutes." - }, - "second": { - "!type": "fn([s])", - "!url": "https://momentjs.com/docs/#/get-set/second/", - "!doc": "Gets or sets the seconds." - }, - "seconds": { - "!type": "fn([s])", - "!url": "https://momentjs.com/docs/#/get-set/second/", - "!doc": "Gets or sets the seconds." - }, - "millisecond": { - "!type": "fn([ms])", - "!url": "https://momentjs.com/docs/#/get-set/millisecond/", - "!doc": "Gets or sets the milliseconds." - }, - "milliseconds": { - "!type": "fn([ms])", - "!url": "https://momentjs.com/docs/#/get-set/millisecond/", - "!doc": "Gets or sets the milliseconds." - }, - "weekday": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/weekday/", - "!doc": "Gets or sets the day of the week according to the locale" - }, - "isoWeekday": { - "!type": "fn(d?: number|string) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/iso-weekday/", - "!doc": "Gets or sets the ISO day of the week with 1 being Monday and 7 being Sunday" - }, - "weekYear": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/week-year/", - "!doc": "Gets or sets the week-year according to the locale" - }, - "isoWeekYear": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/iso-week-year/", - "!doc": "Gets or sets the ISO week-year" - }, - "week": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/week/", - "!doc": "Gets or sets the week of the year" - }, - "weeks": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/week/", - "!doc": "Gets or sets the week of the year" - }, - "isoWeek": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/iso-week/", - "!doc": "Gets or sets the ISO week of the year" - }, - "isoWeeks": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/iso-week/", - "!doc": "Gets or sets the ISO week of the year" - }, - "weeksInYear": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/weeks-in-year/", - "!doc": "Gets the number of weeks according to locale in the current moment's year" - }, - "weeksInWeekYear": { - "!type": "fn() -> number" - }, - "isoWeeksInYear": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/get-set/iso-weeks-in-year/", - "!doc": "Gets the number of weeks in the current moment's year, according to ISO weeks" - }, - "isoWeeksInISOWeekYear": { - "!type": "fn() -> number" - }, - "dayOfYear": { - "!type": "fn(d?: number) -> number|Moment", - "!url": "https://momentjs.com/docs/#/get-set/day-of-year/", - "!doc": "Gets or sets the day of the year" - }, - "get": { - "!type": "fn(unit: ?) -> number", - "!url": "https://momentjs.com/docs/#/get-set/get/", - "!doc": "String getter" - }, - "set": { - "!type": "fn(unit: ?, value: number) -> Moment", - "!url": "https://momentjs.com/docs/#/get-set/set/", - "!doc": "Generic setter, accepting unit as first argument, and value as second" - }, - "toDate": { - "!type": "fn()", - "!url": "https://momentjs.com/docs/#/displaying/as-javascript-date/", - "!doc": "Get a copy of the native Date object that Moment.js wraps" - }, - "toISOString": { - "!type": "fn(keepOffset?: boolean) -> string", - "!url": "https://momentjs.com/docs/#/displaying/as-iso-string/", - "!doc": "Formats a string to the ISO8601 standard" - }, - "inspect": { - "!type": "fn() -> string", - "!url": "https://momentjs.com/docs/#/displaying/inspect/", - "!doc": "Returns a machine readable string, that can be evaluated to produce the same moment" - }, - "toJSON": { - "!type": "fn() -> string", - "!url": "https://momentjs.com/docs/#/displaying/as-json/", - "!doc": "When serializing an object to JSON, if there is a Moment object, it will be represented as an ISO8601 string, adjusted to UTC" - }, - "unix": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/displaying/unix-timestamp/", - "!doc": "Outputs a Unix timestamp (the number of seconds since the Unix Epoch)" - }, - "isLeapYear": { - "!type": "fn() -> bool", - "!url": "https://momentjs.com/docs/#/query/is-leap-year/", - "!doc": "Returns true if that year is a leap year, and false if it is not" - }, - "zone": { - "!type": "fn(b: number|string) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/timezone-offset/", - "!doc": "Get the time zone offset in minutes" - }, - "utcOffset": { - "!type": "fn(b?: number|string, keepLocalTime?: boolean) -> Moment", - "!url": "https://momentjs.com/docs/#/manipulating/utc-offset/", - "!doc": "Get or set the UTC offset in minutes" - }, - "isUtcOffset": { - "!type": "fn() -> boolean" - }, - "daysInMonth": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/displaying/days-in-month/", - "!doc": "Get the number of days in the current month" - }, - "isDST": { - "!type": "fn() -> boolean", - "!url": "https://momentjs.com/docs/#/query/is-daylight-saving-time/", - "!doc": "Get the number of days in the current month" - }, - "zoneAbbr": { - "!type": "fn() -> string" - }, - "zoneName": { - "!type": "fn() -> string" - }, - "isSameOrAfter": { - "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-same-or-after/", - "!doc": "Check if a moment is after or the same as another moment" - }, - "isSameOrBefore": { - "!type": "fn(inp?: MomentInput, granularity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-same-or-before/", - "!doc": "Check if a moment is before or the same as another moment" - }, - "isBetween": { - "!type": "fn(a: MomentInput, b: MomentInput, granularity?: ?, inclusivity?: ?) -> bool", - "!url": "https://momentjs.com/docs/#/query/is-between/", - "!doc": "Check if a moment is between two other moments," - }, - "locale": { - "!type": "fn(locale: LocaleSpecifier) -> string|Moment", - "!url": "https://momentjs.com/docs/#/i18n/instance-locale/", - "!doc": "A global locale configuration can be problematic when passing around moments that may need to be formatted into different locale" - }, - "localeData": { - "!type": "fn() -> Locale" - }, - "toObject": { - "!type": "fn() -> MomentObjectOutput", - "!url": "https://momentjs.com/docs/#/displaying/as-object/", - "!doc": "Returns an object containing year, month, day-of-month, hour, minute, seconds, milliseconds" - } - }, - "Duration": { - "clone": { - "!type": "fn() -> Duration", - "!url": "https://momentjs.com/docs/#/durations/clone/", - "!doc": "Create a clone of a duration" - }, - "humanize": { - "!type": "fn(argWithSuffix?: boolean, argThresholds?: argThresholdOpts) -> string", - "!url": "https://momentjs.com/docs/#/durations/humanize/" - }, - "abs": { - "!type": "fn() -> Duration" - }, - "as": { - "!type": "fn(units: ?) -> number", - "!url": "https://momentjs.com/docs/#/durations/as/" - }, - "get": { - "!type": "fn(units: ?) -> number", - "!url": "https://momentjs.com/docs/#/durations/get/", - "!doc": "Alternate to Duration#x() getters" - }, - "milliseconds": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/milliseconds/", - "!doc": "Get the number of milliseconds in a duration" - }, - "asMilliseconds": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/milliseconds/", - "!doc": "Get the length of the duration in milliseconds" - }, - "seconds": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/seconds/", - "!doc": "Get the number of seconds in a duration" - }, - "asSeconds": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/seconds/", - "!doc": "Get the length of the duration in seconds" - }, - "minutes": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/minutes/", - "!doc": "Gets the minutes (0 - 59)" - }, - "asMinutes": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/minutes/", - "!doc": "Gets the length of the duration in minutes" - }, - "hours": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/hours/", - "!doc": "Gets the hours (0 - 23)" - }, - "asHours": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/hours/", - "!doc": "Gets the length of the duration in hours" - }, - "days": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/days/", - "!doc": "Gets the days (0 - 30)" - }, - "asDays": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/days/", - "!doc": "Gets the length of the duration in days" - }, - "weeks": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/weeks/", - "!doc": "Gets the weeks (0 - 4)" - }, - "asWeeks": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/weeks/", - "!doc": "Gets the length of the duration in weeks" - }, - "months": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/months/", - "!doc": "Gets the months (0 - 11)." - }, - "asMonths": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/months/", - "!doc": "Gets the length of the duration in months" - }, - "years": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/years/", - "!doc": "Gets the years" - }, - "asYears": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/durations/years/", - "!doc": "Gets the length of the duration in years" - }, - "add": { - "!type": "fn(inp?: DurationInputArg1, unit?: DurationInputArg2) -> Duration", - "!url": "https://momentjs.com/docs/#/durations/add/", - "!doc": "Mutates the original duration by adding time" - }, - "subtract": { - "!type": "fn(inp?: DurationInputArg1, unit?: DurationInputArg2) -> Duration", - "!url": "https://momentjs.com/docs/#/durations/subtract/", - "!doc": "Mutates the original duration by subtracting time" - }, - "locale": { - "!type": "fn(locale: LocaleSpecifier) -> Duration", - "!url": "https://momentjs.com/docs/#/durations/locale/", - "!doc": "Get or set the locale of a duration" - }, - "localeData": { - "!type": "fn() -> Locale" - }, - "toISOString": { - "!type": "fn() -> string", - "!url": "https://momentjs.com/docs/#/durations/as-iso-string/", - "!doc": "Returns duration in string as specified by ISO 8601 standard" - }, - "toJSON": { - "!type": "fn() -> string", - "!url": "https://momentjs.com/docs/#/durations/as-json/", - "!doc": "When serializing a duration object to JSON, it will be represented as an ISO8601 string" - }, - "isValid": { - "!type": "fn() -> boolean" - } - }, - "Locale": { - "calendar": { - "!type": "fn(key?: CalendarKey, m?: Moment, now?: Moment) -> string", - "!url": "https://momentjs.com/docs/#/customization/calendar/" - }, - "longDateFormat": { - "!type": "fn(key: LongDateFormatKey) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns the full format of abbreviated date-time formats LT, L, LL and so on" - }, - "invalidDate": { - "!type": "fn() -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns a translation of 'Invalid date'" - }, - "ordinal": { - "!type": "fn(n: number) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Convert number to ordinal string 1 -> 1st" - }, - "preparse": { - "!type": "fn(inp: string) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Called before parsing on every input string" - }, - "postformat": { - "!type": "fn(inp: string) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Called after formatting on every string" - }, - "relativeTime": { - "!type": "fn(n: number, withoutSuffix: boolean, key: RelativeTimeKey, isFuture: boolean) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns relative time string, key is on of 's', 'm', 'mm', 'h', 'hh', 'd', 'dd', 'M', 'MM', 'y', 'yy'" - }, - "pastFuture": { - "!type": "fn(diff: number, absRelTime: string) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Convert relTime string to past or future string depending on diff" - }, - "set": { - "!type": "fn(config: Object) -> void", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Convert relTime string to past or future string depending on diff" - }, - "months": { - "!type": "fn(m?: Moment, format?: string) -> string|[string]", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Full month name of m" - }, - "monthsShort": { - "!type": "fn(m?: Moment, format?: string) -> string|[string]", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Short month name of m" - }, - "monthsParse": { - "!type": "fn(monthName: string, format: string, strict: bool) -> number", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns month id (0 to 11) of input" - }, - "monthsRegex": { - "!type": "fn(strict: bool) -> RegExp" - }, - "monthsShortRegex": { - "!type": "fn(strict: boolean) -> RegExp" - }, - "week": { - "!type": "fn(m: Moment) -> number", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "returns week-of-year of m" - }, - "firstDayOfYear": { - "!type": "fn() -> number", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "0-15 Used to determine first week of the year" - }, - "weekdays": { - "!type": "fn(m: Moment, format?: string) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Full weekday name of m" - }, - "weekdaysMin": { - "!type": "fn(m: Moment, format?: string) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "min weekday name of m" - }, - "weekdaysShort": { - "!type": "fn(m: Moment) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Short weekday name of m" - }, - "weekdaysParse": { - "!type": "fn(weekdayName: string, format: string, strict: boolean) -> number", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns weekday id (0 to 6) of input" - }, - "weekdaysRegex": { - "!type": "fn(strict: bool) -> RegExp" - }, - "weekdaysShortRegex": { - "!type": "fn(strict: bool) -> RegExp" - }, - "weekdaysMinRegex": { - "!type": "fn(strict: bool) -> RegExp" - }, - "isPM": { - "!type": "fn(input: string) -> bool", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns true if input represents PM" - }, - "meridiem": { - "!type": "fn(hour: number, minute: number, isLower: bool) -> string", - "!url": "https://momentjs.com/docs/#/i18n/locale-data/", - "!doc": "Returns am/pm string for particular time-of-day in upper/lower case" - } } + } diff --git a/app/client/src/constants/messages.ts b/app/client/src/constants/messages.ts index cb70d3d3cd..c303d2daed 100644 --- a/app/client/src/constants/messages.ts +++ b/app/client/src/constants/messages.ts @@ -848,3 +848,10 @@ export const WELCOME_FORM_NON_SUPER_USER_USE_CASE = () => "What are you planning to use Appsmith for?"; export const QUERY_CONFIRMATION_MODAL_MESSAGE = () => "Are you sure you want to perform this action?"; +export const ENTITY_EXPLORER_TITLE = () => "NAVIGATION"; +export const MULTI_SELECT_PROPERTY_PANE_MESSAGE = () => + `Select a widget to see it's properties`; +export const LOCK_ENTITY_EXPLORER_MESSAGE = () => `Lock sidebar open`; + +export const TABLE_WIDGET_TOTAL_RECORD_TOOLTIP = () => + "It stores the total no. of rows in the table. Helps in calculating the no. of pages that further allows to enable or disable the next/previous control in pagination."; diff --git a/app/client/src/constants/routes.ts b/app/client/src/constants/routes.ts index b125d5d253..a9c52e2fbb 100644 --- a/app/client/src/constants/routes.ts +++ b/app/client/src/constants/routes.ts @@ -306,6 +306,16 @@ export const JS_COLLECTION_ID_URL = ( }); }; +export const getApplicationEditorPageURL = ( + applicationId = ":applicationId", + pageId = ":pageId", + params: Record = {}, +): string => { + const url = `/applications/${applicationId}/pages/${pageId}/edit`; + const queryString = convertToQueryParams(params); + return url + queryString; +}; + export const getApplicationViewerPageURL = (props: { applicationId?: string; pageId?: string; // TODO make pageId this mandatory diff --git a/app/client/src/entities/DataTree/dataTreeWidget.test.ts b/app/client/src/entities/DataTree/dataTreeWidget.test.ts index 14d1d77098..325383b543 100644 --- a/app/client/src/entities/DataTree/dataTreeWidget.test.ts +++ b/app/client/src/entities/DataTree/dataTreeWidget.test.ts @@ -127,7 +127,7 @@ describe("generateDataTreeWidget", () => { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { propertyName: "onTextChanged", diff --git a/app/client/src/index.css b/app/client/src/index.css index 016901fb71..4da82df106 100755 --- a/app/client/src/index.css +++ b/app/client/src/index.css @@ -28,7 +28,12 @@ div.bp3-select-popover .bp3-popover-content { } span.bp3-popover-target { - display: block; + display: flex; + width: 100%; +} + +span.bp3-popover-target > * { + flex-grow: 1; } div.bp3-popover-arrow { diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index adac756b97..5ea35972dc 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -16,6 +16,7 @@ import { AppState } from "reducers"; import { setThemeMode } from "actions/themeActions"; import { StyledToastContainer } from "components/ads/Toast"; import localStorage from "utils/localStorage"; +import "./assets/styles/index.css"; import "./polyfills/corejs-add-on"; // enable autofreeze only in development import { setAutoFreeze } from "immer"; diff --git a/app/client/src/pages/AppViewer/AppViewerComemntsSidebar.tsx b/app/client/src/pages/AppViewer/AppViewerComemntsSidebar.tsx new file mode 100644 index 0000000000..d6fad25602 --- /dev/null +++ b/app/client/src/pages/AppViewer/AppViewerComemntsSidebar.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { useSelector } from "react-redux"; + +import AppComments from "comments/AppComments/AppComments"; +import { commentModeSelector } from "selectors/commentsSelectors"; + +function AppViewerCommentsSidebar() { + const isCommentMode = useSelector(commentModeSelector); + + // only renders the sidebar in comments mode + if (!isCommentMode) return null; + + return ( +
+
+ +
+
+ ); +} + +export default AppViewerCommentsSidebar; diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index 7b2f938da0..a7a46104dc 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -28,7 +28,6 @@ import { import { editorInitializer } from "utils/EditorUtils"; import * as Sentry from "@sentry/react"; import { getViewModePageList } from "selectors/editorSelectors"; -import AppComments from "comments/AppComments/AppComments"; import AddCommentTourComponent from "comments/tour/AddCommentTourComponent"; import CommentShowCaseCarousel from "comments/CommentsShowcaseCarousel"; import { getThemeDetails, ThemeMode } from "selectors/themeSelectors"; @@ -36,6 +35,7 @@ import { Theme } from "constants/DefaultTheme"; import GlobalHotKeys from "./GlobalHotKeys"; import { getSearchQuery } from "utils/helpers"; +import AppViewerCommentsSidebar from "./AppViewerComemntsSidebar"; const SentryRoute = Sentry.withSentryRouting(Route); @@ -153,7 +153,7 @@ class AppViewer extends Component { }} > - + 1}> {isInitialized && this.state.registered && ( diff --git a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx index 1975f5ad0a..2ddbbf609a 100644 --- a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx +++ b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx @@ -111,24 +111,20 @@ const HeaderRow = styled.div<{ justify: string }>` ${(props) => props.theme.colors.header.tabsHorizontalSeparator}; `; -const HeaderSection = styled.div<{ justify: string }>` +const HeaderSection = styled.div` display: flex; flex: 1; align-items: center; - justify-content: ${(props) => props.justify}; `; const AppsmithLogoImg = styled(AppsmithLogo)` max-width: 110px; width: 110px; - margin-right: 40px; - margin-left: 16px; `; const HeaderRightItemContainer = styled.div` display: flex; align-items: center; - margin-right: ${(props) => props.theme.spaces[7]}px; height: 100%; `; @@ -194,8 +190,8 @@ export function AppViewerHeader(props: AppViewerHeaderProps) { 1}> - - + +
@@ -207,12 +203,12 @@ export function AppViewerHeader(props: AppViewerHeaderProps) {
)}
- + {currentApplicationDetails && ( {currentApplicationDetails.name} )} - + {currentApplicationDetails && ( <> props.theme.colors.header.tabText}; height: ${(props) => `calc(${props.theme.smallHeaderHeight})`}; & span { + height: 100%; max-width: 138px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + display: flex; + align-items: center; } ${PageTab}.is-active & { color: ${(props) => props.theme.colors.header.activeTabText}; @@ -70,22 +70,14 @@ const StyleTabText = styled.div` } `; -const CenterTabNameContainer = styled.div` - position: relative; - flex: 1; - display: flex; - justify-content: center; - align-items: center; -`; - function PageTabName({ name }: { name: string }) { const tabNameRef = useRef(null); const [ellipsisActive, setEllipsisActive] = useState(false); const tabNameText = ( - +
{name} - +
); @@ -145,6 +137,7 @@ export function PageTabs(props: Props) { const { appPages, currentApplicationDetails } = props; const { pathname } = useLocation(); const location = useLocation(); + const appMode = useSelector(getAppMode); const [query, setQuery] = useState(""); useEffect(() => { @@ -152,7 +145,10 @@ export function PageTabs(props: Props) { }, [location]); return ( - +
{appPages.map((page) => ( @@ -185,7 +176,7 @@ export function PageTabs(props: Props) { ))} - +
); } diff --git a/app/client/src/pages/AppViewer/viewer/PageTabsContainer.tsx b/app/client/src/pages/AppViewer/viewer/PageTabsContainer.tsx index f28004ad12..70956132c3 100644 --- a/app/client/src/pages/AppViewer/viewer/PageTabsContainer.tsx +++ b/app/client/src/pages/AppViewer/viewer/PageTabsContainer.tsx @@ -118,7 +118,7 @@ export function PageTabsContainer(props: AppViewerHeaderProps) { }, [isScrolling, isScrollingLeft]); return appPages.length > 1 ? ( - + startScrolling(true)} onMouseLeave={stopScrolling} diff --git a/app/client/src/pages/Applications/ApplicationCard.tsx b/app/client/src/pages/Applications/ApplicationCard.tsx index 1e5570154a..4afbddcc68 100644 --- a/app/client/src/pages/Applications/ApplicationCard.tsx +++ b/app/client/src/pages/Applications/ApplicationCard.tsx @@ -269,6 +269,10 @@ const AppNameWrapper = styled.div<{ isFetching: boolean }>` word-break: break-word; color: ${(props) => props.theme.colors.text.heading}; flex: 1; + + .bp3-popover-target { + display: inline; + } `; type ApplicationCardProps = { @@ -576,7 +580,7 @@ export function ApplicationCard(props: ApplicationCardProps) { > {hasEditPermission && ( ` color: ${(props) => props.theme.colors.modal.title}; } + h2 { + display: block; + font-size: 1.5em; + margin-block-start: 0.83em; + margin-block-end: 0.83em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + } + + ul { + display: block; + list-style-type: disc; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 40px; + } + transition: max-height 0.15s ease-out; overflow: hidden; max-height: ${(props) => props.maxHeight}px; diff --git a/app/client/src/pages/Applications/index.tsx b/app/client/src/pages/Applications/index.tsx index 6a92d83ec8..548131972c 100644 --- a/app/client/src/pages/Applications/index.tsx +++ b/app/client/src/pages/Applications/index.tsx @@ -420,15 +420,6 @@ function LeftPane() { isFetchingApplications={isFetchingApplications} > - {userOrgs && - userOrgs.map((org: any) => ( - - ))} {!isFetchingApplications && fetchedUserOrgs && ( )} + {userOrgs && + userOrgs.map((org: any) => ( + + ))} ` -${(props) => { - const color = props.disabled - ? props.theme.colors.applications.orgColor - : props.theme.colors.applications.hover.orgColor[9]; - return `${textIconStyles({ - color: color, - hover: color, - })}`; -}} + ${(props) => { + const color = props.disabled + ? props.theme.colors.applications.orgColor + : props.theme.colors.applications.hover.orgColor[9]; + return `${textIconStyles({ + color: color, + hover: color, + })}`; + }} -.${Classes.ICON} { - display: ${(props) => (!props.disabled ? "inline" : "none")};; - margin-left: 8px; - color: ${(props) => props.theme.colors.applications.iconColor}; -} + .${Classes.ICON} { + display: ${(props) => (!props.disabled ? "inline" : "none")}; + margin-left: 8px; + color: ${(props) => props.theme.colors.applications.iconColor}; + } `; const OrgRename = styled(EditableText)` padding: 0 2px; @@ -774,27 +774,29 @@ function ApplicationsSection(props: any) { > {hasManageOrgPermissions && ( <> - { - return notEmptyValidator(value).message; - }} - onBlur={(value: string) => { - OrgNameChange(value, organization.id); - }} - placeholder="Workspace name" - savingState={ - isSavingOrgInfo - ? SavingState.STARTED - : SavingState.NOT_STARTED - } - underline - /> +
+ { + return notEmptyValidator(value).message; + }} + onBlur={(value: string) => { + OrgNameChange(value, organization.id); + }} + placeholder="Workspace name" + savingState={ + isSavingOrgInfo + ? SavingState.STARTED + : SavingState.NOT_STARTED + } + underline + /> +
{ try { return ( - <> - - { - if (!isMultiplayerEnabledForUser) return; - const data = getPointerData(e, pageId, isWebsocketConnected); - !!data && delayedShareMousePointer(data); - }} - width={props.dsl.rightColumn} - > - {props.dsl.widgetId && - WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)} - {isMultiplayerEnabledForUser && ( - - )} - - + { + if (!isMultiplayerEnabledForUser) return; + const data = getPointerData(e, pageId, isWebsocketConnected); + !!data && delayedShareMousePointer(data); + }} + width={props.dsl.rightColumn} + > + {props.dsl.widgetId && + WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)} + {isMultiplayerEnabledForUser && ( + + )} + ); } catch (error) { log.error("Error rendering DSL", error); diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx new file mode 100644 index 0000000000..3fd8cd1403 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -0,0 +1,20 @@ +import * as Sentry from "@sentry/react"; +import React from "react"; + +import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; + +export function CanvasPropertyPane() { + return ( +
+
+

Properties

+
+ + +
+ ); +} + +CanvasPropertyPane.displayName = "CanvasPropertyPane"; + +export default Sentry.withProfiler(CanvasPropertyPane); diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index cb5cbb24a6..b38bd3c371 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useState, lazy, Suspense } from "react"; import styled, { ThemeProvider } from "styled-components"; +import classNames from "classnames"; import { Classes as Popover2Classes } from "@blueprintjs/popover2"; import { CurrentApplicationData, @@ -10,7 +11,6 @@ import { getApplicationViewerPageURL, } from "constants/routes"; import AppInviteUsersForm from "pages/organization/AppInviteUsersForm"; -import StyledHeader from "components/designSystems/appsmith/StyledHeader"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent"; import AppsmithLogo from "assets/images/appsmith_logo_square.png"; @@ -20,6 +20,7 @@ import { getCurrentApplicationId, getCurrentPageId, getIsPublishingApplication, + previewModeSelector, } from "selectors/editorSelectors"; import { getAllUsers, getCurrentOrgId } from "selectors/organizationSelectors"; import { connect, useDispatch, useSelector } from "react-redux"; @@ -59,6 +60,7 @@ import { setIsGitSyncModalOpen } from "actions/gitSyncActions"; import RealtimeAppEditors from "./RealtimeAppEditors"; import { EditorSaveIndicator } from "./EditorSaveIndicator"; import getFeatureFlags from "utils/featureFlags"; +``; import { getIsInOnboarding } from "selectors/onboardingSelectors"; import { retryPromise } from "utils/AppsmithUtils"; import { fetchUsersForOrg } from "actions/orgActions"; @@ -71,17 +73,26 @@ import { Position } from "@blueprintjs/core/lib/esnext/common"; import { createMessage, DEPLOY_BUTTON_TOOLTIP, + LOCK_ENTITY_EXPLORER_MESSAGE, LOGO_TOOLTIP, RENAME_APPLICATION_TOOLTIP, SHARE_BUTTON_TOOLTIP, SHARE_BUTTON_TOOLTIP_WITH_USER, } from "constants/messages"; import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; +import { ReactComponent as MenuIcon } from "assets/icons/header/hamburger.svg"; +import { getExplorerPinned } from "selectors/explorerSelector"; +import { ReactComponent as UnpinIcon } from "assets/icons/ads/double-arrow-right.svg"; +import { + setExplorerActiveAction, + setExplorerPinnedAction, +} from "actions/explorerActions"; -const HeaderWrapper = styled(StyledHeader)` +const HeaderWrapper = styled.div` width: 100%; + display: flex; + align-items: center; background-color: ${(props) => props.theme.colors.header.background}; - padding: 0px ${(props) => props.theme.spaces[6]}px; height: ${(props) => props.theme.smallHeaderHeight}; flex-direction: row; box-shadow: none; @@ -124,10 +135,8 @@ const AppsmithLink = styled((props) => { // eslint-disable @typescript-eslint/no-unused-vars return ; })` - margin-right: ${(props) => props.theme.spaces[4]}px; height: 20px; width: 20px; - transform: translate(0px, 2px); display: inline-block; img { width: 20px; @@ -139,10 +148,7 @@ const DeploySection = styled.div` display: flex; `; -const ProfileDropdownContainer = styled.div` - margin: 0 10px; - margin-right: 0px; -`; +const ProfileDropdownContainer = styled.div``; const StyledInviteButton = styled(Button)` margin-right: ${(props) => props.theme.spaces[9]}px; @@ -181,24 +187,17 @@ const BindingBanner = styled.div` const StyledDeployIcon = styled(Icon)` height: 20px; + width: 20px; align-self: center; background: ${(props) => props.theme.colors.header.shareBtnHighlight}; - transform: translate(-6px, 0px); - padding-right: 4px; &:hover { background: rgb(191, 65, 9); } - - & svg { - transform: translate(3px, 0px); - } `; const ShareButton = styled.div` - display: inline-block; cursor: pointer; - margin: 4px 12px 0px 0px; `; const StyledShareText = styled.span` @@ -209,7 +208,6 @@ const StyledShareText = styled.span` const StyledSharedIcon = styled(Icon)` display: inline-block; - vertical-align: middle; `; type EditorHeaderProps = { @@ -235,7 +233,7 @@ const GlobalSearch = lazy(() => { export function ShareButtonComponent() { return ( - + SHARE @@ -255,11 +253,14 @@ export function EditorHeader(props: EditorHeaderProps) { const dispatch = useDispatch(); const isSnipingMode = useSelector(snipingModeSelector); const isSavingName = useSelector(getIsSavingAppName); + const pinned = useSelector(getExplorerPinned); const isGitConnected = useSelector(getIsGitConnected); const isErroredSavingName = useSelector(getIsErroredSavingAppName); const applicationList = useSelector(getApplicationList); const user = useSelector(getCurrentUser); const shouldHideComments = useHideComments(); + const isPreviewMode = useSelector(previewModeSelector); + useEffect(() => { if (window.location.href) { const searchParams = new URL(window.location.href).searchParams; @@ -308,6 +309,20 @@ export function EditorHeader(props: EditorHeaderProps) { } }, [getFeatureFlags().GIT, showGitSyncModal, handlePublish]); + /** + * on hovering the menu, make the explorer active + */ + const onMenuHover = useCallback(() => { + dispatch(setExplorerActiveAction(true)); + }, [setExplorerActiveAction]); + + /** + * toggles the pinned state of sidebar + */ + const onPin = useCallback(() => { + dispatch(setExplorerPinnedAction(!pinned)); + }, [pinned, dispatch, setExplorerPinnedAction]); + //Fetch all users for the application to show the share button tooltip useEffect(() => { if (orgId) { @@ -320,8 +335,36 @@ export function EditorHeader(props: EditorHeaderProps) { return ( - - + + +
+ + {createMessage(LOCK_ENTITY_EXPLORER_MESSAGE)} + Ctrl + / +
+ } + position="bottom-left" + > +
+ + +
+ +
- + - + diff --git a/app/client/src/pages/Editor/EditorSaveIndicator.tsx b/app/client/src/pages/Editor/EditorSaveIndicator.tsx index 3bbdc10c11..89ebb72a55 100644 --- a/app/client/src/pages/Editor/EditorSaveIndicator.tsx +++ b/app/client/src/pages/Editor/EditorSaveIndicator.tsx @@ -17,13 +17,9 @@ import Icon from "components/ads/Icon"; const SaveStatusContainer = styled.div` border-radius: 50%; - width: 32px; - height: 32px; display: flex; align-items: center; justify-content: center; - margin-right: 10px; - margin-top: 2px; .bp3-popover-target { display: flex; } diff --git a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx index cb9c3cfc15..1789ef2c72 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx @@ -3,6 +3,7 @@ import EditableText, { } from "components/editorComponents/EditableText"; import TooltipComponent from "components/ads/Tooltip"; import { Colors } from "constants/Colors"; +import { get } from "lodash"; import React, { forwardRef, @@ -130,7 +131,9 @@ export const EntityName = forwardRef( }); const nameUpdateError = useSelector((state: AppState) => { - return state.ui.explorer.updateEntityError === props.entityId; + return ( + get(state, "ui.explorer.entity.updateEntityError") === props.entityId + ); }); const targetRef = useRef(null); diff --git a/app/client/src/pages/Editor/Explorer/Entity/index.tsx b/app/client/src/pages/Editor/Explorer/Entity/index.tsx index 51758f3cca..a813720a8c 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/index.tsx @@ -56,8 +56,7 @@ export const EntityItem = styled.div<{ position: relative; font-size: 14px; user-select: none; - padding-left: ${(props) => - props.step * props.theme.spaces[2] + props.theme.spaces[2]}px; + padding-left: ${(props) => `calc(0.75rem + (0.25 * ${props.step}rem))`}; background: ${(props) => (props.active ? Colors.GREY_2 : "none")}; height: 30px; width: 100%; diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx index 21d31ad5f3..81ce22a010 100644 --- a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx +++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx @@ -1,8 +1,13 @@ -import React, { useRef, MutableRefObject, useCallback, useEffect } from "react"; +import React, { + useRef, + MutableRefObject, + useCallback, + useEffect, + useState, +} from "react"; import styled from "styled-components"; import Divider from "components/editorComponents/Divider"; import { - useFilteredEntities, useWidgets, useActions, useFilteredDatasources, @@ -21,8 +26,10 @@ import PerformanceTracker, { import { useDispatch, useSelector } from "react-redux"; import { getPlugins } from "selectors/entitiesSelector"; import ScrollIndicator from "components/ads/ScrollIndicator"; + import { ReactComponent as NoEntityFoundSvg } from "assets/svg/no_entities_found.svg"; import { Colors } from "constants/Colors"; + import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors"; import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions"; @@ -70,6 +77,8 @@ const StyledDivider = styled(Divider)` border-bottom-color: rgba(255, 255, 255, 0.1); `; function EntityExplorer(props: IPanelProps) { + const dispatch = useDispatch(); + const [searchKeyword, setSearchhKeyword] = useState(""); const applicationId = useSelector(getCurrentApplicationId); const searchInputRef: MutableRefObject = useRef( @@ -80,15 +89,12 @@ function EntityExplorer(props: IPanelProps) { PerformanceTracker.stopTracking(); }); const explorerRef = useRef(null); - const { clearSearch, searchKeyword } = useFilteredEntities(searchInputRef); - const datasources = useFilteredDatasources(searchKeyword); const plugins = useSelector(getPlugins); - const widgets = useWidgets(searchKeyword); const actions = useActions(searchKeyword); const jsActions = useJSCollections(searchKeyword); - const dispatch = useDispatch(); + const datasources = useFilteredDatasources(searchKeyword); const isFirstTimeUserOnboardingEnabled = useSelector( getIsFirstTimeUserOnboardingEnabled, ); @@ -122,9 +128,31 @@ function EntityExplorer(props: IPanelProps) { [openPanel, applicationId, isFirstTimeUserOnboardingEnabled], ); + /** + * filter entitites + */ + const search = (e: any) => { + setSearchhKeyword(e.target.value); + }; + + const clearSearchInput = () => { + if (searchInputRef.current) { + searchInputRef.current.value = ""; + } + + setSearchhKeyword(""); + }; + return ( - - + + {/* SEARCH */} + + ` - display: ${(props) => (props.isHidden ? "none" : "flex")}; - margin-bottom: 5px; - padding: 0 8px; - height: 48px; - justify-content: flex-start; - align-items: center; - // for the input underline - position: relative; - font-size: 14px; - z-index: 1; - background: ${Colors.ALABASTER_ALT}; - & { - .${Classes.ICON} { - color: ${Colors.GRAY}; - cursor: pointer; - width: 18px; - height: 18px; - display: flex; - justify-content: center; - align-items: center; - &:last-of-type { - color: ${Colors.GRAY}; - } - svg { - width: 16px; - height: 16px; - - &:last-of-type { - width: 14px; - height: 14px; - } - } - } - input { - display: flex; - flex: 1; - min-width: 0; //firefox flex issue fix - border: none; - background: none; - padding: 0px 10px 0px 10px; - color: ${Colors.DOVE_GRAY2}; - // normalize.css adds a line-height which clips the placeholder text on windows - // so setting it back to normal - line-height: normal; - &::placeholder { - color: ${Colors.DOVE_GRAY2}; - } - &:focus { - & ~ div.underline { - width: 100%; - } - & ~ .${Classes.ICON} { - color: ${Colors.GRAY}; - } - } - } - } -`; - -const Underline = styled.div` - position: absolute; - left: 0; - right: 0; - width: 0%; - height: 1px; - background: ${Colors.TIA_MARIA}; - bottom: 0; - transition: width 0.3s ease-in; -`; /*eslint-disable react/display-name */ export const ExplorerSearch = forwardRef( ( @@ -82,26 +14,76 @@ export const ExplorerSearch = forwardRef( placeholder?: string; autoFocus?: boolean; isHidden?: boolean; - hideClear?: boolean; + onChange?: (e: any) => void; }, ref: Ref, ) => { + const [value, setValue] = useState(""); + const [focussed, setFocussed] = useState(false); + + /** + * on change of input + */ + const onChange = useCallback((e: React.FormEvent) => { + e.persist(); + + setValue(e.currentTarget.value); + + if (isFunction(props.onChange)) { + props.onChange(e); + } + }, []); + + /** + * on click of cross button + */ + const onClear = useCallback(() => { + setValue(""); + + if (isFunction(props.onChange)) { + props.clear(); + } + }, []); + return ( - - - +
+ + setFocussed(false)} + onChange={onChange} + onFocus={() => setFocussed(true)} + placeholder="Search Widgets" + ref={ref} + type="text" + /> + {value && ( + + )} +
+
- {props.hideClear !== true && ( - - )} - - +
); }, ); diff --git a/app/client/src/pages/Editor/Explorer/JSDependencies.tsx b/app/client/src/pages/Editor/Explorer/JSDependencies.tsx index 7ef4fa0bc8..edf21f39bd 100644 --- a/app/client/src/pages/Editor/Explorer/JSDependencies.tsx +++ b/app/client/src/pages/Editor/Explorer/JSDependencies.tsx @@ -109,7 +109,7 @@ export function JSDependencies() { isVisible={!!dependencyList} onClick={toggleDependencies} /> - Dependencies + Dependencies { if (params.pageId === pageId) { flashElementsById(widgetId); } - dispatch(forceOpenPropertyPane(widgetId)); }, 0); }; diff --git a/app/client/src/pages/Editor/Explorer/hooks.ts b/app/client/src/pages/Editor/Explorer/hooks.ts index 93f5454629..83e05a727c 100644 --- a/app/client/src/pages/Editor/Explorer/hooks.ts +++ b/app/client/src/pages/Editor/Explorer/hooks.ts @@ -7,7 +7,7 @@ import { } from "react"; import { useSelector } from "react-redux"; import { AppState } from "reducers"; -import { compact, groupBy } from "lodash"; +import { compact, get, groupBy } from "lodash"; import { Datasource } from "entities/Datasource"; import { isStoredDatasource } from "entities/Action"; import { debounce } from "lodash"; @@ -267,12 +267,14 @@ export const useFilteredEntities = ( export const useEntityUpdateState = (entityId: string) => { return useSelector( - (state: AppState) => state.ui.explorer.updatingEntity === entityId, + (state: AppState) => + get(state, "ui.explorer.entity.updatingEntity") === entityId, ); }; export const useEntityEditState = (entityId: string) => { return useSelector( - (state: AppState) => state.ui.explorer.editingEntityName === entityId, + (state: AppState) => + get(state, "ui.explorer.entity.editingEntityName") === entityId, ); }; diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx index 739fcc3415..5b9ca5e012 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx @@ -120,6 +120,10 @@ const CompeleteMarkerIcon = styled.div<{ success: boolean }>` border-color: ${(props) => props.success ? props.theme.colors.success.main : Colors.SILVER_CHALICE}; padding: 2px 2px; + + .bp3-icon { + vertical-align: initial; + } `; const StyledCompleteMarker = styled.div` @@ -155,13 +159,13 @@ const BannerText = styled.p` const StyledImg = styled.img` width: 20px; - transform: translate(0px, 5px); margin-right: 5px; `; const StyledFooter = styled.div` cursor: pointer; - display: inline-block; + display: flex; + align-items: center; margin-top: ${(props) => props.theme.spaces[9]}px; `; @@ -291,7 +295,9 @@ export default function OnboardingChecklist() { /> )} - {createMessage(ONBOARDING_CHECKLIST_HEADER)} + + {createMessage(ONBOARDING_CHECKLIST_HEADER)} + {createMessage(ONBOARDING_CHECKLIST_BODY)} 1}> 1 ? theme.colors.success.main @@ -464,6 +473,7 @@ export default function OnboardingChecklist() { - triggerWelcomeTour(dispatch)}> + triggerWelcomeTour(dispatch)} + > {createMessage(ONBOARDING_CHECKLIST_FOOTER)} diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx index 7ee2d95728..065342be89 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx @@ -49,38 +49,10 @@ const ModalImgWrapper = styled.div` justify-content: center; `; -const ModalContentWrapper = styled.div` - display: flex; - & div:nth-child(1) { - flex-basis: 165px; - } - & div:nth-child(2) { - flex-basis: 265px; - margin-left: 73px; - } - & div:nth-child(3) { - flex-basis: 135px; - margin-left: 72px; - } -`; +const ModalContentWrapper = styled.div``; const StyledImgWrapper = styled.div` text-align: center; - &:nth-child(1) { - width: 25%; - position: relative; - right: -2px; - } - &:nth-child(2) { - padding-top: 28px; - width: 50%; - } - &:nth-child(3) { - padding-top: 22px; - width: 25%; - position: relative; - left: -24px; - } `; const StyledImg = styled.img` @@ -104,7 +76,6 @@ const StyledButton = styled(Button)` width: 145px; height: 38px; display: inline-block; - margin: 33px 0px 30px; `; const ModalFooterNote = styled.p` @@ -157,28 +128,28 @@ export default function IntroductionModal({ close }: IntroductionModalProps) { - - - + + + - - + + - - - + + + - +
1. {createMessage(ONBOARDING_INTRO_CONNECT_YOUR_DATABASE)}
-
+
2. - + {createMessage(ONBOARDING_INTRO_CONNECT_DATA_WIDGET)}
@@ -193,12 +164,12 @@ export default function IntroductionModal({ close }: IntroductionModalProps) { - + {createMessage(ONBOARDING_INTRO_FOOTER)}  ` - position: relative; width: 100%; background-color: ${(props) => props.active ? props.theme.colors.welcomeTourStickySidebarBackground : ""}; @@ -232,7 +231,7 @@ export function OnboardingStatusbar(props: RouteComponentProps) { return ( { history.push(getOnboardingCheckListUrl(applicationId, pageId)); @@ -251,7 +250,7 @@ export function OnboardingStatusbar(props: RouteComponentProps) { {createMessage(ONBOARDING_STATUS_GET_STARTED)} - + {content}   {!isChecklistPage && ( props.width}; + width: 100%; &&& svg { rect { diff --git a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx index a9139c2d5b..d9785886ae 100644 --- a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx +++ b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx @@ -608,14 +608,14 @@ function GeneratePageForm() { !selectedTable.value || !showSubmitButton || isSelectedTableEmpty; return ( -
+
{GENERATE_PAGE_FORM_TITLE()} - + {selectedDatasource.value ? ( - +
-

{p.name}

+

{p.name}

))} diff --git a/app/client/src/pages/Editor/MainContainer.test.tsx b/app/client/src/pages/Editor/MainContainer.test.tsx index 456d877e48..bdf87b5fdb 100644 --- a/app/client/src/pages/Editor/MainContainer.test.tsx +++ b/app/client/src/pages/Editor/MainContainer.test.tsx @@ -695,8 +695,8 @@ describe("Drag and Drop widgets into Main container", () => { cancelable: true, }), { - offsetX: 500, - offsetY: 500, + offsetX: 100, + offsetY: 100, }, ), ); diff --git a/app/client/src/pages/Editor/MainContainer.tsx b/app/client/src/pages/Editor/MainContainer.tsx index d61d6c2609..6d0aafbd1d 100644 --- a/app/client/src/pages/Editor/MainContainer.tsx +++ b/app/client/src/pages/Editor/MainContainer.tsx @@ -1,14 +1,22 @@ -import React from "react"; import styled from "styled-components"; import * as Sentry from "@sentry/react"; -import { Route, Switch } from "react-router"; -import EditorsRouter from "./routes"; -import WidgetsEditor from "./WidgetsEditor"; -import Sidebar from "components/editorComponents/Sidebar"; -import BottomBar from "./BottomBar"; +import { useDispatch } from "react-redux"; +import React, { useState, useCallback, useMemo } from "react"; +import { Route, Switch, matchPath, useLocation } from "react-router"; +import EditorsRouter from "./routes"; +import BottomBar from "./BottomBar"; +import { + DEFAULT_ENTITY_EXPLORER_WIDTH, + DEFAULT_PROPERTY_PANE_WIDTH, +} from "constants/AppConstants"; +import WidgetsEditor from "./WidgetsEditor"; +import { updateExplorerWidthAction } from "actions/explorerActions"; import { BUILDER_CHECKLIST_URL, BUILDER_URL } from "constants/routes"; import OnboardingChecklist from "./FirstTimeUserOnboarding/Checklist"; +import EntityExplorerSidebar from "components/editorComponents/Sidebar"; +import PropertyPaneSidebar from "components/editorComponents/PropertyPaneSidebar"; + const SentryRoute = Sentry.withSentryRouting(Route); const Container = styled.div` @@ -19,20 +27,76 @@ const Container = styled.div` ); background-color: ${(props) => props.theme.appBackground}; `; - -const EditorContainer = styled.div` - position: relative; - width: calc(100vw - ${(props) => props.theme.sidebarWidth}); - display: flex; - flex-direction: column; -`; - function MainContainer() { + const dispatch = useDispatch(); + const location = useLocation(); + const [sidebarWidth, setSidebarWidth] = useState( + DEFAULT_ENTITY_EXPLORER_WIDTH, + ); + const [propertyPaneWidth, setPropertyPaneWidth] = useState( + DEFAULT_PROPERTY_PANE_WIDTH, + ); + + /** + * on entity explorer sidebar width change + * + * @return void + */ + const onLeftSidebarWidthChange = useCallback((newWidth) => { + setSidebarWidth(newWidth); + }, []); + + /** + * on entity explorer sidebar drag end + * + * @return void + */ + const onLeftSidebarDragEnd = useCallback(() => { + dispatch(updateExplorerWidthAction(sidebarWidth)); + }, [sidebarWidth]); + + /** + * on property pane sidebar drag end + * + * @return void + */ + const onRightSidebarDragEnd = useCallback(() => { + dispatch(updateExplorerWidthAction(propertyPaneWidth)); + }, [propertyPaneWidth]); + + /** + * on property pane sidebar width change + */ + const onRightSidebarWidthChange = useCallback((newWidth) => { + setPropertyPaneWidth(newWidth); + }, []); + + /** + * checks if property pane should be rendered or not + * + * @return boolean + */ + const shouldRenderPropertyPane = useMemo(() => { + const match = matchPath(location.pathname, { + path: BUILDER_URL, + exact: true, + }); + + // match is found, that means current URL is BUILDER_URL i.e our editor + if (match) return true; + + return false; + }, [location]); + return ( <> - - - + + +
- +
+ {shouldRenderPropertyPane && ( + + )}
diff --git a/app/client/src/pages/Editor/MainContainerLayoutControl.tsx b/app/client/src/pages/Editor/MainContainerLayoutControl.tsx index 3f6cf59876..49fcf4e475 100644 --- a/app/client/src/pages/Editor/MainContainerLayoutControl.tsx +++ b/app/client/src/pages/Editor/MainContainerLayoutControl.tsx @@ -1,24 +1,20 @@ -import { Position } from "@blueprintjs/core"; -import { updateApplicationLayout } from "actions/applicationActions"; -import Dropdown from "components/ads/Dropdown"; -import Icon, { IconName, IconSize } from "components/ads/Icon"; -import TooltipComponent from "components/ads/Tooltip"; -import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; -import { Colors } from "constants/Colors"; -import { createMessage, LAYOUT_DROPDOWN_TOOLTIP } from "constants/messages"; -import React from "react"; +import classNames from "classnames"; import { useDispatch } from "react-redux"; -import { - AppLayoutConfig, - SupportedLayouts, -} from "reducers/entityReducers/pageListReducer"; +import React, { useMemo, useCallback } from "react"; + import { getCurrentApplicationId, getCurrentApplicationLayout, } from "selectors/editorSelectors"; import { useSelector } from "store"; -import styled from "styled-components"; -import { noop } from "utils/AppsmithUtils"; +import { Colors } from "constants/Colors"; +import { + AppLayoutConfig, + SupportedLayouts, +} from "reducers/entityReducers/pageListReducer"; +import TooltipComponent from "components/ads/Tooltip"; +import Icon, { IconName, IconSize } from "components/ads/Icon"; +import { updateApplicationLayout } from "actions/applicationActions"; interface AppsmithLayoutConfigOption { name: string; @@ -27,13 +23,18 @@ interface AppsmithLayoutConfigOption { } export const AppsmithDefaultLayout: AppLayoutConfig = { - type: "DESKTOP", + type: "FLUID", }; const AppsmithLayouts: AppsmithLayoutConfigOption[] = [ + { + name: "Fluid Width", + type: "FLUID", + icon: "fluid", + }, { name: "Desktop", - ...AppsmithDefaultLayout, + type: "DESKTOP", icon: "desktop", }, { @@ -51,99 +52,77 @@ const AppsmithLayouts: AppsmithLayoutConfigOption[] = [ type: "MOBILE", icon: "mobile", }, - { - name: "Fluid Width", - type: "FLUID", - icon: "fluid", - }, ]; -const LayoutControlWrapper = styled.div` - height: 40px; - display: flex; - justify-content: center; - align-items: center; - .layout-control { - pointer-events: all; - cursor: pointer; - font-size: 14px; - border: none; - box-shadow: none; - } - - .bp3-popover-content { - width: 329px; - background-color: ${Colors.CODE_GRAY}; - } - - & div.bp3-popover-arrow { - display: block; - path { - fill: ${Colors.CODE_GRAY}; - stroke: ${Colors.CODE_GRAY}; - } - } -`; - export function MainContainerLayoutControl() { + const dispatch = useDispatch(); const appId = useSelector(getCurrentApplicationId); const appLayout = useSelector(getCurrentApplicationLayout); - const layoutOptions = AppsmithLayouts.map((each) => { - return { - ...each, - iconSize: IconSize.SMALL, - iconColor: Colors.BLACK, - value: each.name, - onSelect: () => - updateAppLayout({ - type: each.type, - }), - }; - }); - const selectedLayout = appLayout - ? layoutOptions.find((each) => each.type === appLayout.type) - : layoutOptions[0]; - const dispatch = useDispatch(); - const updateAppLayout = (layoutConfig: AppLayoutConfig) => { - const { type } = layoutConfig; - dispatch( - updateApplicationLayout(appId || "", { - appLayout: { - type, - }, - }), + /** + * return selected layout. if there is no app + * layout, use the default one ( fluid ) + */ + const selectedLayout = useMemo(() => { + return AppsmithLayouts.find( + (each) => each.type === (appLayout?.type || AppsmithDefaultLayout.type), ); - }; + }, [appLayout]); + + /** + * updates the app layout + * + * @param layoutConfig + */ + const updateAppLayout = useCallback( + (layoutConfig: AppLayoutConfig) => { + const { type } = layoutConfig; + + dispatch( + updateApplicationLayout(appId || "", { + appLayout: { + type, + }, + }), + ); + }, + [dispatch, appLayout], + ); + return ( - -
- { - return ( - +

Canvas Size

+
+ {AppsmithLayouts.map((layoutOption: any, index: number) => { + return ( + + + + ); + })}
- +
); } diff --git a/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx b/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx new file mode 100644 index 0000000000..8e1a813444 --- /dev/null +++ b/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import * as Sentry from "@sentry/react"; +import { + createMessage, + MULTI_SELECT_PROPERTY_PANE_MESSAGE, +} from "constants/messages"; +import { useSelector } from "react-redux"; +import { getCanvasWidgets } from "selectors/entitiesSelector"; +import { getSelectedWidgets } from "selectors/ui"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; + +function MultiSelectPropertyPane() { + const { focusWidget, selectWidget } = useWidgetSelection(); + const selectedWidgets = useSelector(getSelectedWidgets); + const canvasWidgets = useSelector(getCanvasWidgets); + + return ( +
+
+

Multi

+
+ +
+

+ {createMessage(MULTI_SELECT_PROPERTY_PANE_MESSAGE)} +

+
+ {selectedWidgets.map((selectedWidgetId) => { + if (!canvasWidgets[selectedWidgetId]) return; + + return ( + + ); + })} +
+
+
+ ); +} + +MultiSelectPropertyPane.displayName = "MultiSelectPropertyPane"; + +export default Sentry.withProfiler(MultiSelectPropertyPane); diff --git a/app/client/src/pages/Editor/PropertyPane/PanelPropertiesEditor.tsx b/app/client/src/pages/Editor/PropertyPane/PanelPropertiesEditor.tsx index e4628c897b..dd63cad8a3 100644 --- a/app/client/src/pages/Editor/PropertyPane/PanelPropertiesEditor.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PanelPropertiesEditor.tsx @@ -1,9 +1,7 @@ -import React, { useCallback, useEffect, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import React, { useEffect, useMemo } from "react"; +import { useSelector } from "react-redux"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; import { WidgetProps } from "widgets/BaseWidget"; -import AnalyticsUtil from "utils/AnalyticsUtil"; import { PanelConfig, PropertyPaneConfig, @@ -17,11 +15,10 @@ import { IPanelProps } from "@blueprintjs/core"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import PropertyPaneTitle from "../PropertyPaneTitle"; import { BindingText } from "../APIEditor/Form"; -import { PropertyControlsWrapper, PropertyPaneBodyWrapper } from "."; + import { ControlIcons } from "icons/ControlIcons"; const QuestionIcon = ControlIcons.QUESTION; -const CloseIcon = ControlIcons.CLOSE_CONTROL; function PanelHeader(props: PanelHeaderProps) { return ( @@ -42,20 +39,6 @@ function PanelHeader(props: PanelHeaderProps) { ), icon: , }, - { - tooltipContent: "Close", - icon: ( - { - props.hidePropertyPane(); - e.preventDefault(); - e.stopPropagation(); - }} - /> - ), - }, ]} isPanelTitle onBackClick={props.closePanel} @@ -90,15 +73,7 @@ export function PanelPropertiesEditor( PanelPropertiesEditorPanelProps & IPanelProps, ) { - const dispatch = useDispatch(); const widgetProperties: any = useSelector(getWidgetPropsForPropertyPane); - const hidePropertyPane = useCallback(() => { - AnalyticsUtil.logEvent("PROPERTY_PANE_CLOSE_CLICK", { - widgetType: widgetProperties.type || "", - widgetId: widgetProperties.widgetId, - }); - dispatch({ type: ReduxActionTypes.HIDE_PROPERTY_PANE }); - }, [dispatch, widgetProperties.type, widgetProperties.widgetId]); const { closePanel, @@ -184,27 +159,24 @@ export function PanelPropertiesEditor( } }; return ( - <> +
- - - {panelConfigs && - generatePropertyControl(panelConfigs as PropertyPaneConfig[], { - id: widgetProperties.widgetId, - type: widgetProperties.type, - panel, - theme, - })} - - - +
+ {panelConfigs && + generatePropertyControl(panelConfigs as PropertyPaneConfig[], { + id: widgetProperties.widgetId, + type: widgetProperties.type, + panel, + theme, + })} +
+
); } @@ -222,7 +194,6 @@ interface PanelPropertiesEditorPanelProps { interface PanelHeaderProps { isEditable: boolean; widgetProperties?: WidgetProps; - hidePropertyPane: () => void; title: string; closePanel: () => void; propertyName: string; diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyHelpLabel.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyHelpLabel.tsx index 02aa62e49b..c5412f02f3 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyHelpLabel.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyHelpLabel.tsx @@ -57,9 +57,9 @@ function PropertyHelpLabel(props: Props) { ? { borderBottom: "1px dashed", width: "100%", - display: "inline-block", + display: "flex", position: "relative", - top: "-15px", + top: "-3px", } : {} } diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx index 4fe7dd59c2..bb76d23783 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx @@ -26,21 +26,23 @@ import { setCurrentTab, showDebugger } from "actions/debuggerActions"; import { getTypographyByKey } from "constants/DefaultTheme"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { Colors } from "constants/Colors"; +import { Position } from "@blueprintjs/core"; -const CONNECTION_WIDTH = 113; const CONNECTION_HEIGHT = 28; const TopLayer = styled.div` display: flex; flex: 1; justify-content: space-between; - background-color: ${Colors.GREY_1}; .connection-dropdown { box-shadow: none; border: none; - background-color: ${Colors.GREY_1}; + background-color: ${Colors.WHITE}; + padding: 0; + width: auto; } + .error { border: 1px solid ${(props) => props.theme.colors.propertyPane.connections.error}; @@ -51,16 +53,17 @@ const TopLayer = styled.div` const SelectedNodeWrapper = styled.div<{ entityCount: number; hasError: boolean; + justifyContent: string; }>` display: flex; align-items: center; - justify-content: center; + width: 100%; + justify-content: ${(props) => props.justifyContent}; color: ${(props) => props.hasError ? props.theme.colors.propertyPane.connections.error : props.theme.colors.propertyPane.connections.connectionsCount}; ${(props) => getTypographyByKey(props, "p3")} - width: 113px; opacity: ${(props) => (!!props.entityCount ? 1 : 0.5)}; & > *:nth-child(2) { @@ -167,6 +170,8 @@ type TriggerNodeProps = DefaultDropDownValueNodeProps & { iconAlignment: "LEFT" | "RIGHT"; connectionType: "INCOMING" | "OUTGOING"; hasError: boolean; + justifyContent: string; + tooltipPosition?: Position; }; const doConnectionsHaveErrors = ( @@ -274,6 +279,7 @@ const TriggerNode = memo((props: TriggerNodeProps) => { className={props.hasError ? "t--connection-error" : "t--connection"} entityCount={props.entityCount} hasError={props.hasError} + justifyContent={props.justifyContent} onClick={onClick} > {props.iconAlignment === "LEFT" && ( @@ -285,7 +291,11 @@ const TriggerNode = memo((props: TriggerNodeProps) => { /> )} - + {props.entityCount ? `${props.entityCount} ${ENTITY}` : "No Entity"} @@ -304,6 +314,8 @@ const TriggerNode = memo((props: TriggerNodeProps) => { TriggerNode.displayName = "TriggerNode"; +const selectedOption = { label: "", value: "" }; + function PropertyPaneConnections(props: PropertyPaneConnectionsProps) { const dependencies = useDependencyList(props.widgetName); const { navigateToEntity } = useEntityLink(); @@ -329,10 +341,12 @@ function PropertyPaneConnections(props: PropertyPaneConnectionsProps) { SelectedValueNode={(selectedValueProps) => ( )} className={`connection-dropdown ${ @@ -345,20 +359,22 @@ function PropertyPaneConnections(props: PropertyPaneConnectionsProps) { renderOption={(optionProps) => { return ; }} - selected={{ label: "", value: "" }} + selected={selectedOption} showDropIcon={false} showLabelOnly - width={`${CONNECTION_WIDTH}px`} + width="100%" /> {/* */} ( )} className={`connection-dropdown ${ @@ -375,7 +391,7 @@ function PropertyPaneConnections(props: PropertyPaneConnectionsProps) { selected={{ label: "", value: "" }} showDropIcon={false} showLabelOnly - width={`${CONNECTION_WIDTH}px`} + width={`100%`} /> ); diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyPaneView.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyPaneView.tsx new file mode 100644 index 0000000000..1c36eb356f --- /dev/null +++ b/app/client/src/pages/Editor/PropertyPane/PropertyPaneView.tsx @@ -0,0 +1,133 @@ +import React, { ReactElement, useCallback, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors"; +import { IPanelProps, Position } from "@blueprintjs/core"; + +import PropertyPaneTitle from "pages/Editor/PropertyPaneTitle"; +import PropertyControlsGenerator from "./Generator"; +import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; +import { deleteSelectedWidget, copyWidget } from "actions/widgetActions"; +import ConnectDataCTA, { actionsExist } from "./ConnectDataCTA"; +import PropertyPaneConnections from "./PropertyPaneConnections"; +import CopyIcon from "remixicon-react/FileCopyLineIcon"; +import DeleteIcon from "remixicon-react/DeleteBinLineIcon"; +import { WidgetType } from "constants/WidgetConstants"; + +// TODO(abhinav): The widget should add a flag in their configuration if they donot subscribe to data +// Widgets where we do not want to show the CTA +export const excludeList: WidgetType[] = [ + "CONTAINER_WIDGET", + "TABS_WIDGET", + "FORM_WIDGET", + "MODAL_WIDGET", + "DIVIDER_WIDGET", + "FILE_PICKER_WIDGET", + "BUTTON_WIDGET", + "CANVAS_WIDGET", + "AUDIO_RECORDER_WIDGET", + "IFRAME_WIDGET", + "FILE_PICKER_WIDGET", + "FILE_PICKER_WIDGET_V2", +]; + +function PropertyPaneView( + props: { + theme: EditorTheme; + } & IPanelProps, +) { + const dispatch = useDispatch(); + const { ...panel } = props; + const widgetProperties: any = useSelector(getWidgetPropsForPropertyPane); + const doActionsExist = useSelector(actionsExist); + const hideConnectDataCTA = useMemo(() => { + if (widgetProperties) { + return excludeList.includes(widgetProperties.type); + } + + return true; + }, [widgetProperties?.type, excludeList]); + + /** + * on delete button click + */ + const onDelete = useCallback(() => { + dispatch(deleteSelectedWidget(false)); + }, [dispatch]); + + /** + * on copy button click + */ + const onCopy = useCallback(() => dispatch(copyWidget(false)), [dispatch]); + + /** + * actions shown on the right of title + */ + const actions = useMemo((): Array<{ + tooltipContent: any; + tooltipPosition: Position; + icon: ReactElement; + }> => { + return [ + { + tooltipContent: "Copy Widget", + tooltipPosition: "bottom-right", + icon: ( + + ), + }, + { + tooltipContent: "Delete Widget", + tooltipPosition: "bottom-right", + icon: ( + + ), + }, + ]; + }, [onCopy, onDelete]); + + if (!widgetProperties) return null; + + return ( +
+ + +
+ {!doActionsExist && !hideConnectDataCTA && ( + + )} + + +
+
+ ); +} + +export default PropertyPaneView; diff --git a/app/client/src/pages/Editor/PropertyPane/index.tsx b/app/client/src/pages/Editor/PropertyPane/index.tsx index 5d984638d9..a9ead0ca63 100644 --- a/app/client/src/pages/Editor/PropertyPane/index.tsx +++ b/app/client/src/pages/Editor/PropertyPane/index.tsx @@ -1,396 +1,62 @@ -import React, { Component, ReactElement, useCallback, useMemo } from "react"; -import { connect, useDispatch, useSelector } from "react-redux"; -import { AppState } from "reducers"; -import { - getIsPropertyPaneVisible, - getWidgetPropsForPropertyPane, -} from "selectors/propertyPaneSelectors"; -import { PanelStack, IPanel, Classes, IPanelProps } from "@blueprintjs/core"; +import React, { useRef } from "react"; +import { useSelector } from "react-redux"; +import { PanelStack, Classes } from "@blueprintjs/core"; -import Popper from "pages/Editor/Popper"; -import { generateClassName } from "utils/generators"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; import styled from "constants/DefaultTheme"; -import { WidgetProps } from "widgets/BaseWidget"; -import PropertyPaneTitle from "pages/Editor/PropertyPaneTitle"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import * as log from "loglevel"; -import PerformanceTracker, { - PerformanceTransactionName, -} from "utils/PerformanceTracker"; -import PropertyControlsGenerator from "./Generator"; -import PaneWrapper from "components/editorComponents/PaneWrapper"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import { ThemeMode, getCurrentThemeMode } from "selectors/themeSelectors"; -import { deleteSelectedWidget, copyWidget } from "actions/widgetActions"; -import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { ControlIcons } from "icons/ControlIcons"; -import { FormIcons } from "icons/FormIcons"; -import { getProppanePreference } from "selectors/usersSelectors"; -import { PropertyPanePositionConfig } from "reducers/uiReducers/usersReducer"; import { get } from "lodash"; -import { Layers } from "constants/Layers"; -import ConnectDataCTA, { actionsExist } from "./ConnectDataCTA"; -import PropertyPaneConnections from "./PropertyPaneConnections"; -import { WidgetType } from "constants/WidgetConstants"; - -const PropertyPaneWrapper = styled(PaneWrapper)<{ - themeMode?: EditorTheme; -}>` - max-height: ${(props) => props.theme.propertyPane.height}px; - width: ${(props) => props.theme.propertyPane.width}px; - padding-top: 0px; - margin-bottom: ${(props) => props.theme.spaces[2]}px; - margin-left: ${(props) => props.theme.spaces[10]}px; - padding-top: 0px; - border-right: 0; - overflow: hidden; - text-transform: none; -`; +import { getSelectedWidgets } from "selectors/ui"; +import PropertyPaneView from "./PropertyPaneView"; const StyledPanelStack = styled(PanelStack)` - height: auto; + height: 100%; width: 100%; margin: 0; &&& .bp3-panel-stack-view { margin: 0; border: none; - background: transparent; } - overflow: visible; + overflow: hidden; position: static; &&& .${Classes.PANEL_STACK_VIEW} { position: static; overflow: hidden; + height: 100%; } `; -const CopyIcon = ControlIcons.COPY_CONTROL; -const CloseIcon = ControlIcons.CLOSE_CONTROL; -const DeleteIcon = FormIcons.DELETE_ICON; -interface PropertyPaneState { - currentPanelStack: IPanel[]; -} +function PropertyPane() { + const selectedWidgets = useSelector(getSelectedWidgets); + const panelWrapperRef = useRef(null); -export const PropertyControlsWrapper = styled.div` - overflow: hidden; - margin: ${(props) => props.theme.spaces[5]}px; - margin-top: 0px; -`; - -export const FixedHeader = styled.div` - position: fixed; - z-index: 3; -`; - -export const PropertyPaneBodyWrapper = styled.div` - margin-top: ${(props) => props.theme.propertyPane.titleHeight}px; - max-height: ${(props) => - props.theme.propertyPane.height - - (props.theme.propertyPane.titleHeight + - props.theme.propertyPane.connectionsHeight)}px; - overflow: auto; -`; - -// TODO(abhinav): The widget should add a flag in their configuration if they donot subscribe to data -// Widgets where we do not want to show the CTA -export const excludeList: WidgetType[] = [ - "CONTAINER_WIDGET", - "TABS_WIDGET", - "FORM_WIDGET", - "MODAL_WIDGET", - "DIVIDER_WIDGET", - "FILE_PICKER_WIDGET", - "BUTTON_WIDGET", - "CANVAS_WIDGET", - "AUDIO_RECORDER_WIDGET", - "IFRAME_WIDGET", - "FILE_PICKER_WIDGET", - "FILE_PICKER_WIDGET_V2", -]; - -function PropertyPaneView( - props: { - hidePropertyPane: () => void; - theme: EditorTheme; - } & IPanelProps, -) { - const { hidePropertyPane, theme, ...panel } = props; - const widgetProperties: any = useSelector(getWidgetPropsForPropertyPane); - const doActionsExist = useSelector(actionsExist); - const hideConnectDataCTA = useMemo(() => { - if (widgetProperties) { - return excludeList.includes(widgetProperties.type); - } - - return true; - }, [widgetProperties?.type, excludeList]); - - const dispatch = useDispatch(); - const handleDelete = useCallback(() => { - dispatch(deleteSelectedWidget(false)); - }, [dispatch]); - const handleCopy = useCallback(() => dispatch(copyWidget(false)), [dispatch]); - - const actions = useMemo((): Array<{ - tooltipContent: any; - icon: ReactElement; - }> => { - return [ - { - tooltipContent: "Copy Widget", - icon: ( - - ), - }, - { - tooltipContent: "Delete Widget", - icon: ( - - ), - }, - { - tooltipContent: "Close", - icon: ( - { - AnalyticsUtil.logEvent("PROPERTY_PANE_CLOSE_CLICK", { - widgetType: widgetProperties.widgetType, - widgetId: widgetProperties.widgetId, - }); - hidePropertyPane(); - e.preventDefault(); - e.stopPropagation(); - }} - width={16} - /> - ), - }, - ]; - }, [hidePropertyPane, handleCopy, handleDelete]); - if (!widgetProperties) return null; + // TODO: add analytics code + if ( + selectedWidgets.length === 0 || + get(selectedWidgets, "0.disablePropertyPane") + ) { + return null; + } return ( - <> - { + e.stopPropagation(); + }} + ref={panelWrapperRef} + > + { + const parent = panelWrapperRef.current; + parent?.scrollTo(0, 0); + }} + showPanelHeader={false} /> - - {!doActionsExist && !hideConnectDataCTA && ( - - )} - - - - - +
); } -class PropertyPane extends Component { - private panelWrapperRef = React.createRef(); - - getTheme() { - return EditorTheme.LIGHT; - } - - getPopperTheme() { - return ThemeMode.LIGHT; - } - - onPositionChange = (position: any) => { - this.props.setPropPanePoistion( - position, - this.props.widgetProperties?.widgetId, - ); - }; - - render() { - if ( - !get(this.props, "widgetProperties") || - get(this.props, "widgetProperties.disablePropertyPane") - ) { - return null; - } - - if (this.props.isVisible) { - log.debug("Property pane rendered"); - const content = this.renderPropertyPane(); - const el = document.getElementsByClassName( - generateClassName(this.props.widgetProperties?.widgetId), - )[0]; - return ( - - {content} - - ); - } else { - return null; - } - } - - renderPropertyPane() { - const { widgetProperties } = this.props; - - if (!widgetProperties) { - return null; - } - - // if settings control is disabled, don't render anything - // for e.g - this will be true for list widget tempalte container widget - if (widgetProperties?.disablePropertyPane) return null; - - return ( - { - e.stopPropagation(); - }} - ref={this.panelWrapperRef} - themeMode={this.getTheme()} - > - - { - const parent = this.panelWrapperRef.current; - parent?.scrollTo(0, 0); - }} - showPanelHeader={false} - /> - - ); - } - - componentDidMount() { - PerformanceTracker.stopTracking( - PerformanceTransactionName.OPEN_PROPERTY_PANE, - ); - } - - componentDidUpdate(prevProps: PropertyPaneProps) { - if ( - this.props.widgetProperties?.widgetId !== - prevProps.widgetProperties?.widgetId && - this.props.widgetProperties?.widgetId !== undefined - ) { - PerformanceTracker.stopTracking( - PerformanceTransactionName.OPEN_PROPERTY_PANE, - ); - if (prevProps.widgetProperties?.widgetId && prevProps.widgetProperties) { - AnalyticsUtil.logEvent("PROPERTY_PANE_CLOSE", { - widgetType: prevProps.widgetProperties.type, - widgetId: prevProps.widgetProperties.widgetId, - }); - } - if (this.props.widgetProperties) { - AnalyticsUtil.logEvent("PROPERTY_PANE_OPEN", { - widgetType: this.props.widgetProperties.type, - widgetId: this.props.widgetProperties.widgetId, - }); - } - } - - if ( - this.props.widgetProperties?.widgetId === - prevProps.widgetProperties?.widgetId && - this.props.isVisible && - !prevProps.isVisible && - this.props.widgetProperties !== undefined - ) { - AnalyticsUtil.logEvent("PROPERTY_PANE_OPEN", { - widgetType: this.props.widgetProperties.type, - widgetId: this.props.widgetProperties.widgetId, - }); - } - - return true; - } -} - -const mapStateToProps = (state: AppState) => { - return { - widgetProperties: getWidgetPropsForPropertyPane(state), - isVisible: getIsPropertyPaneVisible(state), - themeMode: getCurrentThemeMode(state), - propPanePreference: getProppanePreference(state), - }; -}; - -const mapDispatchToProps = (dispatch: any): PropertyPaneFunctions => { - return { - setPropPanePoistion: (position: any, widgetId: any) => { - dispatch({ - type: ReduxActionTypes.PROP_PANE_MOVED, - payload: { - position: { - left: position.left, - top: position.top, - }, - }, - }); - dispatch(selectWidgetInitAction(widgetId)); - }, - hidePropertyPane: () => - dispatch({ - type: ReduxActionTypes.HIDE_PROPERTY_PANE, - }), - }; -}; - -export interface PropertyPaneProps extends PropertyPaneFunctions { - widgetProperties?: WidgetProps; - isVisible: boolean; - themeMode: ThemeMode; - propPanePreference?: PropertyPanePositionConfig; -} - -export interface PropertyPaneFunctions { - hidePropertyPane: () => void; - setPropPanePoistion: (position: any, widgetId: any) => void; -} - -export default connect(mapStateToProps, mapDispatchToProps)(PropertyPane); +export default PropertyPane; diff --git a/app/client/src/pages/Editor/PropertyPaneHelpButton.tsx b/app/client/src/pages/Editor/PropertyPaneHelpButton.tsx index 59a16c7715..1987fc1aff 100644 --- a/app/client/src/pages/Editor/PropertyPaneHelpButton.tsx +++ b/app/client/src/pages/Editor/PropertyPaneHelpButton.tsx @@ -1,14 +1,14 @@ import React, { useCallback } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { ControlIcons } from "icons/ControlIcons"; import { setGlobalSearchQuery, toggleShowGlobalSearchModal, } from "actions/globalSearchActions"; -import { getSelectedWidget } from "sagas/selectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import WidgetFactory from "utils/WidgetFactory"; +import { ControlIcons } from "icons/ControlIcons"; +import { getSelectedWidget } from "sagas/selectors"; const QuestionIcon = ControlIcons.QUESTION; @@ -19,7 +19,10 @@ function PropertyPaneHelpButton() { const displayName = WidgetFactory.widgetConfigMap.get(selectedWidgetType)?.displayName || ""; - const openHelpModal = useCallback(() => { + /** + * on click open the omnibar and toggle global search + */ + const onClick = useCallback(() => { dispatch(setGlobalSearchQuery(displayName)); dispatch(toggleShowGlobalSearchModal()); AnalyticsUtil.logEvent("OPEN_OMNIBAR", { @@ -27,7 +30,11 @@ function PropertyPaneHelpButton() { }); }, [selectedWidgetType]); - return ; + return ( + + ); } export default PropertyPaneHelpButton; diff --git a/app/client/src/pages/Editor/PropertyPaneTitle.tsx b/app/client/src/pages/Editor/PropertyPaneTitle.tsx index 707dd44e65..1bb932ea33 100644 --- a/app/client/src/pages/Editor/PropertyPaneTitle.tsx +++ b/app/client/src/pages/Editor/PropertyPaneTitle.tsx @@ -6,11 +6,13 @@ import React, { useRef, useState, } from "react"; +import { isEqual } from "lodash"; import { useDispatch, useSelector } from "react-redux"; import EditableText, { EditInteractionKind, SavingState, } from "components/ads/EditableText"; +import { Position } from "@blueprintjs/core"; import { updateWidgetName } from "actions/propertyPaneActions"; import { AppState } from "reducers"; import { getExistingWidgetNames } from "sagas/selectors"; @@ -18,76 +20,9 @@ import { removeSpecialChars } from "utils/helpers"; import { useToggleEditWidgetName } from "utils/hooks/dragResizeHooks"; import { WidgetType } from "constants/WidgetConstants"; -import styled from "constants/DefaultTheme"; -import { ControlIcons } from "icons/ControlIcons"; -import { AnyStyledComponent } from "styled-components"; -import { Classes as BlueprintClasses } from "@blueprintjs/core"; + import TooltipComponent from "components/ads/Tooltip"; -import { isEqual } from "lodash"; -import { Colors } from "constants/Colors"; - -const FixedTitle = styled.div` - position: fixed; - z-index: 3; - width: ${(props) => props.theme.propertyPane.width}px; - padding: 0px ${(props) => props.theme.spaces[5]}px; -`; - -const Wrapper = styled.div<{ iconCount: number }>` - display: grid; - grid-template-columns: 1fr repeat(${(props) => props.iconCount}, 25px); - justify-items: center; - align-items: center; - height: ${(props) => props.theme.propertyPane.titleHeight}px; - background-color: ${Colors.GREY_1}; - & span.${BlueprintClasses.POPOVER_TARGET} { - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - } - &&& .${BlueprintClasses.EDITABLE_TEXT} { - height: auto; - padding: 0; - width: 100%; - } - &&& - .${BlueprintClasses.EDITABLE_TEXT_CONTENT}, - &&& - .${BlueprintClasses.EDITABLE_TEXT_INPUT} { - color: ${(props) => props.theme.colors.propertyPane.title}; - font-size: ${(props) => props.theme.fontSizes[4]}px; - } - && svg path { - fill: ${(props) => props.theme.colors.propertyPane.label}; - } -`; - -const NameWrapper = styled.div<{ isPanelTitle?: boolean }>` - display: ${(props) => (props.isPanelTitle ? "flex" : "block")}; - align-items: center; - min-width: 100%; - padding-right: 25px; - max-width: 134px; - &&&&&&& > * { - overflow: hidden; - } -`; - -const StyledBackIcon = styled(ControlIcons.BACK_CONTROL as AnyStyledComponent)` - padding: 0; - position: relative; - cursor: pointer; - top: 3px; - margin-right: 8px; - && svg { - width: 16px; - height: 16px; - path { - fill: ${(props) => props.theme.colors.propertyPane.label}; - } - } -`; +import { ReactComponent as BackIcon } from "assets/icons/control/back.svg"; type PropertyPaneTitleProps = { title: string; @@ -99,6 +34,7 @@ type PropertyPaneTitleProps = { actions: Array<{ tooltipContent: any; icon: ReactElement; + tooltipPosition?: Position; }>; }; @@ -160,49 +96,48 @@ const PropertyPaneTitle = memo(function PropertyPaneTitle( }, [props.title]); return props.widgetId || props.isPanelTitle ? ( - - - - <> - {props.isPanelTitle && ( - - )} - - - - +
+ {/* BACK BUTTON */} + {props.isPanelTitle && ( + + )} + {/* EDITABLE TEXT */} +
+ +
+ {/* ACTIONS */} +
{props.actions.map((value, index) => ( {value.icon} ))} - - +
+
) : null; }); export default PropertyPaneTitle; diff --git a/app/client/src/pages/Editor/QueryEditor/Table.tsx b/app/client/src/pages/Editor/QueryEditor/Table.tsx index fdc379a406..9ca267a09b 100644 --- a/app/client/src/pages/Editor/QueryEditor/Table.tsx +++ b/app/client/src/pages/Editor/QueryEditor/Table.tsx @@ -13,6 +13,7 @@ import ErrorBoundary from "components/editorComponents/ErrorBoundry"; import { CellWrapper } from "widgets/TableWidget/component/TableStyledWrappers"; import AutoToolTipComponent from "widgets/TableWidget/component/AutoToolTipComponent"; import { Theme } from "constants/DefaultTheme"; +import { uniqueId } from "lodash"; interface TableProps { data: Record[]; @@ -194,7 +195,24 @@ const renderCell = (props: any) => { }; function Table(props: TableProps) { - const data = React.useMemo(() => props.data, [props.data]); + const data = React.useMemo(() => { + const emptyString = ""; + /* Check for length greater than 0 of rows returned from the query for mappings keys */ + if (props.data?.length > 0) { + const keys = Object.keys(props.data[0]); + keys.forEach((key) => { + if (key === emptyString) { + const value = props.data[0][key]; + delete props.data[0][key]; + props.data[0][uniqueId()] = value; + } + }); + + return props.data; + } + + return []; + }, [props.data]); const columns = React.useMemo(() => { if (data.length) { return Object.keys(data[0]).map((key: any) => { diff --git a/app/client/src/pages/Editor/SaaSEditor/ActionForm.tsx b/app/client/src/pages/Editor/SaaSEditor/ActionForm.tsx index 3903839c3d..cf5bee54d0 100644 --- a/app/client/src/pages/Editor/SaaSEditor/ActionForm.tsx +++ b/app/client/src/pages/Editor/SaaSEditor/ActionForm.tsx @@ -27,11 +27,10 @@ import { import { getConfigInitialValues } from "components/formControls/utils"; import { merge } from "lodash"; import { Datasource } from "entities/Datasource"; - import { INTEGRATION_EDITOR_URL, INTEGRATION_TABS } from "constants/routes"; import { diff, Diff } from "deep-diff"; import { getCurrentApplicationId } from "selectors/editorSelectors"; -import * as Sentry from "@sentry/react"; +import { getPathAndValueFromActionDiffObject } from "../../../utils/getPathAndValueFromActionDiffObject"; type StateAndRouteProps = EditorJSONtoFormProps & { actionObjectDiff?: any; @@ -58,64 +57,17 @@ function ActionForm(props: Props) { const applicationId = useSelector(getCurrentApplicationId); - //Following if block is the fix for the missing where key - /** - * NOTE: - * Action object returned by getAction comes from state.entities.action - * action api's payload is created from state.entities.action and response is saved in the same key - * Data passed to redux form is the merge of values present in state.entities.action, editorConfig, settingsConfig and has the correct datastrucure - * Data structure in state.entities.action is not correct - * Q. What does the following fix do? - * A. It calculates the diff between merged values and state.entities.action and saves the same in state.entities.action - * There is another key form that holds the formData - */ - if (!!props.actionObjectDiff) { - let path = ""; - let value = ""; - // Loop through the diff objects in difference Array - for (let i = 0; i < props.actionObjectDiff.length; i++) { - //kind = N indicates a newly added property/element - //This property is present in initialValues but not in action object - if ( - props.actionObjectDiff && - props.actionObjectDiff.hasOwnProperty("kind") && - props.actionObjectDiff.path && - Array.isArray(props.actionObjectDiff.path) && - props.actionObjectDiff.path.length && - props.actionObjectDiff[i]?.kind === "N" - ) { - // Calculate path from path[] in diff - path = props.actionObjectDiff[i].path.reduce( - (acc: string, item: number | string) => { - try { - if (typeof item === "string" && acc) { - acc += `${path}.${item}`; - } else if (typeof item === "string" && !acc) { - acc += `${item}`; - } else acc += `${path}[${item}]`; - return acc; - } catch (error) { - Sentry.captureException({ - message: `Adding key: where failed, cannot create path`, - oldData: props.actionObjectDiff, - }); - } - }, - "", - ); - // get value from diff object - value = props.actionObjectDiff[i]?.rhs; - } - } - if (value && path) { - dispatch( - setActionProperty({ - actionId: apiId, - propertyName: path, - value: value, - }), - ); - } + const { path = "", value = "" } = { + ...getPathAndValueFromActionDiffObject(props.actionObjectDiff), + }; + if (value && path) { + dispatch( + setActionProperty({ + actionId: apiId, + propertyName: path, + value: value, + }), + ); } const onRunClick = () => { diff --git a/app/client/src/pages/Editor/SaaSEditor/__data__/FinalState.json b/app/client/src/pages/Editor/SaaSEditor/__data__/FinalState.json new file mode 100644 index 0000000000..a7d592a3f8 --- /dev/null +++ b/app/client/src/pages/Editor/SaaSEditor/__data__/FinalState.json @@ -0,0 +1,2814 @@ +{ + "entities": { + "actions": [ + { + "isLoading": false, + "config": { + "id": "6194d8e1abb49e2fd00812b2", + "organizationId": "5e1c5b2014edee5e49efc06c", + "pluginType": "SAAS", + "pluginId": "6080f9266b8cfd602957ba72", + "name": "Api1", + "datasource": { + "id": "6143187b6371c22cc093fdcf", + "userPermissions": [], + "pluginId": "6080f9266b8cfd602957ba72", + "messages": [], + "isValid": true, + "new": false + }, + "pageId": "6194d8cdabb49e2fd00812b1", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "pluginSpecifiedTemplates": [ + { + "key": "method", + "value": "GET" + }, + { + "key": "sheetUrl", + "value": "https://docs.google.com/spreadsheets/d/1HYjlH7T4ii_NZzCg6LvGG0hnqfNOu35XSl-OTo_4K7Y/edit#gid=1562690580" + }, + { + "key": "range", + "value": "" + }, + { + "key": "spreadsheetName", + "value": "" + }, + { + "key": "tableHeaderIndex", + "value": "1" + }, + { + "key": "queryFormat", + "value": "ROWS" + }, + { + "key": "rowLimit", + "value": "10" + }, + { + "key": "sheetName", + "value": "Sheet1" + }, + { + "key": "rowOffset", + "value": "0" + }, + { + "key": "rowObject" + }, + { + "key": "rowObjects" + }, + { + "key": "rowIndex", + "value": "0" + }, + { + "key": "deleteFormat", + "value": "SHEET" + }, + { + "key": "smartSubstitution", + "value": true + }, + { + "key": "where", + "value": [ + {} + ] + } + ] + }, + "executeOnLoad": false, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [ + "read:actions", + "execute:actions", + "manage:actions" + ], + "validName": "Api1" + } + } + ], + "datasources": "undefined", + "pageList": "undefined", + "jsExecutions": {}, + "plugins": { + "list": [], + "loading": false, + "formConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "sectionName": "Connection", + "children": [ + { + "label": "Use Mongo Connection String URI Key", + "configProperty": "datasourceConfiguration.properties[0].key", + "controlType": "INPUT_TEXT", + "initialValue": "Use Mongo Connection String URI", + "hidden": true + }, + { + "label": "Use Mongo Connection String URI", + "configProperty": "datasourceConfiguration.properties[0].value", + "controlType": "DROP_DOWN", + "initialValue": "No", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "label": "Connection String URI Key", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "initialValue": "Connection String URI", + "hidden": true + }, + { + "label": "Connection String URI", + "placeholderText": "mongodb+srv://:@test-db.swrsq.mongodb.net/myDatabase", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "NOT_EQUALS", + "value": "Yes" + } + }, + { + "label": "Connection Mode", + "configProperty": "datasourceConfiguration.connection.mode", + "controlType": "DROP_DOWN", + "initialValue": "READ_WRITE", + "options": [ + { + "label": "Read Only", + "value": "READ_ONLY" + }, + { + "label": "Read / Write", + "value": "READ_WRITE" + } + ], + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "label": "Connection Type", + "configProperty": "datasourceConfiguration.connection.type", + "initialValue": "DIRECT", + "controlType": "DROP_DOWN", + "options": [ + { + "label": "Direct Connection", + "value": "DIRECT" + }, + { + "label": "Replica set", + "value": "REPLICA_SET" + } + ], + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$", + "placeholderText": "myapp.abcde.mongodb.net", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + } + ] + }, + { + "label": "Default Database Name", + "placeholderText": "(Optional)", + "configProperty": "datasourceConfiguration.connection.defaultDatabaseName", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + } + ] + }, + { + "sectionName": "Authentication", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + }, + "children": [ + { + "label": "Database Name", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "placeholderText": "Database name", + "initialValue": "admin" + }, + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authType", + "controlType": "DROP_DOWN", + "initialValue": "SCRAM_SHA_1", + "options": [ + { + "label": "SCRAM-SHA-1", + "value": "SCRAM_SHA_1" + }, + { + "label": "SCRAM-SHA-256", + "value": "SCRAM_SHA_256" + }, + { + "label": "MONGODB-CR", + "value": "MONGODB_CR" + } + ] + }, + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + }, + { + "sectionName": "SSL (optional)", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + }, + "children": [ + { + "label": "SSL Mode", + "configProperty": "datasourceConfiguration.connection.ssl.authType", + "controlType": "DROP_DOWN", + "initialValue": "DEFAULT", + "options": [ + { + "label": "Default", + "value": "DEFAULT" + }, + { + "label": "Enabled", + "value": "ENABLED" + }, + { + "label": "Disabled", + "value": "DISABLED" + } + ] + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "Connection", + "id": 1, + "children": [ + { + "label": "Connection Mode", + "configProperty": "datasourceConfiguration.connection.mode", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "READ_WRITE", + "options": [ + { + "label": "Read Only", + "value": "READ_ONLY" + }, + { + "label": "Read / Write", + "value": "READ_WRITE" + } + ] + }, + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$" + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY" + } + ] + }, + { + "label": "Database Name", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "placeholderText": "Database name", + "initialValue": "admin" + } + ] + }, + { + "sectionName": "Authentication", + "id": 2, + "children": [ + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + }, + { + "id": 3, + "sectionName": "SSL (optional)", + "children": [ + { + "label": "SSL Mode", + "configProperty": "datasourceConfiguration.connection.ssl.authType", + "controlType": "DROP_DOWN", + "initialValue": "DEFAULT", + "options": [ + { + "label": "Default", + "value": "DEFAULT" + }, + { + "label": "Allow", + "value": "ALLOW" + }, + { + "label": "Prefer", + "value": "PREFER" + }, + { + "label": "Require", + "value": "REQUIRE" + }, + { + "label": "Disable", + "value": "DISABLE" + } + ] + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "General", + "children": [ + { + "label": "URL", + "configProperty": "datasourceConfiguration.url", + "controlType": "INPUT_TEXT", + "isRequired": true, + "placeholderText": "https://example.com" + }, + { + "label": "Headers", + "configProperty": "datasourceConfiguration.headers", + "controlType": "KEY_VAL_INPUT" + }, + { + "label": "Send Authentication Information Key (Do not edit)", + "configProperty": "datasourceConfiguration.properties[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "isSendSessionEnabled" + }, + { + "label": "Send Appsmith signature header (X-APPSMITH-SIGNATURE)", + "configProperty": "datasourceConfiguration.properties[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "N", + "options": [ + { + "label": "Yes", + "value": "Y" + }, + { + "label": "No", + "value": "N" + } + ] + }, + { + "label": "Session Details Signature Key Key (Do not edit)", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sessionSignatureKey" + }, + { + "label": "Session Details Signature Key", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "N" + } + }, + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authenticationType", + "controlType": "DROP_DOWN", + "options": [ + { + "label": "None", + "value": "dbAuth" + }, + { + "label": "Basic", + "value": "basic" + }, + { + "label": "OAuth 2.0", + "value": "oAuth2" + }, + { + "label": "API Key", + "value": "apiKey" + }, + { + "label": "Bearer Token", + "value": "bearerToken" + } + ] + }, + { + "label": "Grant Type", + "configProperty": "datasourceConfiguration.authentication.grantType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true + }, + { + "label": "Access Token URL", + "configProperty": "datasourceConfiguration.authentication.accessTokenUrl", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Client Id", + "configProperty": "datasourceConfiguration.authentication.clientId", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Client Secret", + "configProperty": "datasourceConfiguration.authentication.clientSecret", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Scope(s)", + "configProperty": "datasourceConfiguration.authentication.scopeString", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Header Prefix", + "configProperty": "datasourceConfiguration.authentication.headerPrefix", + "controlType": "INPUT_TEXT", + "placeholderText": "Bearer (default)", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Add token to", + "configProperty": "datasourceConfiguration.authentication.isTokenHeader", + "controlType": "DROP_DOWN", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + }, + "options": [ + { + "label": "Header", + "value": true + }, + { + "label": "Query parameters", + "value": false + } + ] + }, + { + "label": "Audience(s)", + "configProperty": "datasourceConfiguration.authentication.audience", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Resource(s)", + "configProperty": "datasourceConfiguration.authentication.resource", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "Details", + "id": 1, + "children": [ + { + "label": "Database URL", + "configProperty": "datasourceConfiguration.url", + "controlType": "INPUT_TEXT", + "isRequired": true, + "placeholderText": "https://your-project-id.firebaseio.com" + }, + { + "label": "Project Id", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "isRequired": true, + "initialValue": "" + }, + { + "label": "Service Account Credentials", + "configProperty": "datasourceConfiguration.authentication.password", + "controlType": "INPUT_TEXT", + "dataType": "PASSWORD", + "isRequired": true, + "initialValue": "" + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "General", + "children": [ + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authenticationType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true, + "initialValue": "oAuth2" + }, + { + "label": "Grant Type", + "configProperty": "datasourceConfiguration.authentication.grantType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true, + "initialValue": "authorization_code" + }, + { + "label": "Scope", + "configProperty": "datasourceConfiguration.authentication.scopeString", + "controlType": "DROP_DOWN", + "isRequired": true, + "options": [ + { + "label": "Read Only", + "value": "https://www.googleapis.com/auth/spreadsheets.readonly,https://www.googleapis.com/auth/drive.readonly" + }, + { + "label": "Read and Write", + "value": "https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/drive" + } + ], + "initialValue": "https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/drive" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "sectionName": "Details", + "id": 1, + "children": [ + { + "label": "S3 Service Provider Key", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "initialValue": "s3Provider", + "hidden": true + }, + { + "label": "S3 Service Provider", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "amazon-s3", + "options": [ + { + "label": "Amazon S3", + "value": "amazon-s3" + }, + { + "label": "Upcloud", + "value": "upcloud" + }, + { + "label": "Digital Ocean Spaces", + "value": "digital-ocean-spaces" + }, + { + "label": "Wasabi", + "value": "wasabi" + }, + { + "label": "DreamObjects", + "value": "dream-objects" + }, + { + "label": "Other", + "value": "other" + } + ] + }, + { + "label": "Access Key", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Secret Key", + "configProperty": "datasourceConfiguration.authentication.password", + "controlType": "INPUT_TEXT", + "dataType": "PASSWORD", + "initialValue": "", + "encrypted": true + }, + { + "label": "Endpoint URL", + "configProperty": "datasourceConfiguration.endpoints[0].host", + "controlType": "INPUT_TEXT", + "initialValue": "", + "placeholderText": "user-storage.de-fra1.upcloudobjects.com", + "hidden": { + "path": "datasourceConfiguration.properties[1].value", + "comparison": "EQUALS", + "value": "amazon-s3" + } + }, + { + "label": "Custom Endpoint URL Key", + "configProperty": "datasourceConfiguration.properties[2].key", + "controlType": "INPUT_TEXT", + "initialValue": "customRegion", + "hidden": true + }, + { + "label": "Region", + "configProperty": "datasourceConfiguration.properties[2].value", + "controlType": "INPUT_TEXT", + "initialValue": "", + "placeholderText": "de-fra1", + "hidden": { + "path": "datasourceConfiguration.properties[1].value", + "comparison": "NOT_EQUALS", + "value": "other" + } + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "Connection", + "id": 1, + "children": [ + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$" + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY" + } + ] + }, + { + "label": "Database Number", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER", + "placeholderText": "0" + } + ] + }, + { + "sectionName": "Authentication", + "id": 2, + "children": [ + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "editorConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "options": [ + { + "label": "Find Document(s)", + "value": "FIND" + }, + { + "label": "Insert Document(s)", + "value": "INSERT" + }, + { + "label": "Update Document(s)", + "value": "UPDATE" + }, + { + "label": "Delete Document(s)", + "value": "DELETE" + }, + { + "label": "Count", + "value": "COUNT" + }, + { + "label": "Distinct", + "value": "DISTINCT" + }, + { + "label": "Aggregate", + "value": "AGGREGATE" + }, + { + "label": "Raw", + "value": "RAW" + } + ], + "label": "Commands", + "description": "Choose method you would like to use to query the database", + "configProperty": "actionConfiguration.formData.command", + "controlType": "DROP_DOWN", + "initialValue": "FIND" + }, + { + "controlType": "SECTION", + "label": "", + "_comment": "This section holds all the templates", + "children": [ + { + "identifier": "FIND", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'FIND'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.find.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Sort", + "configProperty": "actionConfiguration.formData.find.sort", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{name : 1}" + }, + { + "label": "Projection", + "configProperty": "actionConfiguration.formData.find.projection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{name : 1}" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.find.limit", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "10" + }, + { + "label": "Skip", + "configProperty": "actionConfiguration.formData.find.skip", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "0" + } + ] + } + ] + }, + { + "identifier": "INSERT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'INSERT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Documents", + "configProperty": "actionConfiguration.formData.insert.documents", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "[ { _id: 1, user: \"abc123\", status: \"A\" } ]" + } + ] + } + ] + }, + { + "identifier": "UPDATE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'UPDATE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.updateMany.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Update", + "configProperty": "actionConfiguration.formData.updateMany.update", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{ $inc: { score: 1 } }" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.updateMany.limit", + "controlType": "DROP_DOWN", + "initialValue": "SINGLE", + "options": [ + { + "label": "Single Document", + "value": "SINGLE" + }, + { + "label": "All Matching Documents", + "value": "ALL" + } + ] + } + ] + } + ] + }, + { + "identifier": "DELETE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DELETE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.delete.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.delete.limit", + "controlType": "DROP_DOWN", + "initialValue": "SINGLE", + "options": [ + { + "label": "Single Document", + "value": "SINGLE" + }, + { + "label": "All Matching Documents", + "value": "ALL" + } + ] + } + ] + } + ] + }, + { + "identifier": "COUNT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'COUNT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.count.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + } + ] + } + ] + }, + { + "identifier": "DISTINCT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DISTINCT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.distinct.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Key", + "configProperty": "actionConfiguration.formData.distinct.key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "name" + } + ] + } + ] + }, + { + "identifier": "AGGREGATE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'AGGREGATE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Array of Pipelines", + "configProperty": "actionConfiguration.formData.aggregate.arrayPipelines", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "[{ $project: { tags: 1 } }, { $unwind: \"$tags\" }, { $group: { _id: \"$tags\", count: { $sum : 1 } } } ]" + } + ] + } + ] + }, + { + "identifier": "RAW", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "", + "propertyName": "rawWithSmartSubstitute", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW' && actionConfiguration.formData.smartSubstitution === true}}" + } + }, + { + "label": "", + "configProperty": "actionConfiguration.body", + "propertyName": "rawWithTemplateSubstitute", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW' && actionConfiguration.formData.smartSubstitution === false}}" + } + } + ] + } + ] + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "PARAMETER", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": false + } + }, + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": true + } + }, + { + "label": "Use Prepared Statement", + "info": "Turning on Prepared Statement makes your queries resilient against bad things like SQL injections. However, it cannot be used if your dynamic binding contains any SQL keywords like 'SELECT', 'WHERE', 'AND', etc.", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "SWITCH", + "initialValue": true + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT" + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": false + } + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": true + } + }, + { + "label": "Query Parameters", + "configProperty": "actionConfiguration.queryParameters", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + }, + { + "label": "Headers", + "configProperty": "actionConfiguration.headers", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + }, + { + "label": "Form data", + "configProperty": "actionConfiguration.bodyFormData", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Method Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "method" + }, + { + "label": "Method", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "GET_DOCUMENT", + "options": [ + { + "label": "Get Single Document", + "value": "GET_DOCUMENT" + }, + { + "label": "Get Documents in Collection", + "value": "GET_COLLECTION" + }, + { + "label": "Set Document", + "value": "SET_DOCUMENT" + }, + { + "label": "Create Document", + "value": "CREATE_DOCUMENT" + }, + { + "label": "Add Document to Collection", + "value": "ADD_TO_COLLECTION" + }, + { + "label": "Update Document", + "value": "UPDATE_DOCUMENT" + }, + { + "label": "Delete Document", + "value": "DELETE_DOCUMENT" + } + ] + }, + { + "label": "Collection/Document Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "isRequired": true, + "initialValue": "" + }, + { + "label": "Timestamp Value Path Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].key", + "controlType": "INPUT_TEXT", + "initialValue": "timestampValuePath", + "hidden": true + }, + { + "label": "Timestamp Value Path (use dot(.) notation to reference nested key e.g. [\"key1.key2\"])", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "", + "placeholderText": "[\"checkinLog.timestampKey\", \"auditLog.timestampKey\"]", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT" + ] + } + }, + { + "label": "Delete Key Value Pair Path Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].key", + "controlType": "INPUT_TEXT", + "initialValue": "deleteKeyValuePairPath", + "hidden": true + }, + { + "label": "Delete Key Value Pair Path (use dot(.) notation to reference nested key e.g. [\"key1.key2\"])", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "", + "placeholderText": "[\"userKey.nestedNamekey\", \"cityKey.nestedPincodeKey\"]", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT", + "CREATE_DOCUMENT", + "ADD_TO_COLLECTION", + "SET_DOCUMENT" + ] + } + }, + { + "label": "Order By Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "orderBy" + }, + { + "label": "Order By (JSON array of field names to order by)", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "placeholderText": "[\"ascendingField\", \"-descendingField\", \"nestedObj.field\"]", + "initialValue": "" + }, + { + "label": "Start After Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "Start After", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "" + }, + { + "label": "End Before Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "End Before", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "" + }, + { + "label": "Limit Documents Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "Limit Documents", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "10" + }, + { + "label": "Where Conditions Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "whereConditionTuples" + }, + { + "label": "Where Conditions", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].value", + "controlType": "ARRAY_FIELD", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "schema": [ + { + "label": "Path", + "key": "path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "key1/nestedKey2" + }, + { + "label": "Operator", + "key": "operator", + "controlType": "DROP_DOWN", + "initialValue": "EQ", + "options": [ + { + "label": "<", + "value": "LT" + }, + { + "label": "<=", + "value": "LTE" + }, + { + "label": "==", + "value": "EQ" + }, + { + "label": ">=", + "value": "GTE" + }, + { + "label": ">", + "value": "GT" + }, + { + "label": "array-contains", + "value": "ARRAY_CONTAINS" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "array-contains-any", + "value": "ARRAY_CONTAINS_ANY" + } + ] + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "value" + } + ] + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT" + ] + } + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Method Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "method" + }, + { + "label": "Method", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "GET", + "options": [ + { + "label": "Fetch sheet rows", + "value": "GET" + }, + { + "label": "Insert sheet row", + "value": "APPEND" + }, + { + "label": "Update sheet row", + "value": "UPDATE" + }, + { + "label": "Delete row", + "value": "DELETE_ROW" + }, + { + "label": "List sheets", + "value": "LIST" + }, + { + "label": "Fetch sheet", + "value": "INFO" + }, + { + "label": "Create new spreadsheet", + "value": "CREATE" + }, + { + "label": "Delete sheet", + "value": "DELETE" + }, + { + "label": "Bulk insert rows", + "value": "BULK_APPEND" + }, + { + "label": "Bulk update rows", + "value": "BULK_UPDATE" + } + ] + }, + { + "label": "Spreadsheet URL Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sheetUrl" + }, + { + "label": "Spreadsheet URL", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "CREATE", + "LIST" + ] + }, + "placeholderText": "https://docs.google.com/spreadsheets/d/xyz/edit#gid=0", + "initialValue": "" + }, + { + "label": "Spreadsheet Name Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "spreadsheetName" + }, + { + "label": "Spreadsheet Name", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE" + ] + }, + "initialValue": "" + }, + { + "label": "Delete Format Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[12].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "deleteFormat" + }, + { + "label": "Select Entity", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[12].value", + "controlType": "DROP_DOWN", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "DELETE" + }, + "initialValue": "SHEET", + "options": [ + { + "label": "Single Sheet", + "value": "SHEET" + }, + { + "label": "Entire Spreadsheet", + "value": "SPREADSHEET" + } + ] + }, + { + "label": "Sheet Name Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sheetName" + }, + { + "label": "Sheet Name", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET", + "UPDATE", + "BULK_UPDATE", + "DELETE_ROW", + "DELETE", + "APPEND", + "BULK_APPEND" + ] + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "DELETE" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[12].value", + "comparison": "EQUALS", + "value": "SPREADSHEET" + } + ] + } + ] + }, + "initialValue": "Sheet1" + }, + { + "label": "Table Header Index Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[4].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "tableHeaderIndex" + }, + { + "label": "Table Heading Row Index", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[4].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET", + "UPDATE", + "BULK_UPDATE", + "DELETE_ROW", + "APPEND", + "BULK_APPEND" + ] + }, + "initialValue": "1" + }, + { + "label": "Query Format Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[5].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "queryFormat" + }, + { + "label": "Query Format", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "controlType": "DROP_DOWN", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET" + ] + }, + "initialValue": "ROWS", + "options": [ + { + "label": "Query rows", + "value": "ROWS" + }, + { + "label": "Query range", + "value": "RANGE" + } + ] + }, + { + "label": "Range Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "range" + }, + { + "label": "Cell Range", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "ROWS" + } + ] + } + ] + }, + "initialValue": "", + "placeholderText": "A2:B" + }, + { + "label": "Row Offset Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowOffset" + }, + { + "label": "Row Offset", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET" + ] + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "RANGE" + } + ] + } + ] + }, + "initialValue": "0", + "placeholderText": "0" + }, + { + "label": "Row Index Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[11].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowIndex" + }, + { + "label": "Row Index", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[11].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "DELETE_ROW" + ] + }, + "initialValue": "0", + "placeholderText": "0" + }, + { + "label": "Row Limit Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowLimit" + }, + { + "label": "Row Limit", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "RANGE" + } + ] + } + ] + }, + "initialValue": "10", + "placeholderText": "10" + }, + { + "label": "Where Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[14].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "where" + }, + { + "label": "Where Conditions", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[14].value", + "controlType": "ARRAY_FIELD", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + "initialValue": [], + "schema": [ + { + "label": "Column Name", + "key": "path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "name" + }, + { + "label": "Operator", + "key": "operator", + "controlType": "DROP_DOWN", + "initialValue": "EQ", + "options": [ + { + "label": "<", + "value": "LT" + }, + { + "label": "<=", + "value": "LTE" + }, + { + "label": "==", + "value": "EQ" + }, + { + "label": ">=", + "value": "GTE" + }, + { + "label": ">", + "value": "GT" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "not in", + "value": "NOT_IN" + } + ] + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Tobias" + } + ] + }, + { + "label": "Row Object Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowObject" + }, + { + "label": "Row Object", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "APPEND", + "UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": false + } + ] + }, + "placeholderText": "{{\n {\n ...Table1.selectedRow, \n columnName: Input1.text\n }\n}}" + }, + { + "label": "Row Object", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "APPEND", + "UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": true + } + ] + }, + "placeholderText": "{{\n {\n ...Table1.selectedRow, \n columnName: Input1.text\n }\n}}" + }, + { + "label": "Row Objects Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowObjects" + }, + { + "label": "Row Objects", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE", + "BULK_APPEND", + "BULK_UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": false + } + ] + }, + "placeholderText": "{{\n Table1.selectedRows.map((row) => {\n return { ...row, columnName: Input1.text }\n })\n}}" + }, + { + "label": "Row Objects", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE", + "BULK_APPEND", + "BULK_UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": true + } + ] + }, + "placeholderText": "{{\n Table1.selectedRows.map((row) => {\n return { ...row, columnName: Input1.text }\n })\n}}" + }, + { + "label": "Smart JSON Substitution Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[13].key", + "hidden": true, + "controlType": "INPUT_TEXT", + "initialValue": "smartSubstitution" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "options": [ + { + "label": "List files in bucket", + "value": "LIST" + }, + { + "label": "Create a new file", + "value": "UPLOAD_FILE_FROM_BODY" + }, + { + "label": "Read file", + "value": "READ_FILE" + }, + { + "label": "Delete file", + "value": "DELETE_FILE" + } + ], + "label": "Commands", + "description": "Choose method you would like to use", + "configProperty": "actionConfiguration.formData.command", + "controlType": "DROP_DOWN", + "initialValue": "LIST" + }, + { + "controlType": "SECTION", + "label": "", + "_comment": "This section holds all the templates", + "children": [ + { + "identifier": "LIST", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'LIST'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Prefix", + "configProperty": "actionConfiguration.formData.list.prefix", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Where", + "configProperty": "actionConfiguration.formData.list.where", + "nestedLevels": 3, + "controlType": "WHERE_CLAUSE", + "logicalTypes": [ + { + "label": "AND", + "value": "AND" + }, + { + "label": "OR", + "value": "OR" + } + ], + "comparisonTypes": [ + { + "label": "==", + "value": "EQ" + }, + { + "label": "!=", + "value": "NOT_EQ" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "not in", + "value": "NOT_IN" + } + ] + } + ] + }, + { + "controlType": "SECTION", + "label": "Options", + "children": [ + { + "label": "Generate Signed URL", + "configProperty": "actionConfiguration.formData.list.signedUrl", + "controlType": "DROP_DOWN", + "initialValue": "NO", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.list.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5", + "conditionals": { + "show": "{{actionConfiguration.formData.list.signedUrl === 'YES'}}" + } + }, + { + "label": "Generate Un-signed URL", + "configProperty": "actionConfiguration.formData.list.unSignedUrl", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + } + ] + } + ] + }, + { + "identifier": "UPLOAD_FILE_FROM_BODY", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'UPLOAD_FILE_FROM_BODY'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "File Data Type", + "configProperty": "actionConfiguration.formData.create.dataType", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Base64", + "value": "YES" + }, + { + "label": "Text / Binary", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.create.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + }, + { + "label": "Content", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "initialValue": "", + "placeHolderText": "{{ FilePicker1.files[0] }}" + } + ] + } + ] + }, + { + "identifier": "READ_FILE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'READ_FILE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "File Data Type", + "configProperty": "actionConfiguration.formData.read.dataType", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Base64", + "value": "YES" + }, + { + "label": "Text / Binary", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.read.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + }, + { + "label": "Base64 Encode File - Yes/No", + "configProperty": "actionConfiguration.formData.read.usingBase64Encoding", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + } + ] + } + ] + }, + { + "identifier": "DELETE_FILE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DELETE_FILE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.delete.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + } + ] + } + ] + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT" + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "settingConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart BSON Substitution", + "info": "Turning on this property fixes the BSON substitution of bindings in the Mongo BSON document by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.formData.smartSubstitution", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Use Prepared Statement", + "info": "Turning on Prepared Statement makes your queries resilient against bad things like SQL injections. However, it cannot be used if your dynamic binding contains any SQL keywords like 'SELECT', 'WHERE', 'AND', etc.", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run API on Page load", + "configProperty": "executeOnLoad", + "controlType": "CHECKBOX", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running API", + "configProperty": "confirmBeforeExecute", + "controlType": "CHECKBOX", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Encode query params", + "configProperty": "actionConfiguration.encodeParamsToggle", + "controlType": "CHECKBOX", + "info": "Encode query params for all APIs. Also encode form body when Content-Type header is set to x-www-form-encoded" + }, + { + "label": "Smart JSON Substitution", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "CHECKBOX", + "info": "Turning on this property fixes the JSON substitution of bindings in API body by adding/removing quotes intelligently and reduces developer errors", + "initialValue": true + }, + { + "label": "API timeout (in milliseconds)", + "info": "Maximum time after which the API will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "NUMBER_INPUT", + "dataType": "number" + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart JSON Substitution", + "info": "Turning on this property fixes the JSON substitution of bindings in the Row objects by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart JSON Substitution", + "info": "Turning on this property fixes the JSON substitution of bindings in the Content field by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.formData.smartSubstitution", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "dependencies": "undefined", + "fetchingSinglePluginForm": {} + }, + "meta": {}, + "app": "undefined", + "jsActions": [] + } + } \ No newline at end of file diff --git a/app/client/src/pages/Editor/SaaSEditor/__data__/InitialState.json b/app/client/src/pages/Editor/SaaSEditor/__data__/InitialState.json new file mode 100644 index 0000000000..cbac65ca3c --- /dev/null +++ b/app/client/src/pages/Editor/SaaSEditor/__data__/InitialState.json @@ -0,0 +1,2813 @@ +{ + "entities": { + "actions": [ + { + "isLoading": false, + "config": { + "id": "6194d8e1abb49e2fd00812b2", + "organizationId": "5e1c5b2014edee5e49efc06c", + "pluginType": "SAAS", + "pluginId": "6080f9266b8cfd602957ba72", + "name": "Api1", + "datasource": { + "id": "6143187b6371c22cc093fdcf", + "userPermissions": [], + "pluginId": "6080f9266b8cfd602957ba72", + "messages": [], + "isValid": true, + "new": false + }, + "pageId": "6194d8cdabb49e2fd00812b1", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "pluginSpecifiedTemplates": [ + { + "key": "method", + "value": "GET" + }, + { + "key": "sheetUrl", + "value": "https://docs.google.com/spreadsheets/d/1HYjlH7T4ii_NZzCg6LvGG0hnqfNOu35XSl-OTo_4K7Y/edit#gid=1562690580" + }, + { + "key": "range", + "value": "" + }, + { + "key": "spreadsheetName", + "value": "" + }, + { + "key": "tableHeaderIndex", + "value": "1" + }, + { + "key": "queryFormat", + "value": "ROWS" + }, + { + "key": "rowLimit", + "value": "10" + }, + { + "key": "sheetName", + "value": "Sheet1" + }, + { + "key": "rowOffset", + "value": "0" + }, + { + "key": "rowObject" + }, + { + "key": "rowObjects" + }, + { + "key": "rowIndex", + "value": "0" + }, + { + "key": "deleteFormat", + "value": "SHEET" + }, + { + "key": "smartSubstitution", + "value": true + }, + { + "value": [ + {} + ] + } + ] + }, + "executeOnLoad": false, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [ + "read:actions", + "execute:actions", + "manage:actions" + ], + "validName": "Api1" + } + } + ], + "datasources": "undefined", + "pageList": "undefined", + "jsExecutions": {}, + "plugins": { + "list": [], + "loading": false, + "formConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "sectionName": "Connection", + "children": [ + { + "label": "Use Mongo Connection String URI Key", + "configProperty": "datasourceConfiguration.properties[0].key", + "controlType": "INPUT_TEXT", + "initialValue": "Use Mongo Connection String URI", + "hidden": true + }, + { + "label": "Use Mongo Connection String URI", + "configProperty": "datasourceConfiguration.properties[0].value", + "controlType": "DROP_DOWN", + "initialValue": "No", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "label": "Connection String URI Key", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "initialValue": "Connection String URI", + "hidden": true + }, + { + "label": "Connection String URI", + "placeholderText": "mongodb+srv://:@test-db.swrsq.mongodb.net/myDatabase", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "NOT_EQUALS", + "value": "Yes" + } + }, + { + "label": "Connection Mode", + "configProperty": "datasourceConfiguration.connection.mode", + "controlType": "DROP_DOWN", + "initialValue": "READ_WRITE", + "options": [ + { + "label": "Read Only", + "value": "READ_ONLY" + }, + { + "label": "Read / Write", + "value": "READ_WRITE" + } + ], + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "label": "Connection Type", + "configProperty": "datasourceConfiguration.connection.type", + "initialValue": "DIRECT", + "controlType": "DROP_DOWN", + "options": [ + { + "label": "Direct Connection", + "value": "DIRECT" + }, + { + "label": "Replica set", + "value": "REPLICA_SET" + } + ], + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$", + "placeholderText": "myapp.abcde.mongodb.net", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + } + ] + }, + { + "label": "Default Database Name", + "placeholderText": "(Optional)", + "configProperty": "datasourceConfiguration.connection.defaultDatabaseName", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + } + } + ] + }, + { + "sectionName": "Authentication", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + }, + "children": [ + { + "label": "Database Name", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "placeholderText": "Database name", + "initialValue": "admin" + }, + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authType", + "controlType": "DROP_DOWN", + "initialValue": "SCRAM_SHA_1", + "options": [ + { + "label": "SCRAM-SHA-1", + "value": "SCRAM_SHA_1" + }, + { + "label": "SCRAM-SHA-256", + "value": "SCRAM_SHA_256" + }, + { + "label": "MONGODB-CR", + "value": "MONGODB_CR" + } + ] + }, + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + }, + { + "sectionName": "SSL (optional)", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "Yes" + }, + "children": [ + { + "label": "SSL Mode", + "configProperty": "datasourceConfiguration.connection.ssl.authType", + "controlType": "DROP_DOWN", + "initialValue": "DEFAULT", + "options": [ + { + "label": "Default", + "value": "DEFAULT" + }, + { + "label": "Enabled", + "value": "ENABLED" + }, + { + "label": "Disabled", + "value": "DISABLED" + } + ] + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "Connection", + "id": 1, + "children": [ + { + "label": "Connection Mode", + "configProperty": "datasourceConfiguration.connection.mode", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "READ_WRITE", + "options": [ + { + "label": "Read Only", + "value": "READ_ONLY" + }, + { + "label": "Read / Write", + "value": "READ_WRITE" + } + ] + }, + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$" + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY" + } + ] + }, + { + "label": "Database Name", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "placeholderText": "Database name", + "initialValue": "admin" + } + ] + }, + { + "sectionName": "Authentication", + "id": 2, + "children": [ + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + }, + { + "id": 3, + "sectionName": "SSL (optional)", + "children": [ + { + "label": "SSL Mode", + "configProperty": "datasourceConfiguration.connection.ssl.authType", + "controlType": "DROP_DOWN", + "initialValue": "DEFAULT", + "options": [ + { + "label": "Default", + "value": "DEFAULT" + }, + { + "label": "Allow", + "value": "ALLOW" + }, + { + "label": "Prefer", + "value": "PREFER" + }, + { + "label": "Require", + "value": "REQUIRE" + }, + { + "label": "Disable", + "value": "DISABLE" + } + ] + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "General", + "children": [ + { + "label": "URL", + "configProperty": "datasourceConfiguration.url", + "controlType": "INPUT_TEXT", + "isRequired": true, + "placeholderText": "https://example.com" + }, + { + "label": "Headers", + "configProperty": "datasourceConfiguration.headers", + "controlType": "KEY_VAL_INPUT" + }, + { + "label": "Send Authentication Information Key (Do not edit)", + "configProperty": "datasourceConfiguration.properties[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "isSendSessionEnabled" + }, + { + "label": "Send Appsmith signature header (X-APPSMITH-SIGNATURE)", + "configProperty": "datasourceConfiguration.properties[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "N", + "options": [ + { + "label": "Yes", + "value": "Y" + }, + { + "label": "No", + "value": "N" + } + ] + }, + { + "label": "Session Details Signature Key Key (Do not edit)", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sessionSignatureKey" + }, + { + "label": "Session Details Signature Key", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "INPUT_TEXT", + "hidden": { + "path": "datasourceConfiguration.properties[0].value", + "comparison": "EQUALS", + "value": "N" + } + }, + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authenticationType", + "controlType": "DROP_DOWN", + "options": [ + { + "label": "None", + "value": "dbAuth" + }, + { + "label": "Basic", + "value": "basic" + }, + { + "label": "OAuth 2.0", + "value": "oAuth2" + }, + { + "label": "API Key", + "value": "apiKey" + }, + { + "label": "Bearer Token", + "value": "bearerToken" + } + ] + }, + { + "label": "Grant Type", + "configProperty": "datasourceConfiguration.authentication.grantType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true + }, + { + "label": "Access Token URL", + "configProperty": "datasourceConfiguration.authentication.accessTokenUrl", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Client Id", + "configProperty": "datasourceConfiguration.authentication.clientId", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Client Secret", + "configProperty": "datasourceConfiguration.authentication.clientSecret", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Scope(s)", + "configProperty": "datasourceConfiguration.authentication.scopeString", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Header Prefix", + "configProperty": "datasourceConfiguration.authentication.headerPrefix", + "controlType": "INPUT_TEXT", + "placeholderText": "Bearer (default)", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Add token to", + "configProperty": "datasourceConfiguration.authentication.isTokenHeader", + "controlType": "DROP_DOWN", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + }, + "options": [ + { + "label": "Header", + "value": true + }, + { + "label": "Query parameters", + "value": false + } + ] + }, + { + "label": "Audience(s)", + "configProperty": "datasourceConfiguration.authentication.audience", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + }, + { + "label": "Resource(s)", + "configProperty": "datasourceConfiguration.authentication.resource", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": { + "path": "datasourceConfiguration.authentication.authenticationType", + "comparison": "NOT_EQUALS", + "value": "oAuth2" + } + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "Details", + "id": 1, + "children": [ + { + "label": "Database URL", + "configProperty": "datasourceConfiguration.url", + "controlType": "INPUT_TEXT", + "isRequired": true, + "placeholderText": "https://your-project-id.firebaseio.com" + }, + { + "label": "Project Id", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "isRequired": true, + "initialValue": "" + }, + { + "label": "Service Account Credentials", + "configProperty": "datasourceConfiguration.authentication.password", + "controlType": "INPUT_TEXT", + "dataType": "PASSWORD", + "isRequired": true, + "initialValue": "" + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "General", + "children": [ + { + "label": "Authentication Type", + "configProperty": "datasourceConfiguration.authentication.authenticationType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true, + "initialValue": "oAuth2" + }, + { + "label": "Grant Type", + "configProperty": "datasourceConfiguration.authentication.grantType", + "controlType": "INPUT_TEXT", + "isRequired": false, + "hidden": true, + "initialValue": "authorization_code" + }, + { + "label": "Scope", + "configProperty": "datasourceConfiguration.authentication.scopeString", + "controlType": "DROP_DOWN", + "isRequired": true, + "options": [ + { + "label": "Read Only", + "value": "https://www.googleapis.com/auth/spreadsheets.readonly,https://www.googleapis.com/auth/drive.readonly" + }, + { + "label": "Read and Write", + "value": "https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/drive" + } + ], + "initialValue": "https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/drive" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "sectionName": "Details", + "id": 1, + "children": [ + { + "label": "S3 Service Provider Key", + "configProperty": "datasourceConfiguration.properties[1].key", + "controlType": "INPUT_TEXT", + "initialValue": "s3Provider", + "hidden": true + }, + { + "label": "S3 Service Provider", + "configProperty": "datasourceConfiguration.properties[1].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "amazon-s3", + "options": [ + { + "label": "Amazon S3", + "value": "amazon-s3" + }, + { + "label": "Upcloud", + "value": "upcloud" + }, + { + "label": "Digital Ocean Spaces", + "value": "digital-ocean-spaces" + }, + { + "label": "Wasabi", + "value": "wasabi" + }, + { + "label": "DreamObjects", + "value": "dream-objects" + }, + { + "label": "Other", + "value": "other" + } + ] + }, + { + "label": "Access Key", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Secret Key", + "configProperty": "datasourceConfiguration.authentication.password", + "controlType": "INPUT_TEXT", + "dataType": "PASSWORD", + "initialValue": "", + "encrypted": true + }, + { + "label": "Endpoint URL", + "configProperty": "datasourceConfiguration.endpoints[0].host", + "controlType": "INPUT_TEXT", + "initialValue": "", + "placeholderText": "user-storage.de-fra1.upcloudobjects.com", + "hidden": { + "path": "datasourceConfiguration.properties[1].value", + "comparison": "EQUALS", + "value": "amazon-s3" + } + }, + { + "label": "Custom Endpoint URL Key", + "configProperty": "datasourceConfiguration.properties[2].key", + "controlType": "INPUT_TEXT", + "initialValue": "customRegion", + "hidden": true + }, + { + "label": "Region", + "configProperty": "datasourceConfiguration.properties[2].value", + "controlType": "INPUT_TEXT", + "initialValue": "", + "placeholderText": "de-fra1", + "hidden": { + "path": "datasourceConfiguration.properties[1].value", + "comparison": "NOT_EQUALS", + "value": "other" + } + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "Connection", + "id": 1, + "children": [ + { + "children": [ + { + "label": "Host Address", + "configProperty": "datasourceConfiguration.endpoints[*].host", + "controlType": "KEYVALUE_ARRAY", + "validationMessage": "Please enter a valid host", + "validationRegex": "^((?![/:]).)*$" + }, + { + "label": "Port", + "configProperty": "datasourceConfiguration.endpoints[*].port", + "dataType": "NUMBER", + "controlType": "KEYVALUE_ARRAY" + } + ] + }, + { + "label": "Database Number", + "configProperty": "datasourceConfiguration.authentication.databaseName", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER", + "placeholderText": "0" + } + ] + }, + { + "sectionName": "Authentication", + "id": 2, + "children": [ + { + "children": [ + { + "label": "Username", + "configProperty": "datasourceConfiguration.authentication.username", + "controlType": "INPUT_TEXT", + "placeholderText": "Username" + }, + { + "label": "Password", + "configProperty": "datasourceConfiguration.authentication.password", + "dataType": "PASSWORD", + "controlType": "INPUT_TEXT", + "placeholderText": "Password", + "encrypted": true + } + ] + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "editorConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "options": [ + { + "label": "Find Document(s)", + "value": "FIND" + }, + { + "label": "Insert Document(s)", + "value": "INSERT" + }, + { + "label": "Update Document(s)", + "value": "UPDATE" + }, + { + "label": "Delete Document(s)", + "value": "DELETE" + }, + { + "label": "Count", + "value": "COUNT" + }, + { + "label": "Distinct", + "value": "DISTINCT" + }, + { + "label": "Aggregate", + "value": "AGGREGATE" + }, + { + "label": "Raw", + "value": "RAW" + } + ], + "label": "Commands", + "description": "Choose method you would like to use to query the database", + "configProperty": "actionConfiguration.formData.command", + "controlType": "DROP_DOWN", + "initialValue": "FIND" + }, + { + "controlType": "SECTION", + "label": "", + "_comment": "This section holds all the templates", + "children": [ + { + "identifier": "FIND", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'FIND'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.find.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Sort", + "configProperty": "actionConfiguration.formData.find.sort", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{name : 1}" + }, + { + "label": "Projection", + "configProperty": "actionConfiguration.formData.find.projection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{name : 1}" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.find.limit", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "10" + }, + { + "label": "Skip", + "configProperty": "actionConfiguration.formData.find.skip", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "0" + } + ] + } + ] + }, + { + "identifier": "INSERT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'INSERT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Documents", + "configProperty": "actionConfiguration.formData.insert.documents", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "[ { _id: 1, user: \"abc123\", status: \"A\" } ]" + } + ] + } + ] + }, + { + "identifier": "UPDATE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'UPDATE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.updateMany.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Update", + "configProperty": "actionConfiguration.formData.updateMany.update", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{ $inc: { score: 1 } }" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.updateMany.limit", + "controlType": "DROP_DOWN", + "initialValue": "SINGLE", + "options": [ + { + "label": "Single Document", + "value": "SINGLE" + }, + { + "label": "All Matching Documents", + "value": "ALL" + } + ] + } + ] + } + ] + }, + { + "identifier": "DELETE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DELETE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.delete.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Limit", + "configProperty": "actionConfiguration.formData.delete.limit", + "controlType": "DROP_DOWN", + "initialValue": "SINGLE", + "options": [ + { + "label": "Single Document", + "value": "SINGLE" + }, + { + "label": "All Matching Documents", + "value": "ALL" + } + ] + } + ] + } + ] + }, + { + "identifier": "COUNT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'COUNT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.count.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + } + ] + } + ] + }, + { + "identifier": "DISTINCT", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DISTINCT'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Query", + "configProperty": "actionConfiguration.formData.distinct.query", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "{rating : {$gte : 9}}" + }, + { + "label": "Key", + "configProperty": "actionConfiguration.formData.distinct.key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "name" + } + ] + } + ] + }, + { + "identifier": "AGGREGATE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'AGGREGATE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Collection to Query", + "children": [ + { + "label": "Collection", + "configProperty": "actionConfiguration.formData.collection", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Array of Pipelines", + "configProperty": "actionConfiguration.formData.aggregate.arrayPipelines", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "inputType": "JSON", + "evaluationSubstitutionType": "TEMPLATE", + "placeholderText": "[{ $project: { tags: 1 } }, { $unwind: \"$tags\" }, { $group: { _id: \"$tags\", count: { $sum : 1 } } } ]" + } + ] + } + ] + }, + { + "identifier": "RAW", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "", + "propertyName": "rawWithSmartSubstitute", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW' && actionConfiguration.formData.smartSubstitution === true}}" + } + }, + { + "label": "", + "configProperty": "actionConfiguration.body", + "propertyName": "rawWithTemplateSubstitute", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'RAW' && actionConfiguration.formData.smartSubstitution === false}}" + } + } + ] + } + ] + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "PARAMETER", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": false + } + }, + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": true + } + }, + { + "label": "Use Prepared Statement", + "info": "Turning on Prepared Statement makes your queries resilient against bad things like SQL injections. However, it cannot be used if your dynamic binding contains any SQL keywords like 'SELECT', 'WHERE', 'AND', etc.", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "SWITCH", + "initialValue": true + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT" + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": false + } + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": true + } + }, + { + "label": "Query Parameters", + "configProperty": "actionConfiguration.queryParameters", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + }, + { + "label": "Headers", + "configProperty": "actionConfiguration.headers", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + }, + { + "label": "Form data", + "configProperty": "actionConfiguration.bodyFormData", + "controlType": "ARRAY_FIELD", + "schema": [ + { + "label": "Key", + "key": "key", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Key" + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Value" + } + ] + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Method Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "method" + }, + { + "label": "Method", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "GET_DOCUMENT", + "options": [ + { + "label": "Get Single Document", + "value": "GET_DOCUMENT" + }, + { + "label": "Get Documents in Collection", + "value": "GET_COLLECTION" + }, + { + "label": "Set Document", + "value": "SET_DOCUMENT" + }, + { + "label": "Create Document", + "value": "CREATE_DOCUMENT" + }, + { + "label": "Add Document to Collection", + "value": "ADD_TO_COLLECTION" + }, + { + "label": "Update Document", + "value": "UPDATE_DOCUMENT" + }, + { + "label": "Delete Document", + "value": "DELETE_DOCUMENT" + } + ] + }, + { + "label": "Collection/Document Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "isRequired": true, + "initialValue": "" + }, + { + "label": "Timestamp Value Path Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].key", + "controlType": "INPUT_TEXT", + "initialValue": "timestampValuePath", + "hidden": true + }, + { + "label": "Timestamp Value Path (use dot(.) notation to reference nested key e.g. [\"key1.key2\"])", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "", + "placeholderText": "[\"checkinLog.timestampKey\", \"auditLog.timestampKey\"]", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT" + ] + } + }, + { + "label": "Delete Key Value Pair Path Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].key", + "controlType": "INPUT_TEXT", + "initialValue": "deleteKeyValuePairPath", + "hidden": true + }, + { + "label": "Delete Key Value Pair Path (use dot(.) notation to reference nested key e.g. [\"key1.key2\"])", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "", + "placeholderText": "[\"userKey.nestedNamekey\", \"cityKey.nestedPincodeKey\"]", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT", + "CREATE_DOCUMENT", + "ADD_TO_COLLECTION", + "SET_DOCUMENT" + ] + } + }, + { + "label": "Order By Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "orderBy" + }, + { + "label": "Order By (JSON array of field names to order by)", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "placeholderText": "[\"ascendingField\", \"-descendingField\", \"nestedObj.field\"]", + "initialValue": "" + }, + { + "label": "Start After Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "Start After", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "" + }, + { + "label": "End Before Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "End Before", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "" + }, + { + "label": "Limit Documents Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "limit" + }, + { + "label": "Limit Documents", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "initialValue": "10" + }, + { + "label": "Where Conditions Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "whereConditionTuples" + }, + { + "label": "Where Conditions", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].value", + "controlType": "ARRAY_FIELD", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET_COLLECTION" + }, + "schema": [ + { + "label": "Path", + "key": "path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "key1/nestedKey2" + }, + { + "label": "Operator", + "key": "operator", + "controlType": "DROP_DOWN", + "initialValue": "EQ", + "options": [ + { + "label": "<", + "value": "LT" + }, + { + "label": "<=", + "value": "LTE" + }, + { + "label": "==", + "value": "EQ" + }, + { + "label": ">=", + "value": "GTE" + }, + { + "label": ">", + "value": "GT" + }, + { + "label": "array-contains", + "value": "ARRAY_CONTAINS" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "array-contains-any", + "value": "ARRAY_CONTAINS_ANY" + } + ] + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "value" + } + ] + }, + { + "label": "Body", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "GET_DOCUMENT", + "GET_COLLECTION", + "DELETE_DOCUMENT" + ] + } + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Method Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "method" + }, + { + "label": "Method", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "DROP_DOWN", + "isRequired": true, + "initialValue": "GET", + "options": [ + { + "label": "Fetch sheet rows", + "value": "GET" + }, + { + "label": "Insert sheet row", + "value": "APPEND" + }, + { + "label": "Update sheet row", + "value": "UPDATE" + }, + { + "label": "Delete row", + "value": "DELETE_ROW" + }, + { + "label": "List sheets", + "value": "LIST" + }, + { + "label": "Fetch sheet", + "value": "INFO" + }, + { + "label": "Create new spreadsheet", + "value": "CREATE" + }, + { + "label": "Delete sheet", + "value": "DELETE" + }, + { + "label": "Bulk insert rows", + "value": "BULK_APPEND" + }, + { + "label": "Bulk update rows", + "value": "BULK_UPDATE" + } + ] + }, + { + "label": "Spreadsheet URL Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sheetUrl" + }, + { + "label": "Spreadsheet URL", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[1].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "IN", + "value": [ + "CREATE", + "LIST" + ] + }, + "placeholderText": "https://docs.google.com/spreadsheets/d/xyz/edit#gid=0", + "initialValue": "" + }, + { + "label": "Spreadsheet Name Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "spreadsheetName" + }, + { + "label": "Spreadsheet Name", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[3].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE" + ] + }, + "initialValue": "" + }, + { + "label": "Delete Format Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[12].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "deleteFormat" + }, + { + "label": "Select Entity", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[12].value", + "controlType": "DROP_DOWN", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "DELETE" + }, + "initialValue": "SHEET", + "options": [ + { + "label": "Single Sheet", + "value": "SHEET" + }, + { + "label": "Entire Spreadsheet", + "value": "SPREADSHEET" + } + ] + }, + { + "label": "Sheet Name Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "sheetName" + }, + { + "label": "Sheet Name", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[7].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET", + "UPDATE", + "BULK_UPDATE", + "DELETE_ROW", + "DELETE", + "APPEND", + "BULK_APPEND" + ] + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "DELETE" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[12].value", + "comparison": "EQUALS", + "value": "SPREADSHEET" + } + ] + } + ] + }, + "initialValue": "Sheet1" + }, + { + "label": "Table Header Index Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[4].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "tableHeaderIndex" + }, + { + "label": "Table Heading Row Index", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[4].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET", + "UPDATE", + "BULK_UPDATE", + "DELETE_ROW", + "APPEND", + "BULK_APPEND" + ] + }, + "initialValue": "1" + }, + { + "label": "Query Format Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[5].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "queryFormat" + }, + { + "label": "Query Format", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "controlType": "DROP_DOWN", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET" + ] + }, + "initialValue": "ROWS", + "options": [ + { + "label": "Query rows", + "value": "ROWS" + }, + { + "label": "Query range", + "value": "RANGE" + } + ] + }, + { + "label": "Range Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "range" + }, + { + "label": "Cell Range", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[2].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "ROWS" + } + ] + } + ] + }, + "initialValue": "", + "placeholderText": "A2:B" + }, + { + "label": "Row Offset Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowOffset" + }, + { + "label": "Row Offset", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[8].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "GET" + ] + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "RANGE" + } + ] + } + ] + }, + "initialValue": "0", + "placeholderText": "0" + }, + { + "label": "Row Index Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[11].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowIndex" + }, + { + "label": "Row Index", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[11].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "DELETE_ROW" + ] + }, + "initialValue": "0", + "placeholderText": "0" + }, + { + "label": "Row Limit Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowLimit" + }, + { + "label": "Row Limit", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[6].value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + { + "conditionType": "AND", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "EQUALS", + "value": "GET" + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[5].value", + "comparison": "EQUALS", + "value": "RANGE" + } + ] + } + ] + }, + "initialValue": "10", + "placeholderText": "10" + }, + { + "label": "Where Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[14].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "where" + }, + { + "label": "Where Conditions", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[14].value", + "controlType": "ARRAY_FIELD", + "hidden": { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_EQUALS", + "value": "GET" + }, + "initialValue": [], + "schema": [ + { + "label": "Column Name", + "key": "path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "name" + }, + { + "label": "Operator", + "key": "operator", + "controlType": "DROP_DOWN", + "initialValue": "EQ", + "options": [ + { + "label": "<", + "value": "LT" + }, + { + "label": "<=", + "value": "LTE" + }, + { + "label": "==", + "value": "EQ" + }, + { + "label": ">=", + "value": "GTE" + }, + { + "label": ">", + "value": "GT" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "not in", + "value": "NOT_IN" + } + ] + }, + { + "label": "Value", + "key": "value", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "placeholderText": "Tobias" + } + ] + }, + { + "label": "Row Object Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowObject" + }, + { + "label": "Row Object", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "APPEND", + "UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": false + } + ] + }, + "placeholderText": "{{\n {\n ...Table1.selectedRow, \n columnName: Input1.text\n }\n}}" + }, + { + "label": "Row Object", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[9].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "APPEND", + "UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": true + } + ] + }, + "placeholderText": "{{\n {\n ...Table1.selectedRow, \n columnName: Input1.text\n }\n}}" + }, + { + "label": "Row Objects Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].key", + "controlType": "INPUT_TEXT", + "hidden": true, + "initialValue": "rowObjects" + }, + { + "label": "Row Objects", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE", + "BULK_APPEND", + "BULK_UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": false + } + ] + }, + "placeholderText": "{{\n Table1.selectedRows.map((row) => {\n return { ...row, columnName: Input1.text }\n })\n}}" + }, + { + "label": "Row Objects", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[10].value", + "controlType": "QUERY_DYNAMIC_TEXT", + "evaluationSubstitutionType": "SMART_SUBSTITUTE", + "hidden": { + "conditionType": "OR", + "conditions": [ + { + "path": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "comparison": "NOT_IN", + "value": [ + "CREATE", + "BULK_APPEND", + "BULK_UPDATE" + ] + }, + { + "path": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "comparison": "EQUALS", + "value": true + } + ] + }, + "placeholderText": "{{\n Table1.selectedRows.map((row) => {\n return { ...row, columnName: Input1.text }\n })\n}}" + }, + { + "label": "Smart JSON Substitution Key", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[13].key", + "hidden": true, + "controlType": "INPUT_TEXT", + "initialValue": "smartSubstitution" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "options": [ + { + "label": "List files in bucket", + "value": "LIST" + }, + { + "label": "Create a new file", + "value": "UPLOAD_FILE_FROM_BODY" + }, + { + "label": "Read file", + "value": "READ_FILE" + }, + { + "label": "Delete file", + "value": "DELETE_FILE" + } + ], + "label": "Commands", + "description": "Choose method you would like to use", + "configProperty": "actionConfiguration.formData.command", + "controlType": "DROP_DOWN", + "initialValue": "LIST" + }, + { + "controlType": "SECTION", + "label": "", + "_comment": "This section holds all the templates", + "children": [ + { + "identifier": "LIST", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'LIST'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "Prefix", + "configProperty": "actionConfiguration.formData.list.prefix", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Where", + "configProperty": "actionConfiguration.formData.list.where", + "nestedLevels": 3, + "controlType": "WHERE_CLAUSE", + "logicalTypes": [ + { + "label": "AND", + "value": "AND" + }, + { + "label": "OR", + "value": "OR" + } + ], + "comparisonTypes": [ + { + "label": "==", + "value": "EQ" + }, + { + "label": "!=", + "value": "NOT_EQ" + }, + { + "label": "in", + "value": "IN" + }, + { + "label": "not in", + "value": "NOT_IN" + } + ] + } + ] + }, + { + "controlType": "SECTION", + "label": "Options", + "children": [ + { + "label": "Generate Signed URL", + "configProperty": "actionConfiguration.formData.list.signedUrl", + "controlType": "DROP_DOWN", + "initialValue": "NO", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.list.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5", + "conditionals": { + "show": "{{actionConfiguration.formData.list.signedUrl === 'YES'}}" + } + }, + { + "label": "Generate Un-signed URL", + "configProperty": "actionConfiguration.formData.list.unSignedUrl", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + } + ] + } + ] + }, + { + "identifier": "UPLOAD_FILE_FROM_BODY", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'UPLOAD_FILE_FROM_BODY'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "File Data Type", + "configProperty": "actionConfiguration.formData.create.dataType", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Base64", + "value": "YES" + }, + { + "label": "Text / Binary", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.create.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + }, + { + "label": "Content", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT", + "initialValue": "", + "placeHolderText": "{{ FilePicker1.files[0] }}" + } + ] + } + ] + }, + { + "identifier": "READ_FILE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'READ_FILE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "File Data Type", + "configProperty": "actionConfiguration.formData.read.dataType", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Base64", + "value": "YES" + }, + { + "label": "Text / Binary", + "value": "NO" + } + ] + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.read.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + }, + { + "label": "Base64 Encode File - Yes/No", + "configProperty": "actionConfiguration.formData.read.usingBase64Encoding", + "controlType": "DROP_DOWN", + "initialValue": "YES", + "options": [ + { + "label": "Yes", + "value": "YES" + }, + { + "label": "No", + "value": "NO" + } + ] + } + ] + } + ] + }, + { + "identifier": "DELETE_FILE", + "controlType": "SECTION", + "conditionals": { + "show": "{{actionConfiguration.formData.command === 'DELETE_FILE'}}" + }, + "children": [ + { + "controlType": "SECTION", + "label": "Select Bucket to Query", + "children": [ + { + "label": "Bucket Name", + "configProperty": "actionConfiguration.formData.bucket", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "evaluationSubstitutionType": "TEMPLATE", + "isRequired": true, + "initialValue": "" + } + ] + }, + { + "controlType": "SECTION", + "label": "Query", + "description": "Optional", + "children": [ + { + "label": "File Path", + "configProperty": "actionConfiguration.path", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "" + }, + { + "label": "Expiry Duration of Signed URL (Minutes)", + "configProperty": "actionConfiguration.formData.delete.expiry", + "controlType": "QUERY_DYNAMIC_INPUT_TEXT", + "initialValue": "5" + } + ] + } + ] + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "", + "internalLabel": "Query", + "configProperty": "actionConfiguration.body", + "controlType": "QUERY_DYNAMIC_TEXT" + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "settingConfigs": { + "5e687c18fb01e64e6a3f873f": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart BSON Substitution", + "info": "Turning on this property fixes the BSON substitution of bindings in the Mongo BSON document by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.formData.smartSubstitution", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5c9f512f96c1a50004819786": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Use Prepared Statement", + "info": "Turning on Prepared Statement makes your queries resilient against bad things like SQL injections. However, it cannot be used if your dynamic binding contains any SQL keywords like 'SELECT', 'WHERE', 'AND', etc.", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5ca385dc81b37f0004b4db85": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run API on Page load", + "configProperty": "executeOnLoad", + "controlType": "CHECKBOX", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running API", + "configProperty": "confirmBeforeExecute", + "controlType": "CHECKBOX", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Encode query params", + "configProperty": "actionConfiguration.encodeParamsToggle", + "controlType": "CHECKBOX", + "info": "Encode query params for all APIs. Also encode form body when Content-Type header is set to x-www-form-encoded" + }, + { + "label": "Smart JSON Substitution", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[0].value", + "controlType": "CHECKBOX", + "info": "Turning on this property fixes the JSON substitution of bindings in API body by adding/removing quotes intelligently and reduces developer errors", + "initialValue": true + }, + { + "label": "API timeout (in milliseconds)", + "info": "Maximum time after which the API will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "NUMBER_INPUT", + "dataType": "number" + } + ] + } + ], + "5fbbc39ad1f71d6666c32e4b": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6080f9266b8cfd602957ba72": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart JSON Substitution", + "info": "Turning on this property fixes the JSON substitution of bindings in the Row objects by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.pluginSpecifiedTemplates[13].value", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6023b4a070eb652de19476d3": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Smart JSON Substitution", + "info": "Turning on this property fixes the JSON substitution of bindings in the Content field by adding/removing quotes intelligently and reduces developer errors", + "configProperty": "actionConfiguration.formData.smartSubstitution", + "controlType": "SWITCH", + "initialValue": true + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "5f9169920c6d936f469f4c8a": [ + { + "sectionName": "", + "id": 1, + "children": [ + { + "label": "Run query on page load", + "configProperty": "executeOnLoad", + "controlType": "SWITCH", + "info": "Will refresh data each time the page is loaded" + }, + { + "label": "Request confirmation before running query", + "configProperty": "confirmBeforeExecute", + "controlType": "SWITCH", + "info": "Ask confirmation from the user each time before refreshing data" + }, + { + "label": "Query timeout (in milliseconds)", + "info": "Maximum time after which the query will return", + "configProperty": "actionConfiguration.timeoutInMillisecond", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER" + } + ] + } + ], + "6138c786168857325f78ef3e": [] + }, + "dependencies": "undefined", + "fetchingSinglePluginForm": {} + }, + "meta": {}, + "app": "undefined", + "jsActions": [] + } + } \ No newline at end of file diff --git a/app/client/src/pages/Editor/SaaSEditor/__tests__/ActionForm.test.js b/app/client/src/pages/Editor/SaaSEditor/__tests__/ActionForm.test.js new file mode 100644 index 0000000000..358f3e2018 --- /dev/null +++ b/app/client/src/pages/Editor/SaaSEditor/__tests__/ActionForm.test.js @@ -0,0 +1,65 @@ +import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { diff } from "deep-diff"; +import { merge } from "lodash"; +import { getAction } from "selectors/entitiesSelector"; +import { getConfigInitialValues } from "components/formControls/utils"; +import { getPathAndValueFromActionDiffObject } from "../../../../utils/getPathAndValueFromActionDiffObject"; +import configureStore from "redux-mock-store"; +import { setActionProperty } from "actions/pluginActionActions"; +import initialState from "../__data__/InitialState.json"; +import finalState from "../__data__/FinalState.json"; + +describe("missing key: where", () => { + test("getPathAndValueFromActionDiffObject() works correctly", () => { + const apiId = initialState.entities.actions[0].config.id; + const action = getAction(initialState, apiId); + const initialValues = {}; + const { plugins } = initialState.entities; + const { editorConfigs, settingConfigs } = plugins; + const pluginId = action?.pluginId ?? ""; + let editorConfig; + + if (editorConfigs && pluginId) { + editorConfig = editorConfigs[pluginId]; + if (editorConfig) { + merge(initialValues, getConfigInitialValues(editorConfig)); + } + } + let settingConfig; + + if (settingConfigs && pluginId) { + settingConfig = settingConfigs[pluginId]; + } + merge(initialValues, getConfigInitialValues(settingConfig)); + merge(initialValues, action); + + const actionObjectDiff = diff(action, initialValues); + + const { path = "", value = "" } = { + ...getPathAndValueFromActionDiffObject(actionObjectDiff), + }; + + expect(path).toBeTruthy(); + expect(value).toBeTruthy(); + expect(path).toMatch(/actionConfiguration.pluginSpecifiedTemplates/); + + const middlewares = []; + const mockStore = configureStore(middlewares); + const store = mockStore(initialState); + store.dispatch( + setActionProperty({ + type: ReduxActionTypes.SET_ACTION_PROPERTY, + payload: { + actionId: apiId, + propertyName: path, + value: value, + }, + }), + ); + //TODO: + setTimeout(() => { + const stateAfterSetAction = store.getState(); + expect(stateAfterSetAction).toEqual(finalState); + }, 2000); + }); +}); diff --git a/app/client/src/pages/Editor/ToggleModeButton.tsx b/app/client/src/pages/Editor/ToggleModeButton.tsx index 3a58f18e39..5850b71adf 100644 --- a/app/client/src/pages/Editor/ToggleModeButton.tsx +++ b/app/client/src/pages/Editor/ToggleModeButton.tsx @@ -44,6 +44,8 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { getCurrentApplicationId } from "../../selectors/editorSelectors"; import { getAppMode } from "../../selectors/applicationSelectors"; +import { setPreviewModeAction } from "actions/editorActions"; +import { previewModeSelector } from "selectors/editorSelectors"; import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; @@ -99,7 +101,6 @@ const Container = styled.div` display: flex; flex: 1; z-index: ${Indices.Layer1}; - margin-left: ${(props) => props.theme.spaces[5]}px; `; /** @@ -319,7 +320,9 @@ export const useHasUnreadCommentThread = (applicationId: string) => { function ToggleCommentModeButton({ showSelectedMode = true, }: ToggleCommentModeButtonProps) { + const dispatch = useDispatch(); const isCommentMode = useSelector(commentModeSelector); + const isPreviewMode = useSelector(previewModeSelector); const currentUser = useSelector(getCurrentUser); const appId = useSelector(getCurrentApplicationId) || ""; const appMode = useSelector(getAppMode); @@ -355,15 +358,26 @@ function ToggleCommentModeButton({ source: "CLICK", }); setCommentModeInUrl(true); + dispatch(setPreviewModeAction(false)); proceedToNextTourStep(); }, [proceedToNextTourStep]); + // Show comment mode button only on the canvas editor and viewer + const isHideComments = useHideComments(); + + const onClickPreviewModeButton = useCallback(() => { + dispatch(setPreviewModeAction(true)); + setCommentModeInUrl(false); + }, [dispatch, setPreviewModeAction]); + + if (isHideComments) return null; + return (
{ AnalyticsUtil.logEvent("COMMENTS_TOGGLE_MODE", { @@ -371,6 +385,7 @@ function ToggleCommentModeButton({ source: "CLICK", }); setCommentModeInUrl(false); + dispatch(setPreviewModeAction(false)); }} showSelectedMode={showSelectedMode} type="fill" @@ -383,6 +398,28 @@ function ToggleCommentModeButton({ showSelectedMode={showSelectedMode} showUnreadIndicator={showUnreadIndicator} /> + {appMode === APP_MODE.EDIT && ( + + Preview Mode + P + + } + hoverOpenDelay={1000} + position={Position.BOTTOM} + > + + + + + )}
diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index e787277a0c..d55f9d111e 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -90,7 +90,7 @@ function WidgetCard(props: CardProps) {
- + {props.details.displayName} {props.details.isBeta && Beta} diff --git a/app/client/src/pages/Editor/WidgetSidebar.tsx b/app/client/src/pages/Editor/WidgetSidebar.tsx index 689bdac258..43ca264119 100644 --- a/app/client/src/pages/Editor/WidgetSidebar.tsx +++ b/app/client/src/pages/Editor/WidgetSidebar.tsx @@ -1,12 +1,13 @@ import React, { useRef, useEffect, useState } from "react"; import { useSelector } from "react-redux"; import WidgetCard from "./WidgetCard"; -import styled from "styled-components"; import { getWidgetCards } from "selectors/editorSelectors"; import { IPanelProps } from "@blueprintjs/core"; import ExplorerSearch from "./Explorer/ExplorerSearch"; import { debounce } from "lodash"; import produce from "immer"; +import { useLocation } from "react-router"; + import { createMessage, WIDGET_SIDEBAR_CAPTION } from "constants/messages"; import Boxed from "components/editorComponents/Onboarding/Boxed"; import { OnboardingStep } from "constants/OnboardingConstants"; @@ -16,63 +17,12 @@ import { inOnboarding, } from "sagas/OnboardingSagas"; import { matchBuilderPath } from "constants/routes"; -import OnboardingIndicator from "components/editorComponents/Onboarding/Indicator"; -import { useLocation } from "react-router"; import { AppState } from "reducers"; -import { hideScrollbar } from "constants/DefaultTheme"; -import ScrollIndicator from "components/ads/ScrollIndicator"; - -const MainWrapper = styled.div` - text-transform: capitalize; - height: 100%; - overflow: hidden; - padding: 0px 10px 20px 10px; - &:active, - &:focus, - &:hover { - overflow: auto; - ${hideScrollbar} - } - &::-webkit-scrollbar-track { - background-color: transparent; - } -`; - -const CardsWrapper = styled.div` - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-gap: ${(props) => props.theme.spaces[1]}px; - justify-items: stretch; - align-items: stretch; -`; - -const Header = styled.div` - padding: 10px 10px 0px 10px; - display: grid; - grid-template-columns: 7fr 1fr; -`; - -const Info = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: space-around; - text-transform: none; - h4 { - margin-top: 0px; - } - p { - opacity: 0.6; - } -`; +import OnboardingIndicator from "components/editorComponents/Onboarding/Indicator"; function WidgetSidebar(props: IPanelProps) { const location = useLocation(); const cards = useSelector(getWidgetCards); - const isForceOpenWidgetPanel = useSelector( - (state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel, - ); - const sidebarRef = useRef(null); const [filteredCards, setFilteredCards] = useState(cards); const searchInputRef = useRef(null); const filterCards = (keyword: string) => { @@ -88,12 +38,9 @@ function WidgetSidebar(props: IPanelProps) { } setFilteredCards(filteredCards); }; - const clearSearchInput = () => { - if (searchInputRef.current) { - searchInputRef.current.value = ""; - } - filterCards(""); - }; + const isForceOpenWidgetPanel = useSelector( + (state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel, + ); // For onboarding const isInOnboarding = useSelector(inOnboarding); @@ -111,41 +58,42 @@ function WidgetSidebar(props: IPanelProps) { } }, [currentStep, onCanvas, isInOnboarding, location, isForceOpenWidgetPanel]); + /** + * filter widgets + */ const search = debounce((e: any) => { filterCards(e.target.value.toLowerCase()); }, 300); - useEffect(() => { - const el: HTMLInputElement | null = searchInputRef.current; - el?.addEventListener("keydown", search); - el?.addEventListener("cleared", search); - return () => { - el?.removeEventListener("keydown", search); - el?.removeEventListener("cleared", search); - }; - }, [searchInputRef, search]); + /** + * clear the search input + */ + const clearSearchInput = () => { + if (searchInputRef.current) { + searchInputRef.current.value = ""; + } + filterCards(""); + }; const showTableWidget = currentStep >= OnboardingStep.RUN_QUERY_SUCCESS; const showInputWidget = currentStep >= OnboardingStep.ADD_INPUT_WIDGET; return ( - <> +
-
- -

{createMessage(WIDGET_SIDEBAR_CAPTION)}

-
-
- - +
+

+ {createMessage(WIDGET_SIDEBAR_CAPTION)} +

+
{filteredCards.map((card) => ( ))} - - - - +
+
+
); } diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx new file mode 100644 index 0000000000..916408aa80 --- /dev/null +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -0,0 +1,72 @@ +import React, { ReactNode } from "react"; +import { useSelector } from "react-redux"; +import { + getCurrentPageId, + getIsFetchingPage, + getCanvasWidgetDsl, + getViewModePageList, + previewModeSelector, +} from "selectors/editorSelectors"; +import styled from "styled-components"; +import { getCanvasClassName } from "utils/generators"; + +import Centered from "components/designSystems/appsmith/CenteredWrapper"; +import { Spinner } from "@blueprintjs/core"; +import Canvas from "../Canvas"; +import { useParams } from "react-router"; +import classNames from "classnames"; + +const Container = styled.section` + width: 100%; + position: relative; + overflow-x: auto; + overflow-y: auto; + &:before { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + } +`; + +function CanvasContainer() { + const currentPageId = useSelector(getCurrentPageId); + const isFetchingPage = useSelector(getIsFetchingPage); + const widgets = useSelector(getCanvasWidgetDsl); + const pages = useSelector(getViewModePageList); + const isPreviewMode = useSelector(previewModeSelector); + const params = useParams<{ applicationId: string; pageId: string }>(); + const shouldHaveTopMargin = !isPreviewMode || pages.length > 1; + + const pageLoading = ( + + + + ); + let node: ReactNode; + if (isFetchingPage) { + node = pageLoading; + } + + if (!isFetchingPage && widgets) { + node = ; + } + + return ( + + {node} + + ); +} + +export default CanvasContainer; diff --git a/app/client/src/pages/Editor/WidgetsEditor/PageTabs.tsx b/app/client/src/pages/Editor/WidgetsEditor/PageTabs.tsx new file mode 100644 index 0000000000..4562aad46d --- /dev/null +++ b/app/client/src/pages/Editor/WidgetsEditor/PageTabs.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import classNames from "classnames"; +import { useSelector } from "react-redux"; + +import { getCurrentApplication } from "selectors/applicationSelectors"; +import PageTabsContainer from "pages/AppViewer/viewer/PageTabsContainer"; +import { + getViewModePageList, + previewModeSelector, +} from "selectors/editorSelectors"; + +function PageTabs() { + const pages = useSelector(getViewModePageList); + const isPreviewMode = useSelector(previewModeSelector); + const currentApplicationDetails = useSelector(getCurrentApplication); + + return ( +
+ +
+ ); +} + +export default PageTabs; diff --git a/app/client/src/pages/Editor/WidgetsEditor/Toolbar.tsx b/app/client/src/pages/Editor/WidgetsEditor/Toolbar.tsx new file mode 100644 index 0000000000..10a23c32d1 --- /dev/null +++ b/app/client/src/pages/Editor/WidgetsEditor/Toolbar.tsx @@ -0,0 +1,33 @@ +import React, { useCallback } from "react"; +import { useDispatch, useSelector } from "react-redux"; + +import MenuIcon from "remixicon-react/MenuLineIcon"; +import { setExplorerActiveAction } from "actions/explorerActions"; +import { getExplorerPinned } from "selectors/explorerSelector"; + +function Toolbar() { + const dispatch = useDispatch(); + const explorerPinned = useSelector(getExplorerPinned); + + /** + * on hovering the menu, make the explorer active + */ + const onMenuHover = useCallback(() => { + dispatch(setExplorerActiveAction(true)); + }, [setExplorerActiveAction]); + + return ( +
+
+ {explorerPinned === false && ( + + )} +
+
+ ); +} + +export default Toolbar; diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor/index.tsx similarity index 66% rename from app/client/src/pages/Editor/WidgetsEditor.tsx rename to app/client/src/pages/Editor/WidgetsEditor/index.tsx index 4f614fd70c..47a461523a 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/index.tsx @@ -1,78 +1,43 @@ -import React, { useEffect, ReactNode, useCallback } from "react"; +import React, { useEffect, useCallback } from "react"; import { useSelector, useDispatch } from "react-redux"; -import styled from "styled-components"; -import Canvas from "./Canvas"; +import * as log from "loglevel"; + import { getIsFetchingPage, getCurrentPageId, - getCanvasWidgetDsl, getCurrentPageName, } from "selectors/editorSelectors"; -import Centered from "components/designSystems/appsmith/CenteredWrapper"; -import { Spinner } from "@blueprintjs/core"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import * as log from "loglevel"; -import { getCanvasClassName } from "utils/generators"; -import { flashElementsById } from "utils/helpers"; -import { useParams } from "react-router"; +import PageTabs from "./PageTabs"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { getCurrentApplication } from "selectors/applicationSelectors"; -import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; -import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import CanvasContainer from "./CanvasContainer"; +import { flashElementsById } from "utils/helpers"; import Debugger from "components/editorComponents/Debugger"; -import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; +import OnboardingTasks from "../FirstTimeUserOnboarding/Tasks"; +import CrudInfoModal from "../GeneratePage/components/CrudInfoModal"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; +import { getCurrentApplication } from "selectors/applicationSelectors"; import { setCanvasSelectionFromEditor } from "actions/canvasSelectionActions"; -import CrudInfoModal from "./GeneratePage/components/CrudInfoModal"; -import EditorContextProvider from "components/editorComponents/EditorContextProvider"; -import { useAllowEditorDragToSelect } from "utils/hooks/useAllowEditorDragToSelect"; -import OnboardingTasks from "./FirstTimeUserOnboarding/Tasks"; +import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { getIsOnboardingTasksView, getIsOnboardingWidgetSelection, } from "selectors/entitiesSelector"; +import { useAllowEditorDragToSelect } from "utils/hooks/useAllowEditorDragToSelect"; import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors"; - -const EditorWrapper = styled.div` - display: flex; - flex: 1; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; - overflow: hidden; - position: relative; -`; - -const CanvasContainer = styled.section` - height: 100%; - width: 100%; - position: relative; - overflow-x: auto; - overflow-y: auto; - padding-top: 1px; - &:before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - } -`; +import EditorContextProvider from "components/editorComponents/EditorContextProvider"; /* eslint-disable react/display-name */ function WidgetsEditor() { const { deselectAll, focusWidget, selectWidget } = useWidgetSelection(); - const params = useParams<{ pageId: string }>(); const dispatch = useDispatch(); - const widgets = useSelector(getCanvasWidgetDsl); - const isFetchingPage = useSelector(getIsFetchingPage); const currentPageId = useSelector(getCurrentPageId); const currentPageName = useSelector(getCurrentPageName); const currentApp = useSelector(getCurrentApplication); - + const isFetchingPage = useSelector(getIsFetchingPage); const showOnboardingTasks = useSelector(getIsOnboardingTasksView); const enableFirstTimeUserOnboarding = useSelector( getIsFirstTimeUserOnboardingEnabled, @@ -107,29 +72,21 @@ function WidgetsEditor() { } }, [isFetchingPage, selectWidget]); - const handleWrapperClick = useCallback(() => { - focusWidget && focusWidget(); - deselectAll && deselectAll(); - dispatch(closePropertyPane()); - dispatch(closeTableFilterPane()); - dispatch(setCanvasSelectionFromEditor(false)); - }, [focusWidget, deselectAll]); - - const pageLoading = ( - - - - ); - let node: ReactNode; - if (isFetchingPage) { - node = pageLoading; - } - - if (!isFetchingPage && widgets) { - node = ; - } const allowDragToSelect = useAllowEditorDragToSelect(); + const handleWrapperClick = useCallback(() => { + if (allowDragToSelect) { + focusWidget && focusWidget(); + deselectAll && deselectAll(); + dispatch(closePropertyPane()); + dispatch(closeTableFilterPane()); + dispatch(setCanvasSelectionFromEditor(false)); + } + }, [allowDragToSelect, focusWidget, deselectAll]); + + /** + * drag event handler for selection drawing + */ const onDragStart = useCallback( (e: any) => { e.preventDefault(); @@ -155,19 +112,18 @@ function WidgetsEditor() { !isOnboardingWidgetSelection ? ( ) : ( - - - - {node} - - + + - + +
)} ); diff --git a/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx b/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx new file mode 100644 index 0000000000..c70f2729e2 --- /dev/null +++ b/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { render, screen } from "test/testUtils"; +import "@testing-library/jest-dom"; +import Table from "../QueryEditor/Table"; + +describe("Query Editor Table", () => { + it("it should render table with missing key", () => { + render(); + const date = screen.getByText(/Jan 1 1970 10:15AM/i); + expect(date).toBeInTheDocument(); + }); +}); diff --git a/app/client/src/pages/Editor/index.tsx b/app/client/src/pages/Editor/index.tsx index c7adcf6503..67e5e9f97f 100644 --- a/app/client/src/pages/Editor/index.tsx +++ b/app/client/src/pages/Editor/index.tsx @@ -28,7 +28,6 @@ import { ThemeProvider } from "styled-components"; import { Theme } from "constants/DefaultTheme"; import GlobalHotKeys from "./GlobalHotKeys"; import { handlePathUpdated } from "actions/recentEntityActions"; -import AppComments from "comments/AppComments/AppComments"; import AddCommentTourComponent from "comments/tour/AddCommentTourComponent"; import CommentShowCaseCarousel from "comments/CommentsShowcaseCarousel"; import GitSyncModal from "pages/Editor/gitSync/GitSyncModal"; @@ -206,7 +205,6 @@ class Editor extends Component { - diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 598677ff45..6693db9036 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -12,6 +12,7 @@ import { getAppMode } from "selectors/applicationSelectors"; import { getCurrentApplicationLayout, getCurrentPageId, + previewModeSelector, } from "selectors/editorSelectors"; import styled from "styled-components"; import { getNearestParentCanvas } from "utils/generators"; @@ -20,6 +21,7 @@ import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { XYCord } from "utils/hooks/useCanvasDragging"; import { theme } from "constants/DefaultTheme"; import { commentModeSelector } from "../../selectors/commentsSelectors"; +import { getIsDraggingForSelection } from "selectors/canvasSelectors"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -67,6 +69,7 @@ export function CanvasSelectionArena({ (parentWidget && parentWidget.detachFromLayout) ); const appMode = useSelector(getAppMode); + const isPreviewMode = useSelector(previewModeSelector); const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); @@ -107,9 +110,7 @@ export function CanvasSelectionArena({ ), [widgetId, snapColumnSpace, snapRowSpace], ); - const isDraggingForSelection = useSelector((state: AppState) => { - return state.ui.canvasSelection.isDraggingForSelection; - }); + const isDraggingForSelection = useSelector(getIsDraggingForSelection); const isCurrentWidgetDrawing = useSelector((state: AppState) => { return state.ui.canvasSelection.widgetId === widgetId; }); @@ -391,7 +392,7 @@ export function CanvasSelectionArena({ const addEventListeners = () => { canvasRef.current?.addEventListener("click", onClick, false); canvasRef.current?.addEventListener("mousedown", onMouseDown, false); - canvasRef.current?.addEventListener("mouseup", onMouseUp, false); + document.addEventListener("mouseup", onMouseUp, false); canvasRef.current?.addEventListener("mousemove", onMouseMove, false); canvasRef.current?.addEventListener("mouseleave", onMouseLeave, false); canvasRef.current?.addEventListener("mouseenter", onMouseEnter, false); @@ -399,7 +400,7 @@ export function CanvasSelectionArena({ }; const removeEventListeners = () => { canvasRef.current?.removeEventListener("mousedown", onMouseDown); - canvasRef.current?.removeEventListener("mouseup", onMouseUp); + document?.removeEventListener("mouseup", onMouseUp); canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseleave", onMouseLeave); canvasRef.current?.removeEventListener("mouseenter", onMouseEnter); @@ -443,7 +444,8 @@ export function CanvasSelectionArena({ ]); const shouldShow = - appMode === APP_MODE.EDIT && !(isDragging || isResizing || isCommentMode); + appMode === APP_MODE.EDIT && + !(isDragging || isResizing || isCommentMode || isPreviewMode); return shouldShow ? ( + Page Unavailable -
+

{createMessage(PAGE_NOT_FOUND)}

Either this page doesn't exist, or you don't have access to{" "} diff --git a/app/client/src/pages/setup/DataCollectionForm.tsx b/app/client/src/pages/setup/DataCollectionForm.tsx index bccda17acb..dc9771e217 100644 --- a/app/client/src/pages/setup/DataCollectionForm.tsx +++ b/app/client/src/pages/setup/DataCollectionForm.tsx @@ -38,8 +38,8 @@ export default memo(function DataCollectionForm() { const [allowCollection, setAllowCollection] = useState(true); return ( - - 2. + + 2. {createMessage(WELCOME_FORM_DATA_COLLECTION_HEADER)} diff --git a/app/client/src/pages/setup/DetailsForm.tsx b/app/client/src/pages/setup/DetailsForm.tsx index 799ae4502f..a701a5a690 100644 --- a/app/client/src/pages/setup/DetailsForm.tsx +++ b/app/client/src/pages/setup/DetailsForm.tsx @@ -45,8 +45,8 @@ export default function DetailsForm( return ( - - 1. + + 1. {createMessage(WELCOME_FORM_HEADER)} diff --git a/app/client/src/pages/setup/NewsletterForm.tsx b/app/client/src/pages/setup/NewsletterForm.tsx index 113b9baf3c..8626110a4f 100644 --- a/app/client/src/pages/setup/NewsletterForm.tsx +++ b/app/client/src/pages/setup/NewsletterForm.tsx @@ -36,8 +36,8 @@ const NewsletterContainer = styled.div` export default memo(function NewsletterForm() { return ( - - 3. + + 3. {createMessage(WELCOME_FORM_NEWLETTER_HEADER)} diff --git a/app/client/src/pages/setup/common.tsx b/app/client/src/pages/setup/common.tsx index ace96055a8..1e3029d59c 100644 --- a/app/client/src/pages/setup/common.tsx +++ b/app/client/src/pages/setup/common.tsx @@ -13,16 +13,12 @@ export const FormHeaderWrapper = styled.div` export const FormHeaderLabel = styled.h5` width: 100%; font-size: 20px; - margin: 8px 0 16px; font-weight: 500; `; export const FormHeaderIndex = styled.h5` font-size: 20px; font-weight: 500; - position: absolute; - left: -33px; - top: -33px; `; export const FormBodyWrapper = styled.div` diff --git a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx index 0726a34f27..1b06c76f9a 100644 --- a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx +++ b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx @@ -5,7 +5,7 @@ import { ReduxAction, } from "constants/ReduxActionConstants"; import { WidgetProps } from "widgets/BaseWidget"; -import { UpdateCanvasLayout } from "actions/controlActions"; +import { UpdateCanvasLayoutPayload } from "actions/controlActions"; import { set } from "lodash"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; @@ -32,7 +32,7 @@ const canvasWidgetsReducer = createImmerReducer(initialState, { }, [ReduxActionTypes.UPDATE_CANVAS_LAYOUT]: ( state: CanvasWidgetsReduxState, - action: ReduxAction, + action: ReduxAction, ) => { set(state[MAIN_CONTAINER_WIDGET_ID], "rightColumn", action.payload.width); set(state[MAIN_CONTAINER_WIDGET_ID], "minHeight", action.payload.height); diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx index 7a365aec29..9aac98ecc2 100644 --- a/app/client/src/reducers/uiReducers/editorReducer.tsx +++ b/app/client/src/reducers/uiReducers/editorReducer.tsx @@ -7,7 +7,6 @@ import { } from "constants/ReduxActionConstants"; import moment from "moment"; import { PageAction } from "constants/AppsmithActionConstants/ActionConstants"; -import { CommentsReduxState } from "./commentsReducer/interfaces"; const initialState: EditorReduxState = { initialized: false, @@ -28,6 +27,8 @@ const initialState: EditorReduxState = { updateWidgetNameError: false, }, isSnipingMode: false, + isPreviewMode: false, + zoomLevel: 1, }; const editorReducer = createReducer(initialState, { @@ -175,7 +176,7 @@ const editorReducer = createReducer(initialState, { return { ...state }; }, [ReduxActionTypes.SET_SNIPING_MODE]: ( - state: CommentsReduxState, + state: EditorReduxState, action: ReduxAction, ) => { return { @@ -183,6 +184,15 @@ const editorReducer = createReducer(initialState, { isSnipingMode: action.payload, }; }, + [ReduxActionTypes.SET_PREVIEW_MODE]: ( + state: EditorReduxState, + action: ReduxAction, + ) => { + return { + ...state, + isPreviewMode: action.payload, + }; + }, }); export interface EditorReduxState { @@ -194,6 +204,8 @@ export interface EditorReduxState { lastUpdatedTime?: number; pageActions?: PageAction[][]; isSnipingMode: boolean; + isPreviewMode: boolean; + zoomLevel: number; loadingStates: { saving: boolean; savingError: boolean; diff --git a/app/client/src/reducers/uiReducers/explorerReducer.ts b/app/client/src/reducers/uiReducers/explorerReducer.ts index 309ae4375d..6ec26737f5 100644 --- a/app/client/src/reducers/uiReducers/explorerReducer.ts +++ b/app/client/src/reducers/uiReducers/explorerReducer.ts @@ -4,27 +4,48 @@ import { ReduxActionTypes, ReduxActionErrorTypes, } from "constants/ReduxActionConstants"; +import { get } from "lodash"; export interface ExplorerReduxState { - updatingEntity?: string; - updateEntityError?: string; - editingEntityName?: string; + entity: { + updatingEntity?: string; + updateEntityError?: string; + editingEntityName?: string; + }; + pinned: boolean; + width: number | undefined; + active: boolean; } -const initialState: ExplorerReduxState = {}; + +const initialState: ExplorerReduxState = { + pinned: true, + entity: {}, + width: undefined, + active: true, +}; const setUpdatingEntity = ( state: ExplorerReduxState, action: ReduxAction<{ id: string }>, ) => { - return { updatingEntity: action.payload.id, updateEntityError: undefined }; + return { + ...state, + entity: { updatingEntity: action.payload.id, updateEntityError: undefined }, + }; }; const setEntityUpdateError = (state: ExplorerReduxState) => { - return { updatingEntity: undefined, updateEntityError: state.updatingEntity }; + return { + ...state, + entity: { + updatingEntity: undefined, + updateEntityError: state.entity.updatingEntity, + }, + }; }; -const setEntityUpdateSuccess = () => { - return {}; +const setEntityUpdateSuccess = (state: ExplorerReduxState) => { + return { ...state, entity: {} }; }; const setUpdatingDatasourceEntity = ( @@ -34,10 +55,13 @@ const setUpdatingDatasourceEntity = ( const pathParts = window.location.pathname.split("/"); const pageId = pathParts[pathParts.indexOf("pages") + 1]; - if (!state.updatingEntity?.includes(action.payload.id)) { + if (!get(state, "entity.updatingEntity", "")?.includes(action.payload.id)) { return { - updatingEntity: `${action.payload.id}-${pageId}`, - updateEntityError: undefined, + ...state, + entity: { + updatingEntity: `${action.payload.id}-${pageId}`, + updateEntityError: undefined, + }, }; } @@ -101,10 +125,33 @@ const explorerReducer = createReducer(initialState, { state: ExplorerReduxState, action: ReduxAction<{ id: string }>, ) => { - return { editingEntityName: action.payload.id }; + return { ...state, entity: { editingEntityName: action.payload.id } }; }, - [ReduxActionTypes.END_EXPLORER_ENTITY_NAME_EDIT]: () => { - return {}; + [ReduxActionTypes.END_EXPLORER_ENTITY_NAME_EDIT]: ( + state: ExplorerReduxState, + ) => { + return { ...state, entity: {} }; + }, + [ReduxActionTypes.SET_EXPLORER_PINNED]: ( + state: ExplorerReduxState, + action: ReduxAction<{ shouldPin: boolean }>, + ) => { + return { ...state, pinned: action.payload.shouldPin }; + }, + [ReduxActionTypes.UPDATE_EXPLORER_WIDTH]: ( + state: ExplorerReduxState, + action: ReduxAction<{ width: number | undefined }>, + ) => { + return { ...state, width: action.payload.width }; + }, + [ReduxActionTypes.SET_EXPLORER_ACTIVE]: ( + state: ExplorerReduxState, + action: ReduxAction, + ) => { + return { + ...state, + active: action.payload, + }; }, }); diff --git a/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts index faa468b77d..bbb92510cf 100644 --- a/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts +++ b/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts @@ -66,7 +66,6 @@ export function* executeActionTriggers( executePluginActionTriggerSaga, trigger.payload, eventType, - triggerMeta, ); break; case ActionTriggerType.CLEAR_PLUGIN_ACTION: diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts index 56ff16bbf1..3ef1f12aa2 100644 --- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts @@ -86,7 +86,6 @@ import { RunPluginActionDescription } from "entities/DataTree/actionTriggers"; import { APP_MODE } from "entities/App"; import { FileDataTypes } from "widgets/constants"; import { hideDebuggerErrors } from "actions/debuggerActions"; -import { TriggerMeta } from "sagas/ActionExecution/ActionExecutionSagas"; import { PluginTriggerFailureError, PluginActionExecutionError, @@ -243,7 +242,6 @@ function* confirmRunActionSaga() { export default function* executePluginActionTriggerSaga( pluginAction: RunPluginActionDescription["payload"], eventType: EventType, - triggerMeta: TriggerMeta, ) { const { actionId, params } = pluginAction; PerformanceTracker.startAsyncTracking( @@ -310,7 +308,6 @@ export default function* executePluginActionTriggerSaga( throw new PluginTriggerFailureError( createMessage(ERROR_PLUGIN_ACTION_EXECUTE, action.name), [payload.body, params], - triggerMeta, ); } else { AppsmithConsole.info({ diff --git a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts index c1a7bdb3e2..0d428a57c9 100644 --- a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts @@ -8,8 +8,8 @@ import { all, call } from "redux-saga/effects"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import log from "loglevel"; import { + logActionExecutionError, PluginTriggerFailureError, - UncaughtAppsmithPromiseError, UserCancelledActionExecutionError, } from "sagas/ActionExecution/errorUtils"; @@ -65,7 +65,12 @@ export default function* executePromiseSaga( }); } else { log.error(e); - throw new UncaughtAppsmithPromiseError(e.message, triggerMeta, e); + /* Logging the error instead of throwing an error as it was making the ui to go into a loading states */ + logActionExecutionError( + e.message, + triggerMeta.source, + triggerMeta.triggerPropertyName, + ); } } diff --git a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts index 50e4dfc13c..14c65a51aa 100644 --- a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts @@ -1,4 +1,4 @@ -import { Variant } from "components/ads/common"; +import { ToastTypeOptions, Variant } from "components/ads/common"; import { Toaster } from "components/ads/Toast"; import AppsmithConsole from "utils/AppsmithConsole"; import { ShowAlertActionDescription } from "entities/DataTree/actionTriggers"; @@ -32,7 +32,9 @@ export default function* showAlertSaga( } if (payload.style && !variant) { throw new TriggerFailureError( - `Toast type needs to be a one of ${Object.values(Variant).join(", ")}`, + `Toast type needs to be a one of ${Object.values(ToastTypeOptions).join( + ", ", + )}`, triggerMeta, ); } diff --git a/app/client/src/sagas/ActionExecution/errorUtils.ts b/app/client/src/sagas/ActionExecution/errorUtils.ts index 8fd3982f17..cc57d4ca9c 100644 --- a/app/client/src/sagas/ActionExecution/errorUtils.ts +++ b/app/client/src/sagas/ActionExecution/errorUtils.ts @@ -58,15 +58,11 @@ export const logActionExecutionError = ( }); }; -export class PluginTriggerFailureError extends TriggerFailureError { +export class PluginTriggerFailureError extends Error { responseData: unknown[] = []; - constructor( - reason: string, - responseData: unknown[], - triggerMeta: TriggerMeta, - ) { - super(reason, triggerMeta); + constructor(reason: string, responseData: unknown[]) { + super(reason); this.responseData = responseData; } } diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 808516717b..8c6d98391b 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -35,10 +35,7 @@ import { updateActionProperty, updateActionSuccess, } from "actions/pluginActionActions"; -import { - getDynamicBindingsChangesSaga, - removeBindingsFromActionObject, -} from "utils/DynamicBindingUtils"; +import { getDynamicBindingsChangesSaga } from "utils/DynamicBindingUtils"; import { validateResponse } from "./ErrorSagas"; import { transformRestAction } from "transformers/RestActionTransformer"; import { @@ -110,7 +107,7 @@ import { filterCategories, SEARCH_CATEGORY_ID, } from "components/editorComponents/GlobalSearch/utils"; -import { getSelectedWidget, getWidgetById } from "./selectors"; +import { getSelectedWidget, getWidgetByID } from "./selectors"; import { onApiEditor, onQueryEditor, @@ -447,11 +444,10 @@ function* moveActionSaga( }>, ) { const actionObject: Action = yield select(getAction, action.payload.id); - const withoutBindings = removeBindingsFromActionObject(actionObject); try { const response = yield ActionAPI.moveAction({ action: { - ...withoutBindings, + ...actionObject, pageId: action.payload.originalPageId, name: action.payload.name, }, @@ -490,12 +486,9 @@ function* moveActionSaga( function* copyActionSaga( action: ReduxAction<{ id: string; destinationPageId: string; name: string }>, ) { - let actionObject: Action = yield select(getAction, action.payload.id); + const actionObject: Action = yield select(getAction, action.payload.id); try { if (!actionObject) throw new Error("Could not find action to copy"); - if (action.payload.destinationPageId !== actionObject.pageId) { - actionObject = removeBindingsFromActionObject(actionObject); - } const copyAction = Object.assign({}, actionObject, { name: action.payload.name, @@ -646,7 +639,9 @@ function* saveActionName(action: ReduxAction<{ id: string; name: string }>) { } } -function* setActionPropertySaga(action: ReduxAction) { +export function* setActionPropertySaga( + action: ReduxAction, +) { const { actionId, propertyName, value } = action.payload; if (!actionId) return; if (propertyName === "name") return; @@ -796,8 +791,7 @@ function* buildMetaForSnippets( } if (entityType === ENTITY_TYPE.WIDGET && entityId) { const currentEntity: FlattenedWidgetProps = yield select( - getWidgetById, - entityId, + getWidgetByID(entityId), ); const type: string = currentEntity.type || ""; refinements.entities = [type, entityType]; diff --git a/app/client/src/sagas/DraggingCanvasSagas.ts b/app/client/src/sagas/DraggingCanvasSagas.ts index b88bce2f6a..b7fed39368 100644 --- a/app/client/src/sagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/DraggingCanvasSagas.ts @@ -19,7 +19,7 @@ import { calculateDropTargetRows } from "components/editorComponents/DropTargetU import { GridDefaults } from "constants/WidgetConstants"; import { WidgetProps } from "widgets/BaseWidget"; import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; export type WidgetMoveParams = { widgetId: string; diff --git a/app/client/src/sagas/OnboardingSagas.ts b/app/client/src/sagas/OnboardingSagas.ts index 50ee93b7ba..01c91bf1ab 100644 --- a/app/client/src/sagas/OnboardingSagas.ts +++ b/app/client/src/sagas/OnboardingSagas.ts @@ -90,7 +90,6 @@ import { getQueryIdFromURL } from "pages/Editor/Explorer/helpers"; // import { calculateNewWidgetPosition } from "./WidgetOperationSagas"; import { RenderModes } from "constants/WidgetConstants"; import { generateReactKey } from "utils/generators"; -import { forceOpenPropertyPane } from "actions/widgetActions"; import { navigateToCanvas } from "pages/Editor/Explorer/Widgets/utils"; import { batchUpdateWidgetProperty, @@ -721,7 +720,6 @@ function* addWidget(widgetConfig: any) { type: ReduxActionTypes.SELECT_WIDGET_INIT, payload: { widgetId: newWidget.newWidgetId }, }); - yield put(forceOpenPropertyPane(newWidget.newWidgetId)); } catch (error) {} } @@ -835,7 +833,6 @@ function* addOnSubmitHandler() { type: ReduxActionTypes.SELECT_WIDGET_INIT, payload: { widgetId: inputWidget.widgetId }, }); - yield put(forceOpenPropertyPane(inputWidget.widgetId)); yield put( updateWidgetPropertyRequest( diff --git a/app/client/src/sagas/ReplaySaga.ts b/app/client/src/sagas/ReplaySaga.ts index 12c086f724..121b6445a4 100644 --- a/app/client/src/sagas/ReplaySaga.ts +++ b/app/client/src/sagas/ReplaySaga.ts @@ -8,10 +8,7 @@ import { getIsPropertyPaneVisible, getCurrentWidgetId, } from "../selectors/propertyPaneSelectors"; -import { - closePropertyPane, - forceOpenPropertyPane, -} from "actions/widgetActions"; +import { closePropertyPane } from "actions/widgetActions"; import { selectMultipleWidgetsInitAction, selectWidgetAction, @@ -60,7 +57,6 @@ export function* openPropertyPaneSaga(replay: any) { //if property pane is not visible, select the widget and force open property pane if (selectedWidgetId !== replayWidgetId || !isPropertyPaneVisible) { yield put(selectWidgetAction(replayWidgetId, false)); - yield put(forceOpenPropertyPane(replayWidgetId)); } flashElementsById( diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts index 9ce550b138..346a05e897 100644 --- a/app/client/src/sagas/SelectionCanvasSagas.ts +++ b/app/client/src/sagas/SelectionCanvasSagas.ts @@ -1,5 +1,5 @@ import { selectMultipleWidgetsAction } from "actions/widgetSelectionActions"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { isEqual } from "lodash"; diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index b3a60a2811..1b318341b8 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -204,6 +204,7 @@ function* deleteSaga(deleteAction: ReduxAction) { if (!disallowUndo) { // close property pane after delete yield put(closePropertyPane()); + yield put(selectWidgetInitAction(undefined)); yield call(postDelete, widgetId, widgetName, otherWidgetsToDelete); } } diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 0790a65ba9..64c397aa07 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -56,7 +56,6 @@ import { getCurrentApplicationId, getCurrentPageId, } from "selectors/editorSelectors"; -import { forceOpenPropertyPane } from "actions/widgetActions"; import { selectMultipleWidgetsInitAction } from "actions/widgetSelectionActions"; import { getDataTree } from "selectors/dataTreeSelectors"; @@ -1097,7 +1096,6 @@ function* addSuggestedWidget(action: ReduxAction>) { widgetId: newWidget.newWidgetId, applicationId, }); - yield put(forceOpenPropertyPane(newWidget.newWidgetId)); } catch (error) { log.error(error); } diff --git a/app/client/src/sagas/selectors.tsx b/app/client/src/sagas/selectors.tsx index f39c014309..6b6ec31461 100644 --- a/app/client/src/sagas/selectors.tsx +++ b/app/client/src/sagas/selectors.tsx @@ -19,6 +19,15 @@ export const getWidgetsMeta = (state: AppState) => state.entities.meta; export const getWidgetMetaProps = (state: AppState, widgetId: string) => state.entities.meta[widgetId]; +export const getWidgetByID = (widgetId: string) => { + return createSelector( + getWidgets, + (canvasWidgets: { [widgetId: string]: FlattenedWidgetProps }) => { + return canvasWidgets[widgetId]; + }, + ); +}; + export const getWidget = (state: AppState, widgetId: string): WidgetProps => { return state.entities.canvasWidgets[widgetId]; }; @@ -111,14 +120,6 @@ export const getWidgetByName = ( ); }; -export const getWidgetById = ( - state: AppState, - id: string, -): FlattenedWidgetProps | undefined => { - const widgets = state.entities.canvasWidgets; - return widgets[id]; -}; - export const getAllPageIds = (state: AppState) => { return state.entities.pageList.pages.map((page) => page.pageId); }; diff --git a/app/client/src/selectors/canvasSelectors.ts b/app/client/src/selectors/canvasSelectors.ts new file mode 100644 index 0000000000..2613bc0edf --- /dev/null +++ b/app/client/src/selectors/canvasSelectors.ts @@ -0,0 +1,5 @@ +import { AppState } from "reducers"; + +export const getIsDraggingForSelection = (state: AppState) => { + return state.ui.canvasSelection.isDraggingForSelection; +}; diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 1f13740df3..d10ecb3520 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -9,7 +9,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { PageListReduxState } from "reducers/entityReducers/pageListReducer"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { getActions, getCanvasWidgets, @@ -97,6 +97,9 @@ export const getPageList = (state: AppState) => state.entities.pageList.pages; export const getCurrentPageId = (state: AppState) => state.entities.pageList.currentPageId; +export const getCurrentApplication = (state: AppState) => + state.ui.applications.currentApplication; + export const getCurrentApplicationId = (state: AppState) => state.entities.pageList.applicationId || ""; /** this is set during init can assume it to be defined */ @@ -369,3 +372,23 @@ export const getJSCollectionById = createSelector( export const getApplicationLastDeployedAt = (state: AppState) => state.ui.applications.currentApplication?.lastDeployedAt; + +/** + * returns the `state.ui.editor.isPreviewMode` + * + * @param state AppState + * @returns boolean + */ +export const previewModeSelector = (state: AppState) => { + return state.ui.editor.isPreviewMode; +}; + +/** + * returns the `state.ui.editor.zoomLevel` + * + * @param state AppState + * @returns number + */ +export const getZoomLevel = (state: AppState) => { + return state.ui.editor.zoomLevel; +}; diff --git a/app/client/src/selectors/entitiesSelector.ts b/app/client/src/selectors/entitiesSelector.ts index ac21aea257..a70da164e0 100644 --- a/app/client/src/selectors/entitiesSelector.ts +++ b/app/client/src/selectors/entitiesSelector.ts @@ -306,15 +306,6 @@ export const getJSCollectionsForCurrentPage = createSelector( }, ); -export const getQueryActionsForCurrentPage = createSelector( - getActionsForCurrentPage, - (actions) => { - return actions.filter((action) => { - return action.config.pluginType === PluginType.DB; - }); - }, -); - export const getPlugin = (state: AppState, pluginId: string) => { return state.entities.plugins.list.find((plugin) => plugin.id === pluginId); }; diff --git a/app/client/src/selectors/explorerSelector.ts b/app/client/src/selectors/explorerSelector.ts new file mode 100644 index 0000000000..563af5d080 --- /dev/null +++ b/app/client/src/selectors/explorerSelector.ts @@ -0,0 +1,31 @@ +import { AppState } from "reducers"; + +/** + * returns the pinned state of explorer + * + * @param state + * @returns + */ +export const getExplorerPinned = (state: AppState) => { + return state.ui.explorer.pinned; +}; + +/** + * returns the width of explorer + * + * @param state + * @returns + */ +export const getExplorerWidth = (state: AppState) => { + return state.ui.explorer.width; +}; + +/** + * returns the active state of explorer + * + * @param state + * @returns + */ +export const getExplorerActive = (state: AppState) => { + return state.ui.explorer.active; +}; diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 187498b99b..1d46d35a53 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -27,12 +27,12 @@ export const getCurrentWidgetId = createSelector( export const getCurrentWidgetProperties = createSelector( getCanvasWidgets, - getPropertyPaneState, + getSelectedWidgets, ( widgets: CanvasWidgetsReduxState, - pane: PropertyPaneReduxState, + selectedWidgetIds: string[], ): WidgetProps | undefined => { - return get(widgets, `${pane.widgetId}`); + return get(widgets, `${selectedWidgetIds[0]}`); }, ); diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx index cb00830447..39384e40e3 100644 --- a/app/client/src/utils/AppsmithUtils.tsx +++ b/app/client/src/utils/AppsmithUtils.tsx @@ -1,4 +1,8 @@ -import { ReduxAction } from "constants/ReduxActionConstants"; +import { + CurrentApplicationData, + Page, + ReduxAction, +} from "constants/ReduxActionConstants"; import { getAppsmithConfigs } from "configs"; import * as Sentry from "@sentry/react"; import AnalyticsUtil from "./AnalyticsUtil"; @@ -13,6 +17,12 @@ import { AppIconCollection, AppIconName } from "components/ads/AppIcon"; import { ERROR_CODES } from "constants/ApiConstants"; import { createMessage, ERROR_500 } from "../constants/messages"; import localStorage from "utils/localStorage"; +import { APP_MODE } from "entities/App"; +import { trimQueryString } from "./helpers"; +import { + getApplicationEditorPageURL, + getApplicationViewerPageURL, +} from "constants/routes"; export const createReducer = ( initialState: any, @@ -345,3 +355,31 @@ export const parseBlobUrl = (blobId: string) => { }/${blobId.substring(5)}`; return url.split("?type="); }; + +/** + * gets the page url + * + * Note: for edit mode, the page will have different url ( contains '/edit' at the end ) + * + * @param page + * @returns + */ +export const getPageURL = ( + page: Page, + appMode: APP_MODE | undefined, + currentApplicationDetails: CurrentApplicationData | undefined, +) => { + if (appMode === APP_MODE.PUBLISHED) { + return trimQueryString( + getApplicationViewerPageURL({ + applicationId: currentApplicationDetails?.id, + pageId: page.pageId, + }), + ); + } + + return getApplicationEditorPageURL( + currentApplicationDetails?.id, + page.pageId, + ); +}; diff --git a/app/client/src/utils/DSLMigrations.ts b/app/client/src/utils/DSLMigrations.ts index ed5cc50306..a5cb509894 100644 --- a/app/client/src/utils/DSLMigrations.ts +++ b/app/client/src/utils/DSLMigrations.ts @@ -24,6 +24,7 @@ import { migrateTableWidgetSelectedRowBindings, migrateTableSanitizeColumnKeys, isSortableMigration, + migrateTableWidgetIconButtonVariant, } from "./migrations/TableWidget"; import { migrateTextStyleFromTextWidget } from "./migrations/TextWidgetReplaceTextStyle"; import { DATA_BIND_REGEX_GLOBAL } from "constants/BindingsConstants"; @@ -978,6 +979,10 @@ export const transformDSL = ( } if (currentDSL.version === 44) { currentDSL = isSortableMigration(currentDSL); + currentDSL.version = 45; + } + if (currentDSL.version === 45) { + currentDSL = migrateTableWidgetIconButtonVariant(currentDSL); currentDSL.version = LATEST_PAGE_VERSION; } diff --git a/app/client/src/utils/DynamicBindingUtils.ts b/app/client/src/utils/DynamicBindingUtils.ts index 2ce82a896e..47054974c0 100644 --- a/app/client/src/utils/DynamicBindingUtils.ts +++ b/app/client/src/utils/DynamicBindingUtils.ts @@ -1,8 +1,5 @@ import _, { VERSION as lodashVersion } from "lodash"; -import { - DATA_BIND_REGEX, - DATA_BIND_REGEX_GLOBAL, -} from "constants/BindingsConstants"; +import { DATA_BIND_REGEX } from "constants/BindingsConstants"; import { Action } from "entities/Action"; import moment from "moment-timezone"; import { WidgetProps } from "widgets/BaseWidget"; @@ -21,11 +18,6 @@ export type FormEditorConfigs = Record; export type FormSettingsConfigs = Record; export type FormDependencyConfigs = Record; -export const removeBindingsFromActionObject = (obj: Action) => { - const string = JSON.stringify(obj); - const withBindings = string.replace(DATA_BIND_REGEX_GLOBAL, "{{ }}"); - return JSON.parse(withBindings); -}; // referencing DATA_BIND_REGEX fails for the value "{{Table1.tableData[Table1.selectedRowIndex]}}" if you run it multiple times and don't recreate export const isDynamicValue = (value: string): boolean => DATA_BIND_REGEX.test(value); diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index ca8e87e179..8f24efd4eb 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -8,7 +8,7 @@ import { } from "widgets/BaseWidget"; import { GridDefaults, RenderMode } from "constants/WidgetConstants"; import { snapToGrid } from "./helpers"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import defaultTemplate from "templates/default"; import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer"; import { transformDSL } from "./DSLMigrations"; diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index a4e2955c28..ba1fabdbee 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -97,6 +97,9 @@ import AudioWidget, { import AudioRecorderWidget, { CONFIG as AUDIO_RECORDER_WIDGET_CONFIG, } from "widgets/AudioRecorderWidget"; +import DocumentViewerWidget, { + CONFIG as DOCUMENT_VIEWER_WIDGET_CONFIG, +} from "widgets/DocumentViewerWidget"; import ButtonGroupWidget, { CONFIG as BUTTON_GROUP_CONFIG, } from "widgets/ButtonGroupWidget"; @@ -147,6 +150,7 @@ export const registerWidgets = () => { registerWidget(FilePickerWidgetV2, FILEPICKER_WIDGET_V2_CONFIG); registerWidget(StatboxWidget, STATBOX_WIDGET_CONFIG); registerWidget(AudioRecorderWidget, AUDIO_RECORDER_WIDGET_CONFIG); + registerWidget(DocumentViewerWidget, DOCUMENT_VIEWER_WIDGET_CONFIG); registerWidget(ButtonGroupWidget, BUTTON_GROUP_CONFIG); registerWidget(MultiSelectTreeWidget, MULTI_SELECT_TREE_WIDGET_CONFIG); registerWidget(SingleSelectTreeWidget, SINGLE_SELECT_TREE_WIDGET_CONFIG); diff --git a/app/client/src/utils/getPathAndValueFromActionDiffObject.ts b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts new file mode 100644 index 0000000000..6e8448adfa --- /dev/null +++ b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts @@ -0,0 +1,60 @@ +import * as Sentry from "@sentry/react"; + +//Following function is the fix for the missing where key +/** + * NOTE: + * Action object returned by getAction comes from state.entities.action + * action api's payload is created from state.entities.action and response is saved in the same key + * Data passed to redux form is the merge of values present in state.entities.action, editorConfig, settingsConfig and has the correct datastrucure + * Data structure in state.entities.action is not correct + * Q. What does the following fix do? + * A. It calculates the diff between merged values and state.entities.action and saves the same in state.entities.action + * There is another key form that holds the formData + */ +export function getPathAndValueFromActionDiffObject(actionObjectDiff: any) { + if (!actionObjectDiff) { + return { + path: undefined, + value: undefined, + }; + } else { + let path = ""; + let value = ""; + // Loop through the diff objects in difference Array + for (let i = 0; i < actionObjectDiff.length; i++) { + //kind = N indicates a newly added property/element + //This property is present in initialValues but not in action object + if ( + actionObjectDiff && + actionObjectDiff[i].hasOwnProperty("kind") && + actionObjectDiff[i].path && + Array.isArray(actionObjectDiff[i].path) && + actionObjectDiff[i].path.length && + actionObjectDiff[i]?.kind === "N" + ) { + // Calculate path from path[] in diff + path = actionObjectDiff[i].path.reduce( + (acc: string, item: number | string) => { + try { + if (typeof item === "string" && acc) { + acc += `${path}.${item}`; + } else if (typeof item === "string" && !acc) { + acc += `${item}`; + } else acc += `${path}[${item}]`; + return acc; + } catch (error) { + Sentry.captureException({ + message: `Adding key: where failed, cannot create path`, + oldData: actionObjectDiff, + }); + } + }, + "", + ); + // get value from diff object + value = actionObjectDiff[i]?.rhs; + } + return { path, value }; + } + } +} diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 192ae72471..d74e5d5128 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -587,3 +587,20 @@ export const getQueryParamsObject = () => { return {}; } }; + +/* + * unfocus all window selection + * + * @param document + * @param window + */ +export function unFocus(document: Document, window: Window) { + if (document.getSelection()) { + document.getSelection()?.empty(); + } else { + try { + window.getSelection()?.removeAllRanges(); + // eslint-disable-next-line no-empty + } catch (e) {} + } +} diff --git a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts index c62505e173..11fe68fc5d 100644 --- a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts +++ b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts @@ -1,6 +1,9 @@ import { AppState } from "reducers"; import { commentModeSelector } from "selectors/commentsSelectors"; -import { snipingModeSelector } from "selectors/editorSelectors"; +import { + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; import { useSelector } from "store"; export const useAllowEditorDragToSelect = () => { @@ -30,10 +33,12 @@ export const useAllowEditorDragToSelect = () => { const isResizingOrDragging = !!isResizing || !!isDragging || !!isSelecting; const isCommentMode = useSelector(commentModeSelector); const isSnipingMode = useSelector(snipingModeSelector); + const isPreviewMode = useSelector(previewModeSelector); return ( !isResizingOrDragging && !isDraggingDisabled && !isCommentMode && - !isSnipingMode + !isSnipingMode && + !isPreviewMode ); }; diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index f6412ae58a..771212bcb7 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -9,7 +9,7 @@ import { AppState } from "reducers"; import { getSelectedWidgets } from "selectors/ui"; import { getOccupiedSpaces } from "selectors/editorSelectors"; import { getTableFilterState } from "selectors/tableFilterSelectors"; -import { OccupiedSpace } from "constants/editorConstants"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { getDragDetails, getWidgets } from "sagas/selectors"; import { getDropZoneOffsets, @@ -23,7 +23,6 @@ import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useDispatch } from "react-redux"; import { ReduxActionTypes } from "constants/ReduxActionConstants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { useShowPropertyPane } from "./dragResizeHooks"; import { useWidgetSelection } from "./useWidgetSelection"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { snapToGrid } from "utils/helpers"; @@ -52,7 +51,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetId, }: CanvasDraggingArenaProps) => { const dispatch = useDispatch(); - const showPropertyPane = useShowPropertyPane(); const { selectWidget } = useWidgetSelection(); const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING; @@ -233,7 +231,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ // Not needed for most widgets except for Modal Widget. setTimeout(() => { selectWidget(updateWidgetParams.payload.newWidgetId); - showPropertyPane(updateWidgetParams.payload.newWidgetId); }, 100); AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { widgetType: dragDetails.newWidget.type, diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index fcd0cbd96a..5119908d21 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -5,6 +5,8 @@ import { import { debounce, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useEffect } from "react"; +import { useSelector } from "react-redux"; +import { getZoomLevel } from "selectors/editorSelectors"; import { getNearestParentCanvas } from "utils/generators"; import { noCollision } from "utils/WidgetPropsUtils"; import { useWidgetDragResize } from "./dragResizeHooks"; @@ -32,6 +34,7 @@ export const useCanvasDragging = ( widgetId, }: CanvasDraggingArenaProps, ) => { + const canvasZoomLevel = useSelector(getZoomLevel); const { devicePixelRatio: scale = 1 } = window; const { @@ -88,11 +91,12 @@ export const useCanvasDragging = ( height: scrollParentTopHeight, } = parentCanvas.getBoundingClientRect(); const { width } = canvasRef.current.getBoundingClientRect(); - canvasDrawRef.current.style.width = width + "px"; + canvasDrawRef.current.style.width = width / canvasZoomLevel + "px"; canvasDrawRef.current.style.position = canExtend ? "absolute" : "sticky"; canvasDrawRef.current.style.left = "0px"; canvasDrawRef.current.style.top = getCanvasTopOffset() + "px"; - canvasDrawRef.current.style.height = scrollParentTopHeight + "px"; + canvasDrawRef.current.style.height = + scrollParentTopHeight / canvasZoomLevel + "px"; } }; @@ -323,6 +327,7 @@ export const useCanvasDragging = ( canvasDrawRef.current.height, ); isUpdatingRows = false; + canvasCtx.transform(canvasZoomLevel, 0, 0, canvasZoomLevel, 0, 0); if (canvasIsDragging) { currentRectanglesToDraw.forEach((each) => { drawBlockOnCanvas(each); diff --git a/app/client/src/utils/hooks/useClickToSelectWidget.tsx b/app/client/src/utils/hooks/useClickToSelectWidget.tsx index 8340a83fd5..74b19bb41d 100644 --- a/app/client/src/utils/hooks/useClickToSelectWidget.tsx +++ b/app/client/src/utils/hooks/useClickToSelectWidget.tsx @@ -14,7 +14,6 @@ import { getWidgets } from "sagas/selectors"; import { useWidgetSelection } from "./useWidgetSelection"; import React, { ReactNode, useCallback } from "react"; import { stopEventPropagation } from "utils/AppsmithUtils"; -import { useShowPropertyPane } from "./dragResizeHooks"; /** * @@ -102,7 +101,6 @@ export function ClickContentToOpenPropPane({ export const useClickToSelectWidget = () => { const { focusWidget, selectWidget } = useWidgetSelection(); - const showPropertyPane = useShowPropertyPane(); const isPropPaneVisible = useSelector(getIsPropertyPaneVisible); const isTableFilterPaneVisible = useSelector(getIsTableFilterPaneVisible); const widgets: CanvasWidgetsReduxState = useSelector(getWidgets); @@ -146,7 +144,7 @@ export const useClickToSelectWidget = () => { selectWidget(focusedWidgetId, isMultiSelect); focusWidget(focusedWidgetId); } - showPropertyPane(); + if (isMultiSelect) { e.stopPropagation(); } diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index b228ef5928..aa60919fb5 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -1,104 +1,193 @@ -import { theme } from "constants/DefaultTheme"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { debounce, get } from "lodash"; +import { useDispatch, useSelector } from "react-redux"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { getWidgetByID, getWidgets } from "sagas/selectors"; + import { DefaultLayoutType, layoutConfigurations, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { APP_MODE } from "entities/App"; -import { debounce } from "lodash"; -import { AppsmithDefaultLayout } from "pages/Editor/MainContainerLayoutControl"; -import { useCallback, useEffect } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { AppState } from "reducers"; -import { getWidget, getWidgets } from "sagas/selectors"; -import { getAppMode } from "selectors/applicationSelectors"; +import { + getExplorerPinned, + getExplorerWidth, +} from "selectors/explorerSelector"; import { getCurrentApplicationLayout, getCurrentPageId, + previewModeSelector, } from "selectors/editorSelectors"; -import { calculateDynamicHeight } from "utils/DSLMigrations"; +import { APP_MODE } from "entities/App"; +import { scrollbarWidth } from "utils/helpers"; import { useWindowSizeHooks } from "./dragResizeHooks"; +import { getAppMode } from "selectors/entitiesSelector"; +import { updateCanvasLayoutAction } from "actions/editorActions"; +import { calculateDynamicHeight } from "utils/DSLMigrations"; + +const BORDERS_WIDTH = 2; +const GUTTER_WIDTH = 72; export const useDynamicAppLayout = () => { + const dispatch = useDispatch(); + const [initialized, setInitialized] = useState(false); + const explorerWidth = useSelector(getExplorerWidth); + const isExplorerPinned = useSelector(getExplorerPinned); + const appMode: APP_MODE | undefined = useSelector(getAppMode); + const domEntityExplorer = document.querySelector(".js-entity-explorer"); + const domPropertyPane = document.querySelector(".js-property-pane-sidebar"); const { height: screenHeight, width: screenWidth } = useWindowSizeHooks(); - const mainContainer = useSelector((state: AppState) => - getWidget(state, MAIN_CONTAINER_WIDGET_ID), - ); + const mainContainer = useSelector(getWidgetByID(MAIN_CONTAINER_WIDGET_ID)); + const isPreviewMode = useSelector(previewModeSelector); const currentPageId = useSelector(getCurrentPageId); - const appMode = useSelector(getAppMode); const canvasWidgets = useSelector(getWidgets); const appLayout = useSelector(getCurrentApplicationLayout); - const dispatch = useDispatch(); - const calculateFluidMaxWidth = ( - screenWidth: number, - layoutMaxWidth: number, - ) => { - const screenWidthWithBuffer = 0.95 * screenWidth; - const widthToFill = - appMode === APP_MODE.EDIT - ? screenWidthWithBuffer - parseInt(theme.sidebarWidth) - : screenWidth; - if (layoutMaxWidth < 0) { - return widthToFill; - } else { - return widthToFill < layoutMaxWidth ? widthToFill : layoutMaxWidth; + /** + * calculates min height + */ + const calculatedMinHeight = useMemo(() => { + return calculateDynamicHeight(canvasWidgets, mainContainer?.minHeight); + }, [mainContainer]); + + /** + * app layout range i.e minWidth and maxWidth for the current layout + * if there is no config for the current layout, use default layout i.e desktop + */ + const layoutWidthRange = useMemo(() => { + let minWidth = -1; + let maxWidth = -1; + + if (appLayout) { + const { type } = appLayout; + const currentLayoutConfig = get( + layoutConfigurations, + type, + layoutConfigurations[DefaultLayoutType], + ); + + if (currentLayoutConfig.minWidth) minWidth = currentLayoutConfig.minWidth; + if (currentLayoutConfig.maxWidth) maxWidth = currentLayoutConfig.maxWidth; + } + + return { minWidth, maxWidth }; + }, [appLayout]); + + /** + * calculate the width for the canvas + * + * cases: + * - if max width is negative, use calculated width + * - if calculated width is in range of min/max widths of layout, use calculated width + * - if calculated width is less then min width, use min Width + * - if calculated width is larger than max width, use max width + * - by default use min width + * + * @param screenWidth + * @param layoutMaxWidth + * @returns + */ + const calculateCanvasWidth = () => { + const { maxWidth, minWidth } = layoutWidthRange; + let calculatedWidth = screenWidth - scrollbarWidth(); + + // if preview mode is on, we don't need to subtract the Property Pane width + if (isPreviewMode === false) { + const propertyPaneWidth = domPropertyPane?.clientWidth || 0; + + calculatedWidth -= propertyPaneWidth; + } + + // if explorer is unpinned or its preview mode, we don't need to subtract the EE width + if (isExplorerPinned === true && isPreviewMode === false) { + const explorerWidth = domEntityExplorer?.clientWidth || 0; + + calculatedWidth -= explorerWidth; + } + + switch (true) { + case maxWidth < 0: + case appLayout?.type === "FLUID": + case calculatedWidth < maxWidth && calculatedWidth > minWidth: + return ( + calculatedWidth - + (appMode === APP_MODE.EDIT && !isPreviewMode + ? BORDERS_WIDTH + GUTTER_WIDTH + : 0) + ); + case calculatedWidth < minWidth: + return minWidth; + case calculatedWidth > maxWidth: + return maxWidth; + default: + return minWidth; } }; - const resizeToLayout = ( - screenWidth: number, - appLayout = AppsmithDefaultLayout, - ) => { - const { type } = appLayout; - const { minWidth = -1, maxWidth = -1 } = - layoutConfigurations[type] || layoutConfigurations[DefaultLayoutType]; - const calculatedMinWidth = - appMode === APP_MODE.EDIT - ? minWidth - parseInt(theme.sidebarWidth) - : minWidth; - const layoutWidth = calculateFluidMaxWidth(screenWidth, maxWidth); + /** + * resizes the layout based on the layout type + * + * @param screenWidth + * @param appLayout + */ + const resizeToLayout = () => { + const calculatedWidth = calculateCanvasWidth(); const { rightColumn } = mainContainer || {}; - if ( - (type === "FLUID" || calculatedMinWidth <= layoutWidth) && - rightColumn !== layoutWidth - ) { - dispatch({ - type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT, - payload: { - width: layoutWidth, - height: mainContainer?.minHeight, - }, - }); + + if (rightColumn !== calculatedWidth) { + dispatch( + updateCanvasLayoutAction(calculatedWidth, mainContainer?.minHeight), + ); } }; const debouncedResize = useCallback(debounce(resizeToLayout, 250), [ mainContainer, + screenWidth, ]); + /** + * when screen height is changed, update canvas layout + */ useEffect(() => { - const calculatedMinHeight = calculateDynamicHeight( - canvasWidgets, - mainContainer?.minHeight, - ); if (calculatedMinHeight !== mainContainer?.minHeight) { - dispatch({ - type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT, - payload: { - height: calculatedMinHeight, - width: mainContainer?.rightColumn, - }, - }); + dispatch( + updateCanvasLayoutAction( + mainContainer?.rightColumn, + calculatedMinHeight, + ), + ); } }, [screenHeight, mainContainer?.minHeight]); useEffect(() => { - debouncedResize(screenWidth, appLayout); + debouncedResize(); }, [screenWidth]); + /** + * resize the layout if any of the following thing changes: + * - app layout + * - page + * - container right column + * - preview mode + * - explorer width + * - explorer is pinned + */ useEffect(() => { - resizeToLayout(screenWidth, appLayout); - }, [appLayout, currentPageId, mainContainer?.rightColumn]); + resizeToLayout(); + }, [ + appLayout, + currentPageId, + mainContainer?.rightColumn, + isPreviewMode, + explorerWidth, + isExplorerPinned, + initialized, + ]); + + /** + * calling the setInitialized here so that property pane width is initialized + */ + useEffect(() => { + setInitialized(true); + }); }; diff --git a/app/client/src/utils/hooks/useHorizontalResize.tsx b/app/client/src/utils/hooks/useHorizontalResize.tsx new file mode 100644 index 0000000000..ef9e50a0b8 --- /dev/null +++ b/app/client/src/utils/hooks/useHorizontalResize.tsx @@ -0,0 +1,140 @@ +import React, { useState, useEffect, MutableRefObject } from "react"; + +import { unFocus } from "utils/helpers"; + +/** + * use horizontal resize + * + * @param ref + * @param onWidthChange + */ +const useHorizontalResize = ( + ref: MutableRefObject, + onWidthChange?: (newWidth: number) => void, + onDragEnd?: () => void, + inverse = false, +) => { + let MIN_WIDTH = 0; + let MAX_WIDTH = 0; + const [resizing, setResizing] = useState(false); + const [position, setPosition] = useState(0); + + // saving min width and max width + useEffect(() => { + if (ref.current) { + MIN_WIDTH = parseInt( + window.getComputedStyle(ref.current).minWidth.replace("px", ""), + ); + MAX_WIDTH = parseInt( + window.getComputedStyle(ref.current).maxWidth.replace("px", ""), + ); + } + }); + + // registering event listeners + useEffect(() => { + document.addEventListener("mouseup", onMouseUp); + document.addEventListener("mousemove", onMouseMove); + document.addEventListener("touchmove", onTouchMove); + + return () => { + document.removeEventListener("mouseup", onMouseUp); + document.removeEventListener("mousemove", onMouseMove); + document.removeEventListener("touchmove", onTouchMove); + }; + }, [resizing, position]); + + /** + * passing the event to touch start on mouse down + * + * @param event + */ + const onMouseDown = (event: React.MouseEvent) => { + const eventWithTouches = Object.assign({}, event, { + touches: [{ clientX: event.clientX, clientY: event.clientY }], + }); + + onTouchStart(eventWithTouches); + }; + + /** + * sets resizing and position on touch start + */ + const onTouchStart = ( + event: + | React.TouchEvent + | (React.MouseEvent & { + touches: { clientX: number; clientY: number }[]; + }), + ) => { + unFocus(document, window); + setPosition(event.touches[0].clientX); + setResizing(true); + document.body.classList.add("cursor-ew-resize"); + }; + + /** + * sets resizing false on mouse up + * also calls onDragFinished if any + */ + const onMouseUp = () => { + if (resizing) { + if (typeof onDragEnd === "function") { + onDragEnd(); + } + + setResizing(false); + document.body.classList.remove("cursor-ew-resize"); + } + }; + + /** + * passing the event to touch move on mouse move + */ + const onMouseMove = (event: MouseEvent) => { + const eventWithTouches = Object.assign({}, event, { + touches: [{ clientX: event.clientX, clientY: event.clientY }], + }); + onTouchMove(eventWithTouches); + }; + + /** + * calculate the new width based on the pixel moved + * + * @param event + */ + const onTouchMove = ( + event: + | TouchEvent + | (MouseEvent & { touches: { clientX: number; clientY: number }[] }), + ) => { + if (resizing) { + unFocus(document, window); + + if (ref.current) { + const width = ref.current.getBoundingClientRect().width; + const current = event.touches[0].clientX; + const positionDelta = position - current; + const widthDelta = inverse ? -positionDelta : positionDelta; + let newWidth = width - widthDelta; + const newPosition = position - positionDelta; + + if (newWidth < MIN_WIDTH) { + newWidth = MIN_WIDTH; + } else if (newWidth > MAX_WIDTH) { + newWidth = MAX_WIDTH; + } else { + setPosition(newPosition); + } + + if (typeof onWidthChange === "function") { + onWidthChange(newWidth); + } + } + } + }; + + return { onTouchStart, onMouseDown, onMouseUp, resizing }; +}; + +export default useHorizontalResize; diff --git a/app/client/src/utils/migrations/TableWidget.ts b/app/client/src/utils/migrations/TableWidget.ts index 5f9f5b8dd1..e63033b30c 100644 --- a/app/client/src/utils/migrations/TableWidget.ts +++ b/app/client/src/utils/migrations/TableWidget.ts @@ -462,3 +462,27 @@ export const migrateTableSanitizeColumnKeys = (currentDSL: DSLWidget) => { return currentDSL; }; + +export const migrateTableWidgetIconButtonVariant = (currentDSL: DSLWidget) => { + currentDSL.children = currentDSL.children?.map((child: WidgetProps) => { + if (child.type === "TABLE_WIDGET") { + const primaryColumns = child.primaryColumns as Record< + string, + ColumnProperties + >; + Object.keys(primaryColumns).forEach((accessor: string) => { + const primaryColumn = primaryColumns[accessor]; + + if (primaryColumn.columnType === "iconButton") { + if (!("buttonVariant" in primaryColumn)) { + primaryColumn.buttonVariant = "TERTIARY"; + } + } + }); + } else if (child.children && child.children.length > 0) { + child = migrateTableWidgetIconButtonVariant(child); + } + return child; + }); + return currentDSL; +}; diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index c69f702249..8ad7b414a1 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -71,7 +71,7 @@ class AudioRecorderWidget extends BaseWidget< ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the recording starts", diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index 86037d3d3a..11ab7cbe2c 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -67,7 +67,7 @@ class AudioWidget extends BaseWidget { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the audio is played", diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 6a91b33989..4837396c6a 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -9,6 +9,7 @@ import { ButtonBoxShadow, ButtonVariant, ButtonBorderRadiusTypes, + ButtonVariantTypes, } from "components/constants"; import ButtonGroupComponent from "../component"; @@ -40,30 +41,6 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, }, - { - propertyName: "buttonVariant", - label: "Button Variant", - controlType: "DROP_DOWN", - helpText: "Sets the variant of the icon button", - options: [ - { - label: "Primary", - value: "PRIMARY", - }, - { - label: "Secondary", - value: "SECONDARY", - }, - { - label: "Tertiary", - value: "TERTIARY", - }, - ], - isJSConvertible: true, - isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, { helpText: "Controls the visibility of the widget", propertyName: "isVisible", @@ -376,6 +353,30 @@ class ButtonGroupWidget extends BaseWidget< { sectionName: "Styles", children: [ + { + propertyName: "buttonVariant", + label: "Button Variant", + controlType: "DROP_DOWN", + helpText: "Sets the variant of the button", + options: [ + { + label: "Primary", + value: ButtonVariantTypes.PRIMARY, + }, + { + label: "Secondary", + value: ButtonVariantTypes.SECONDARY, + }, + { + label: "Tertiary", + value: ButtonVariantTypes.TERTIARY, + }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, { propertyName: "borderRadius", label: "Border Radius", diff --git a/app/client/src/widgets/ButtonWidget/component/index.tsx b/app/client/src/widgets/ButtonWidget/component/index.tsx index 995c4164e9..9cb57e4dd6 100644 --- a/app/client/src/widgets/ButtonWidget/component/index.tsx +++ b/app/client/src/widgets/ButtonWidget/component/index.tsx @@ -160,7 +160,6 @@ const StyledButton = styled((props) => ( } `} - border-radius: ${({ borderRadius }) => borderRadius === ButtonBorderRadiusTypes.ROUNDED ? "5px" : 0}; diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 5b116e2a50..96e9f1e7d5 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -104,7 +104,7 @@ class ButtonWidget extends BaseWidget { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the button is clicked", diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index f04d1f5819..2a7460264d 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -295,7 +295,7 @@ export default [ ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the chart data point is clicked", diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index cd97fa1b6b..1622180a48 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -166,7 +166,7 @@ class CheckboxGroupWidget extends BaseWidget< ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the check state is changed", diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 1385637f7e..51faa7c9bb 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -84,7 +84,7 @@ class CheckboxWidget extends BaseWidget { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { helpText: "Triggers an action when the check state is changed", diff --git a/app/client/src/widgets/DatePickerWidget/widget/index.tsx b/app/client/src/widgets/DatePickerWidget/widget/index.tsx index 0f38289c27..5d4ea10922 100644 --- a/app/client/src/widgets/DatePickerWidget/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget/widget/index.tsx @@ -284,7 +284,7 @@ class DatePickerWidget extends BaseWidget { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { propertyName: "onDateSelected", diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 6060a11771..c4a7a8ae63 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -198,7 +198,7 @@ class DatePickerWidget extends BaseWidget { ], }, { - sectionName: "Actions", + sectionName: "Events", children: [ { propertyName: "onDateSelected", diff --git a/app/client/src/widgets/DividerWidget/widget/index.test.tsx b/app/client/src/widgets/DividerWidget/widget/index.test.tsx index f47c547d34..f17732b277 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.test.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.test.tsx @@ -26,6 +26,9 @@ describe("", () => { comments: { dragPointerOffset: null, }, + editor: { + isPreviewMode: false, + }, }, entities: { canvasWidgets: {}, app: { mode: "canvas" } }, }; diff --git a/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx new file mode 100644 index 0000000000..196fcd0d0a --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx @@ -0,0 +1,53 @@ +import React, { useEffect, useState } from "react"; +import mammoth from "mammoth"; +import styled from "styled-components"; +import Interweave from "interweave"; + +const StyledViewer = styled.div` + width: 100%; + height: 100%; + background: #fff; + overflow: auto; +`; +export default function DocViewer(props: { blob?: Blob }) { + const [state, setState] = useState({ isLoading: false, isError: false }); + const [htmlContent, setHtmlContent] = useState(""); + // when DocViewer gets new Blob of uploaded file convert it to html for preview + useEffect(() => { + setState({ isLoading: true, isError: false }); + setHtmlContent(""); + props.blob + ?.arrayBuffer() + .then((buffer) => { + mammoth + .convertToHtml( + { arrayBuffer: buffer }, + { includeEmbeddedStyleMap: true, includeDefaultStyleMap: true }, + ) + .then((result) => { + setState({ isLoading: false, isError: false }); + setHtmlContent(result.value); + }) + .catch(() => { + setHtmlContent(""); + setState({ isLoading: false, isError: true }); + }); + }) + .catch(() => { + setState({ isLoading: false, isError: false }); + setHtmlContent(""); + }); + }, [props.blob]); + return ( + + + {state.isLoading ? ( +

Loading...
+ ) : state.isError ? ( +
Failed to read docx content
+ ) : ( +
+ )} + + ); +} diff --git a/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx new file mode 100644 index 0000000000..ab034f2edd --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx @@ -0,0 +1,204 @@ +import React, { useEffect, useState, useRef, useCallback } from "react"; +import styled from "styled-components"; +import Excel from "exceljs-lightweight"; +import { useTable, Column } from "react-table"; +import _ from "lodash"; + +const StyledViewer = styled.div` + width: 100%; + height: 100%; + background: #fff; + overflow: auto; + + table { + border: 1px solid #b0cbef; + border-width: 1px 0 0 1px; + border-spacing: 0; + border-collapse: collapse; + padding: 10px; + + th { + font-weight: 700; + font-size: 14px; + border: 1px solid #9eb6ce; + border-width: 0 1px 1px 0; + height: 17px; + line-height: 17px; + text-align: center; + background: #9eb6ce4d; + } + + td { + background-color: #fff; + padding: 0 4px 0 2px; + border: 1px solid #d0d7e5; + border-width: 0 1px 1px 0; + } + } +`; + +const chars = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", +]; + +// get excel column name from index, e.g. A,B,...,AA,AB +const numberToExcelHeader = (index: number): string => { + index -= 1; + const quotient = Math.floor(index / 26); + if (quotient > 0) { + return numberToExcelHeader(quotient) + chars[index % 26]; + } + return chars[index % 26]; +}; + +type sheetsDataType = { + name: string; + id: number; +}; + +export default function XlsxViewer(props: { blob?: Blob }) { + const [sheets, setSheets] = useState([] as sheetsDataType[]); + const [tableData, setTableData] = useState([]); + const [headerData, setHeaderData] = useState([] as Column[]); + const workbook = useRef(new Excel.Workbook()); + + useEffect(() => { + props.blob?.arrayBuffer().then((buffer) => { + // read excel + workbook.current.xlsx.load(buffer).then(() => { + const newSheets = [] as any; + // get all sheets from excel + workbook.current.eachSheet((sheet, id) => { + newSheets.push({ name: sheet.name, id }); + }); + setSheets(newSheets); + // get 1st sheet data + getSheetData(1); + }); + }); + }, [props.blob]); + + // get provided sheet data, read all row and columns + const getSheetData = useCallback((sheetId: number) => { + const worksheet = workbook.current.getWorksheet(sheetId); + // collect all row data + const data = [] as any; + worksheet.eachRow({ includeEmpty: true }, (row) => { + const currRow = {} as any; + // read value of each cell of current row + row.eachCell((cell) => { + // value can be merged value | Date | formula result | string | number + let value: any; + if (cell.isMerged) { + value = _.get(cell, "_mergeCount") ? cell.value : ""; + } else { + value = cell.value; + } + if (_.isDate(value)) { + value = value.toDateString(); + } else if (_.isObject(value) && _.has(value, "result")) { + value = _.get(value, "result", ""); + } + currRow[String(numberToExcelHeader(Number(cell.col)))] = value; + }); + data.push(currRow); + }); + setTableData(data); + if (data.length) { + // create header letters based on columnCount + const newHeader = []; + for (let index = 1; index <= worksheet.columnCount; index++) { + const currHeader = numberToExcelHeader(index); + newHeader.push({ + Header: currHeader, + accessor: currHeader, + }); + } + setHeaderData(newHeader); + } else { + setHeaderData([]); + } + }, []); + + // when user click on another sheet, re-generate data + const updateSheet = useCallback( + (sheetId) => () => { + getSheetData(sheetId); + }, + [], + ); + + const { + getTableBodyProps, + getTableProps, + headerGroups, + prepareRow, + rows, + } = useTable({ columns: headerData, data: tableData }); + + return ( + +
+ {sheets.map((sheet) => ( + + ))} +
+
+ + {headerGroups.map((headerGroup, hgInd) => ( + + {headerGroup.headers.map((column, colId) => ( + + ))} + + ))} + + + {rows.map((row, rInd) => { + prepareRow(row); + return ( + + {row.cells.map((cell, ind) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render("Header")} +
+ {cell.render("Cell")} +
+ + ); +} diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx new file mode 100644 index 0000000000..a6a2c4e402 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx @@ -0,0 +1,68 @@ +import { getDocViewerConfigs } from "widgets/DocumentViewerWidget/component"; +import { Renderers } from "../constants"; + +describe("validate document viewer url", () => { + it("validate correct config should return for extension based urls", () => { + const input = [ + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt", + ]; + + const expected = [ + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx", + viewer: "office", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt", + viewer: "url", + errorMessage: "Current file type is not supported", + renderer: Renderers.ERROR, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf", + viewer: "url", + errorMessage: "Current file type is not supported", + renderer: Renderers.ERROR, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf", + viewer: "url", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt", + viewer: "url", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + ]; + + for (let index = 0; index < input.length; index++) { + const result = getDocViewerConfigs(input[index]); + expect(result).toStrictEqual(expected[index]); + } + }); + + it("validate errorMessage should return for empty url", () => { + const input = ""; + const result = getDocViewerConfigs(input); + expect(result).toStrictEqual({ + url: "", + viewer: "url", + errorMessage: "No document url provided for viewer", + renderer: Renderers.ERROR, + }); + }); +}); diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx new file mode 100644 index 0000000000..a923f5a84f --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx @@ -0,0 +1,220 @@ +import React, { Suspense, lazy } from "react"; +import styled from "styled-components"; +import { DocumentViewer } from "react-documents"; +import { includes, replace, split, get } from "lodash"; +import { + SUPPORTED_EXTENSIONS, + Renderers, + Renderer, + ViewerType, +} from "../constants"; +import { retryPromise } from "utils/AppsmithUtils"; +import Skeleton from "components/utils/Skeleton"; + +const DocViewer = lazy(() => retryPromise(() => import("./DocViewer"))); +const XlsxViewer = lazy(() => retryPromise(() => import("./XlsxViewer"))); + +const ErrorWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + background: #fff; +`; + +const checkUrlExtension = (docUrl: string) => { + // Remove everything to the last slash in URL + let url = docUrl.substr(1 + docUrl.lastIndexOf("/")); + // Break URL at ? and take first part (file name, extension) + url = url.split("?")[0]; + // Sometimes URL doesn't have ? but #, so we should aslo do the same for # + url = url.split("#")[0]; + // Now we have only filename and extension + const chunkList = url.split("."); + if (chunkList.length > 1) { + const ext = chunkList[chunkList.length - 1]; + // check extension is valid or not + const validExtension = SUPPORTED_EXTENSIONS.includes(ext); + return { + hasExtension: true, + validExtension: validExtension, + extension: ext, + }; + } else { + // might be preview url + return { hasExtension: false }; + } +}; + +const getBlob = (docUrl: string) => { + // Split into two parts + const parts = docUrl.split(";base64,"); + // Hold the content type + const mimeType = parts[0].split(":")[1]; + + try { + // Decode Base64 string + const decodedData = window.atob(parts[1]); + // Create UNIT8ARRAY of size same as row data length + const uInt8Array = new Uint8Array(decodedData.length); + // Insert all character code into uInt8Array + for (let i = 0; i < decodedData.length; ++i) { + uInt8Array[i] = decodedData.charCodeAt(i); + } + // Return BLOB image after conversion + const blob = new Blob([uInt8Array], { type: mimeType }); + return blob; + } catch (error) { + return; + } +}; + +// get extension from base64 +const getFileExtensionFromBase64 = (docUrl: string) => { + let extension = ""; + const fileType = docUrl.split(";")[0].split("/")[1]; + + switch (fileType) { + case "vnd.openxmlformats-officedocument.wordprocessingml.document": + extension = "docx"; + break; + case "vnd.openxmlformats-officedocument.spreadsheetml.sheet": + extension = "xlsx"; + break; + case "plain": + extension = "txt"; + break; + case "pdf": + extension = "pdf"; + break; + default: + break; + } + + return extension; +}; + +interface ConfigResponse { + url: string; + blob?: Blob; + viewer: ViewerType; + renderer: Renderer; + errorMessage?: string; +} + +export const getDocViewerConfigs = (docUrl: string): ConfigResponse => { + /** + * From user provided document url decide on which viewer to use + * handle different cases for url hosts + * Case 1: Dropbox + * if user exported download url than change that to raw url + * Case 2: Onedrive + * change url to embeded url to show in viewer + * Case 3: + * All other urls will be either Google Preview or urls with file extentions ( e.x. from aws s3 ) + * need to verify urls with extention and use default viewer "url viewer" for them + */ + let viewer = "url" as ViewerType; + let url = docUrl; + let blob; + let errorMessage = !!docUrl ? "" : "No document url provided for viewer"; + let renderer: Renderer = errorMessage + ? Renderers.ERROR + : Renderers.DOCUMENT_VIEWER; + + if (docUrl && includes(docUrl, "base64")) { + const extension = getFileExtensionFromBase64(docUrl); + const isValidExtension = SUPPORTED_EXTENSIONS.includes(extension); + if (isValidExtension) { + blob = getBlob(docUrl); + if (blob) { + if (extension === "docx") { + renderer = Renderers.DOCX_VIEWER; + } else if (extension === "xlsx") { + renderer = Renderers.XLSX_VIEWER; + } + } else { + errorMessage = "invalid base64 data"; + renderer = Renderers.ERROR; + } + } else { + errorMessage = "Current file type is not supported " + extension; + renderer = Renderers.ERROR; + } + return { blob, url, viewer, errorMessage, renderer }; + } + + // handled dropbox url + if (includes(url, "dropbox.com")) { + if (includes(url, "?dl=0") || !includes(url, "?raw=1")) { + url = replace(url, "?dl=0", ""); + url = url + "?raw=1"; + } + return { url, viewer, renderer }; + } + // handled onedrive url + if (includes(url, "onedrive.live.com")) { + const onedriveUrl = new URL(url); + const onedriveUrlParamList = split(onedriveUrl.search, /[?&=]+/); + const cid = get( + onedriveUrlParamList, + onedriveUrlParamList.indexOf("cid") + 1, + ); + const resid = get( + onedriveUrlParamList, + onedriveUrlParamList.indexOf("id") + 1, + ); + + url = `https://onedrive.live.com/embed?cid=${cid}&resid=${resid}&em=2`; + return { url, viewer, renderer }; + } + + // check url extension and if it is supported + const { extension, hasExtension, validExtension } = checkUrlExtension(url); + if (hasExtension) { + if (validExtension) { + if (!(extension === "txt" || extension === "pdf")) { + viewer = "office"; + renderer = Renderers.DOCUMENT_VIEWER; + } + } else { + errorMessage = "Current file type is not supported"; + renderer = Renderers.ERROR; + } + } + + return { url, viewer, errorMessage, renderer }; +}; + +function DocumentViewerComponent(props: DocumentViewerComponentProps) { + const { blob, errorMessage, renderer, url, viewer } = React.useMemo( + () => getDocViewerConfigs(props.docUrl), + [props.docUrl], + ); + switch (renderer) { + case Renderers.ERROR: + return {errorMessage}; + case Renderers.DOCX_VIEWER: + return ( + }> + + + ); + case Renderers.XLSX_VIEWER: + return ( + }> + + + ); + case Renderers.DOCUMENT_VIEWER: + return ; + + default: + return null; + } +} + +export interface DocumentViewerComponentProps { + docUrl: string; +} + +export default DocumentViewerComponent; diff --git a/app/client/src/widgets/DocumentViewerWidget/constants.ts b/app/client/src/widgets/DocumentViewerWidget/constants.ts new file mode 100644 index 0000000000..fc70832c8c --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/constants.ts @@ -0,0 +1,23 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const DOCUMENTVIEWER_WIDGET_CONSTANT = ""; +// txt and pdf handle by viewerType = "url" +// and other types handle by viewerType = "office" +export const SUPPORTED_EXTENSIONS = [ + "txt", + "pdf", + "docx", + "ppt", + "pptx", + "xlsx", +]; + +export const Renderers = { + DOCUMENT_VIEWER: "DOCUMENT_VIEWER", + DOCX_VIEWER: "DOCX_VIEWER", + XLSX_VIEWER: "XLSX_VIEWER", + ERROR: "ERROR", +}; + +export type Renderer = typeof Renderers[keyof typeof Renderers]; + +export type ViewerType = "google" | "office" | "mammoth" | "pdf" | "url"; diff --git a/app/client/src/widgets/DocumentViewerWidget/icon.svg b/app/client/src/widgets/DocumentViewerWidget/icon.svg new file mode 100644 index 0000000000..b48db5cb0a --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/client/src/widgets/DocumentViewerWidget/index.ts b/app/client/src/widgets/DocumentViewerWidget/index.ts new file mode 100644 index 0000000000..a492c6c3cb --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/index.ts @@ -0,0 +1,27 @@ +import Widget from "./widget"; +import IconSVG from "./icon.svg"; +import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; + +export const CONFIG = { + type: Widget.getWidgetType(), + name: "Document Viewer", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + iconSVG: IconSVG, + needsMeta: false, // Defines if this widget adds any meta properties + isCanvas: false, // Defines if this widget has a canvas within in which we can drop other widgets + defaults: { + widgetName: "DocumentViewer", + docUrl: + "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + rows: 10 * GRID_DENSITY_MIGRATION_V1, + columns: 6 * GRID_DENSITY_MIGRATION_V1, + version: 1, + }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + }, +}; + +export default Widget; diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx new file mode 100644 index 0000000000..19aab1c529 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx @@ -0,0 +1,127 @@ +import { documentUrlValidation } from "."; + +describe("validate propertypane input : docUrl", () => { + it("validation for empty or space value", () => { + const input1 = ""; + const expected1 = { + isValid: true, + parsed: "", + messages: [""], + }; + + const result = documentUrlValidation(input1); + expect(result).toStrictEqual(expected1); + + const input2 = "https: //www.example.com"; + const expected2 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result1 = documentUrlValidation(input2); + expect(result1).toStrictEqual(expected2); + + const input3 = "https://www.exam ple.com"; + const expected3 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result2 = documentUrlValidation(input3); + expect(result2).toStrictEqual(expected3); + + const input4 = "https://examplecom"; + const expected4 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result3 = documentUrlValidation(input4); + expect(result3).toStrictEqual(expected4); + + const input6 = "://www.appsmith.com/docs/sample.pdf"; + const expected6 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result5 = documentUrlValidation(input6); + expect(result5).toStrictEqual(expected6); + }); + + it("validation for invalid url or base64 value", () => { + const input1 = "htt"; + const expected1 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result1 = documentUrlValidation(input1); + expect(result1).toStrictEqual(expected1); + + const input2 = "data:application/pdf;base64"; + const expected2 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result2 = documentUrlValidation(input2); + expect(result2).toStrictEqual(expected2); + }); + + it("validation for valid url or base64 value", () => { + const input1 = "https://www.example.com"; + const expected1 = { + isValid: true, + parsed: "https://www.example.com/", + }; + + const result1 = documentUrlValidation(input1); + expect(result1).toStrictEqual(expected1); + + const input2 = + "data:application/pdf;base64,JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyAzMiBUZiggIFlPVVIgVEVYVCBIRVJFICAgKScgRVQKZW5kc3RyZWFtCmVuZG9iago0IDAgb2JqCjw8Ci9UeXBlIC9QYWdlCi9QYXJlbnQgNSAwIFIKL0NvbnRlbnRzIDkgMCBSCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9LaWRzIFs0IDAgUiBdCi9Db3VudCAxCi9UeXBlIC9QYWdlcwovTWVkaWFCb3ggWyAwIDAgMjUwIDUwIF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G"; + const expected2 = { + isValid: true, + parsed: input2, + }; + + const result2 = documentUrlValidation(input2); + expect(result2).toStrictEqual(expected2); + + const input3 = "https:www.appsmith.com/docs/sample.pdf"; + const expected3 = { + isValid: true, + parsed: "https://www.appsmith.com/docs/sample.pdf", + }; + + const result3 = documentUrlValidation(input3); + expect(result3).toStrictEqual(expected3); + + const input4 = "https://www.apsmith.com/docs/sample"; + const expected4 = { + isValid: true, + parsed: "https://www.apsmith.com/docs/sample", + }; + + const result4 = documentUrlValidation(input4); + expect(result4).toStrictEqual(expected4); + + const input5 = + "www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf"; + const expected5 = { + isValid: true, + parsed: + "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + }; + + const result5 = documentUrlValidation(input5); + expect(result5).toStrictEqual(expected5); + }); +}); diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx new file mode 100644 index 0000000000..b296327c30 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -0,0 +1,121 @@ +import React from "react"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import DocumentViewerComponent from "../component"; +import { + ValidationTypes, + ValidationResponse, +} from "constants/WidgetValidation"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; + +export function documentUrlValidation(value: unknown): ValidationResponse { + // applied validations if value exist + if (value) { + const whiteSpaceRegex = /\s/g; + const urlRegex = /(?:https:\/\/|www)?([\da-z.-]+)\.([a-z.]{2,6})[/\w .-]*\/?/; + const base64Regex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i; + if ( + urlRegex.test(value as string) && + !whiteSpaceRegex.test(value as string) + ) { + if ((value as string).startsWith("www")) { + return { + isValid: true, + parsed: "https://" + value, + }; + } + try { + const newUrl = new URL(value as string); + // URL is valid + return { + isValid: true, + parsed: newUrl.href, + }; + } catch (error) { + return { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + } + } else if (base64Regex.test(value as string)) { + // base 64 is valid + return { + isValid: true, + parsed: value, + }; + } else { + // value is not valid URL / Base64 + return { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + } + } + // value is empty here + return { + isValid: true, + parsed: "", + messages: [""], + }; +} + +class DocumentViewerWidget extends BaseWidget< + DocumentViewerWidgetProps, + WidgetState +> { + static getPropertyPaneConfig() { + return [ + { + sectionName: "General", + children: [ + { + helpText: + "Document url for preview. for URL, supported extensions are txt, pdf, docx, ppt, pptx, xlsx. ppt is currently not supported by base64.", + propertyName: "docUrl", + label: "Document Link", + controlType: "INPUT_TEXT", + placeholderText: "URL / Base64", + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.FUNCTION, + params: { + fn: documentUrlValidation, + expected: { + type: "URL / Base64", + example: "https://www.example.com", + autocompleteDataType: AutocompleteDataType.STRING, + }, + }, + }, + }, + { + helpText: "Controls visibility of the widget", + propertyName: "isVisible", + label: "Visible", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + ], + }, + ]; + } + + getPageView() { + return ; + } + + static getWidgetType(): string { + return "DOCUMENT_VIEWER_WIDGET"; + } +} + +export interface DocumentViewerWidgetProps extends WidgetProps { + docUrl: string; +} + +export default DocumentViewerWidget; diff --git a/app/client/src/widgets/DropdownWidget/component/index.tsx b/app/client/src/widgets/DropdownWidget/component/index.tsx index 0374d1dab4..d573031539 100644 --- a/app/client/src/widgets/DropdownWidget/component/index.tsx +++ b/app/client/src/widgets/DropdownWidget/component/index.tsx @@ -13,6 +13,7 @@ import { Colors } from "constants/Colors"; import { TextSize } from "constants/WidgetConstants"; import { StyledLabel, TextLabelWrapper } from "./index.styled"; import Fuse from "fuse.js"; +import { WidgetContainerDiff } from "widgets/WidgetUtils"; import Icon from "components/ads/Icon"; const FUSE_OPTIONS = { @@ -122,9 +123,22 @@ const StyledControlGroup = styled(ControlGroup)` } `; -const DropdownStyles = createGlobalStyle<{ width: number }>` +const DropdownStyles = createGlobalStyle<{ + parentWidth: number; + dropDownWidth: number; + id: string; +}>` +${({ dropDownWidth, id, parentWidth }) => ` + .select-popover-width-${id} { + min-width: ${parentWidth > dropDownWidth ? parentWidth : dropDownWidth}px; + + & .${Classes.INPUT_GROUP} { + width: ${parentWidth > dropDownWidth ? parentWidth : dropDownWidth}px; + } + } +`} .select-popover-wrapper { - width: 100%; + width: auto; box-shadow: 0 6px 20px 0px rgba(0, 0, 0, 0.15) !important; border-radius: 0; background: white; @@ -178,12 +192,12 @@ const DropdownStyles = createGlobalStyle<{ width: number }>` margin-top: -3px; max-width: 100%; max-height: auto; + min-width: 0px !important; } &&&& .${Classes.MENU_ITEM} { min-height: 38px; padding: 9px 12px; color: ${Colors.GREY_8}; - min-width: 180px; &:hover{ background: ${Colors.GREEN_SOLID_LIGHT_HOVER}; } @@ -243,8 +257,8 @@ class DropDownComponent extends React.Component< ]); this.setState({ activeItemIndex }); }; - - render = () => { + render() { + const id = _.uniqueId(); const { compactMode, disabled, @@ -271,7 +285,11 @@ class DropDownComponent extends React.Component< : this.props.placeholder || "-- Select --"; return ( - + {labelText && (