mirror of
https://github.com/n8n-io/n8n.git
synced 2024-08-16 16:40:30 +03:00
feat(editor): Migrate Design System and Editor UI to Vue 3 (#6476)
* feat: remove vue-fragment (no-changelog)
* feat: partial design-system migration
* feat: migrate info-accordion and info-tip components
* feat: migrate several components to vue 3
* feat: migrated several components
* feat: migrate several components
* feat: migrate several components
* feat: migrate several components
* feat: re-exported all design system components
* fix: fix design for popper components
* fix: editor kind of working, lots of issues to fix
* fix: fix several vue 3 migration issues
* fix: replace @change with @update:modelValue in several places
* fix: fix translation linking
* fix: fix inline-edit input
* fix: fix ndv and dialog design
* fix: update parameter input event bindings
* fix: rename deprecated lifecycle methods
* fix: fix json view mapping
* build: update lock file
* fix(editor): revisit last conflict with master and fix issues
* fix(editor): revisit last conflict with master and fix issues
* fix: fix expression editor bug causing code mirror to no longer be reactive
* fix: fix resource locator bug
* fix: fix vue-agile integration
* fix: remove global import for vue-agile
* fix: replace element-plus buttons with n8n-buttons everywhere
* fix(editor): Fix various element-plus styles (#6571)
* fix(editor): Fix various element-plus styles
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* Remove debugging code
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* Address PR comments
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
---------
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* fix(editor): Fix loading in production mode [Vue 3] (#6578)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* fix(editor): First round of e2e tests fixes with Vue 3 (#6579)
* fix(editor): Fix broken smoke and workflow list e2e tests
* ✔️ Fix failing canvas action tests. Updating some selectors used in credentials and workflow tests
* feat: add vue 3 eslint rules and fix issues
* fix: fix tags-dropdown
* fix: fix white-space issues caused by i18n-t
* fix: rename non-generic click events
* fix: fix search in resources list layout
* fix: fix datatable paginator
* fix: fix popper select caret and dropdown size
* fix: add width to action-dropdown
* fix: fix workflow settings icon not being hidden
* fix: refactor newly added code
* fix: fix merge issue
* fix: fix ndv credentials watcher
* fix: fix workflow saving and grabber notch
* fix: fix nodes list panel transition
* fix: fix node title visibility
* fix: fix data unpinning
* fix: fix value access
* fix: show input panel only if trigger panel enabled or not trigger node
* fix: fix tags dropdown and executions status spcing
* fix(editor): Prevent execution list to load back when leaving the route (#6697)
fix(editor): prevent execution list to load back when leaving the route
* fix: fix drawer visibility
* fix: fix expression toggle padding
* fix: fix expressions editor styling
* chore: prepare for testing
* fix: fix styling for el-button without patching
* test: fix unit tests in design-system
* test: fix most unit tests
* fix: remove import cycle.
* fix: fix personalization modal tests
* fix further resource mapper test adjustments
* fix: fix multiple tests and n8n-route attr duplication
* fix: fix source control tets
* fix: fixed remaining unit tests
* fix: fix workflows and credentials e2e tests
* fix: fix localizeNodeNames
* fix: update ndv e2e tests
* fix: fix popper left placement arrow
* fix: fix 5-ndv e2e tests
* fix: fix 6-code-node e2e tests
* fix(editor): Drop click outside directive from NodeCreator (#6716)
* fix(editor): Drop click outside directive from NodeCreator
* fix(editor): make sure mouseup outside is unbound at least before the component is unmounted
* fix: fix 10-settings-log-streaming e2e tests
* fix: fix node redrawing
* fix: fix tooltip buttons styling
* fix: fix varous e2e suites
* fix: fix 15-scheduler-node e2e suite
* fix: fix route watcher
* fix: fixed param name update and credential edit
* feat: update event names
* refactor: Remove deprecated `$data` (#6576)
Co-authored-by: Alex Grozav <alex@grozav.com>
* fix: fix 17-sharing e2e suite
* fix: fix tags dropdown
* fix: fix tags manager
* fix(editor): move :deep selectors to a separate scoped style block
* fix: fix sticky component and inline text edit
* fix: update e2e tests
* fix: remove button override references
* fix(editor): Adjust spacing in templates for Vue 3 (#6744)
* fix(editor): Adjust spacing in templates
* fix: Undo unneeded change
* fix: Undo unneeded change
* fix(editor): Adjust NDV height for Vue 3 (#6742)
fix(editor): Adjust NDV height
* fix(editor): Restore collapsed sidebar items for Vue 3 (#6743)
fix(editor): Restore collapsed sidebar items
* fix: fix linting issues
* fix: fix design-system deps
* fix: post-merge fixes
* fix: update tests
* fix: increase timeout for executionslist tets
* chore: fix linting issue
* fix: fix 14-mapping e2e tests in ci
* fix: re-enable tests
* fix: fix workflow duplication e2e tests after tags update
* fix(editor): Change component prop to be typed
* fix: fix tags dropdown in duplicate wf modal
* fix: fix focus behaviour in tags selector
* fix: fix tag creation
* fix: fix log streaming e2e race condition
* fix(editor): Fix Vue 3 linting issues (#6748)
* fix(editor): Fix Vue 3 linting issues
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* fix MainSidebar linter issues
* revert pnpm lock
* update pnpm lock file
---------
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Alex Grozav <alex@grozav.com>
* fix(editor): Some css fixes for vue3 branch (#6749)
* ✨ Fixing filter button height
* ✨ Update input modal button position
* ✨ Updating tags styling
* ✨ Fix event logging settings spacing
* 👕 Fixing lint errors
* fix: fix linting issues
* Revert to `// eslint-disable-next-line @typescript-eslint/no-misused-promises` disabling of mixins init
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* fix: fix css issue
* fix(editor): Lint fix
* fix(editor): Fix settings initialisation (#6750)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* fix: fix initial settings loading
* fix: replace realClick with click force
* fix: fix randomly failing mapping e2e tests
* fix(editor): Fix menu item event handling
* fix: fix resource filters dropdown events (#6752)
* fix: fix resource filters dropdown events
* fix: remove teleported:false
* fix: fix event selection event naming (#6753)
* fix: removed console.log (#6754)
* fix: rever await nextTick changes
* fix: redo linting changes
* fix(editor): Redraw node connections if adding more than one node to canvas (#6755)
* fix(editor): Redraw node connections if adding more than one node to canvas
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* Update position before connection two nodes
* Lint fix
---------
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Alex Grozav <alex@grozav.com>
* fix(editor): Fix `ResourceMapper` unit tests (#6758)
* ✔️ Fix matching columns test
* ✔️ Fix multiple matching columns test
* ✔️ Removing `skip` from the last test
* fix: Allow pasting a big workflow (#6760)
* fix: pasting a big workflow
* chore: update comment
* refactor: move try/catch to function
* refactor: move try/catch to function
* fix(editor): Fix modal layer width
* fix: fix position changes
* fix: undo it.only
* fix: make undo/redo multiple steps more verbose
* fix: Fix value survey styles (#6764)
* fix: fix value survey styles
* fix: lint
* Revert "fix: lint"
72869c431f
* fix: lint
* fix(editor): Fix collapsed sub menu
* fix: Fix drawer animation (#6767)
fix: drawer animation
* fix(editor): Fix source control buttons (#6769)
* fix(editor): Fix App loading & auth (#6768)
* fix(editor): Fix App loading & auth
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* Await promises
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
* Fix eslint error
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
---------
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
---------
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
Co-authored-by: OlegIvaniv <me@olegivaniv.com>
Co-authored-by: Milorad FIlipović <milorad@n8n.io>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
This commit is contained in:
parent
d050b99fb2
commit
dd6a4c956a
@ -1,4 +1,6 @@
|
||||
import { SettingsLogStreamingPage } from '../pages';
|
||||
import { getVisibleModalOverlay } from '../utils/modal';
|
||||
import { getVisibleDropdown } from '../utils';
|
||||
|
||||
const settingsLogStreamingPage = new SettingsLogStreamingPage();
|
||||
|
||||
@ -19,6 +21,7 @@ describe('Log Streaming Settings', () => {
|
||||
});
|
||||
|
||||
it('should show the add destination modal', () => {
|
||||
cy.enableFeature('logStreaming');
|
||||
cy.visit('/settings/log-streaming');
|
||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
||||
cy.wait(100);
|
||||
@ -27,7 +30,7 @@ describe('Log Streaming Settings', () => {
|
||||
settingsLogStreamingPage.getters.getSelectDestinationButton().should('be.visible');
|
||||
settingsLogStreamingPage.getters.getSelectDestinationButton().should('have.attr', 'disabled');
|
||||
settingsLogStreamingPage.getters
|
||||
.getDestinationModalDialog()
|
||||
.getDestinationModal()
|
||||
.invoke('css', 'width')
|
||||
.then((widthStr) => parseInt((widthStr as unknown as string).replace('px', '')))
|
||||
.should('be.lessThan', 500);
|
||||
@ -36,65 +39,67 @@ describe('Log Streaming Settings', () => {
|
||||
settingsLogStreamingPage.getters
|
||||
.getSelectDestinationButton()
|
||||
.should('not.have.attr', 'disabled');
|
||||
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
|
||||
getVisibleModalOverlay().click(1, 1);
|
||||
settingsLogStreamingPage.getters.getDestinationModal().should('not.exist');
|
||||
});
|
||||
|
||||
it('should create a destination and delete it', () => {
|
||||
cy.enableFeature('logStreaming');
|
||||
cy.visit('/settings/log-streaming');
|
||||
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
|
||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
||||
cy.wait(100);
|
||||
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
|
||||
settingsLogStreamingPage.getters.getSelectDestinationType().click();
|
||||
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
|
||||
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().click()
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().click();
|
||||
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 0');
|
||||
settingsLogStreamingPage.getters
|
||||
.getDestinationNameInput()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Destination 0');
|
||||
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
|
||||
cy.wait(100);
|
||||
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
|
||||
getVisibleModalOverlay().click(1, 1);
|
||||
cy.reload();
|
||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).click();
|
||||
settingsLogStreamingPage.getters.getDestinationDeleteButton().should('be.visible').click();
|
||||
cy.get('.el-message-box').should('be.visible').find('.btn--cancel').click();
|
||||
settingsLogStreamingPage.getters.getDestinationDeleteButton().click();
|
||||
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
|
||||
cy.reload();
|
||||
});
|
||||
|
||||
it('should create a destination and delete it via card actions', () => {
|
||||
cy.enableFeature('logStreaming');
|
||||
cy.visit('/settings/log-streaming');
|
||||
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
|
||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
||||
cy.wait(100);
|
||||
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
|
||||
settingsLogStreamingPage.getters.getSelectDestinationType().click();
|
||||
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
|
||||
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().click()
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 1');
|
||||
settingsLogStreamingPage.getters.getDestinationNameInput().click();
|
||||
settingsLogStreamingPage.getters
|
||||
.getDestinationNameInput()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Destination 1');
|
||||
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.have.attr', 'disabled');
|
||||
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
|
||||
cy.wait(100);
|
||||
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
|
||||
getVisibleModalOverlay().click(1, 1);
|
||||
cy.reload();
|
||||
|
||||
settingsLogStreamingPage.getters
|
||||
.getDestinationCards()
|
||||
.eq(0)
|
||||
.find('.el-dropdown-selfdefine')
|
||||
.click();
|
||||
cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(0).click();
|
||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
|
||||
getVisibleDropdown().find('.el-dropdown-menu__item').eq(0).click();
|
||||
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.exist');
|
||||
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
|
||||
getVisibleModalOverlay().click(1, 1);
|
||||
|
||||
settingsLogStreamingPage.getters
|
||||
.getDestinationCards()
|
||||
.eq(0)
|
||||
.find('.el-dropdown-selfdefine')
|
||||
.click();
|
||||
cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(1).click();
|
||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
|
||||
getVisibleDropdown().find('.el-dropdown-menu__item').eq(1).click();
|
||||
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
|
||||
cy.reload();
|
||||
});
|
||||
});
|
||||
|
@ -119,18 +119,15 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.attr', 'style', 'left: 740px; top: 320px;');
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.attr', 'style', 'left: 640px; top: 220px;');
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.attr', 'style', 'left: 740px; top: 320px;');
|
||||
});
|
||||
|
||||
@ -138,7 +135,10 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.nodeConnections().realHover();
|
||||
cy.get('.connection-actions .delete').filter(':visible').should('be.visible').click();
|
||||
cy.get('.connection-actions .delete')
|
||||
.filter(':visible')
|
||||
.should('be.visible')
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
@ -256,6 +256,9 @@ describe('Undo/Redo', () => {
|
||||
});
|
||||
|
||||
it('should undo/redo multiple steps', () => {
|
||||
const initialPosition = 'left: 420px; top: 220px;';
|
||||
const movedPosition = 'left: 540px; top: 360px;';
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
|
||||
@ -266,8 +269,10 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters.canvasNodes().last().click();
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
// Move first one
|
||||
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition);
|
||||
WorkflowPage.getters.canvasNodes().first().click();
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]);
|
||||
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition);
|
||||
// Delete the set node
|
||||
WorkflowPage.getters.canvasNodeByName(SET_NODE_NAME).click().click();
|
||||
cy.get('body').type('{backspace}');
|
||||
@ -278,10 +283,7 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 3);
|
||||
// Second undo: Should move first node to it's original position
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.attr', 'style', 'left: 420px; top: 220px;');
|
||||
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition);
|
||||
// Third undo: Should enable last node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
@ -291,10 +293,7 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
// Second redo: Should move the first node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.attr', 'style', 'left: 540px; top: 360px;');
|
||||
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition);
|
||||
// Third redo: Should delete the Set node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
|
@ -66,7 +66,6 @@ describe('Canvas Actions', () => {
|
||||
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
||||
});
|
||||
|
||||
|
||||
it('should add a connected node using plus endpoint', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
cy.get('.plus-endpoint').should('be.visible').click();
|
||||
|
@ -107,7 +107,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
|
||||
cy.get('.jtk-connector.success').should('have.length', 4);
|
||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
||||
cy.get('.jtk-connector').should('have.length', 4);
|
||||
});
|
||||
|
||||
|
@ -7,12 +7,10 @@ describe('Data transformation expressions', () => {
|
||||
beforeEach(() => {
|
||||
wf.actions.visit();
|
||||
|
||||
cy.window().then(
|
||||
(win) => {
|
||||
// @ts-ignore
|
||||
win.preventNodeViewBeforeUnload = true;
|
||||
},
|
||||
);
|
||||
cy.window().then((win) => {
|
||||
// @ts-ignore
|
||||
win.preventNodeViewBeforeUnload = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('$json + native string methods', () => {
|
||||
@ -85,7 +83,7 @@ describe('Data transformation expressions', () => {
|
||||
|
||||
ndv.getters.inlineExpressionEditorInput().clear().type(input);
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist')
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist');
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output);
|
||||
});
|
||||
|
||||
@ -100,7 +98,7 @@ describe('Data transformation expressions', () => {
|
||||
|
||||
ndv.getters.inlineExpressionEditorInput().clear().type(input);
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist')
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist');
|
||||
ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output);
|
||||
});
|
||||
});
|
||||
@ -111,7 +109,7 @@ describe('Data transformation expressions', () => {
|
||||
|
||||
const addSet = () => {
|
||||
wf.actions.addNodeToCanvas('Set', true, true);
|
||||
ndv.getters.parameterInput('keepOnlySet').find('div[role=switch]').click(); // shorten output
|
||||
ndv.getters.parameterInput('keepOnlySet').find('.el-switch').click(); // shorten output
|
||||
cy.get('input[placeholder="Add Value"]').click();
|
||||
cy.get('span').contains('String').click();
|
||||
ndv.getters.nthParam(3).contains('Expression').invoke('show').click(); // Values to Set > String > Value
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
} from './../constants';
|
||||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
@ -28,11 +29,7 @@ describe('Data mapping', () => {
|
||||
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
|
||||
|
||||
ndv.getters.nodeParameters().find('input[placeholder*="Add Value"]').click();
|
||||
ndv.getters
|
||||
.nodeParameters()
|
||||
.find('.el-select-dropdown__list li:nth-child(3)')
|
||||
.should('have.text', 'String')
|
||||
.click();
|
||||
getVisibleSelect().find('li:nth-child(3)').should('have.text', 'String').click();
|
||||
ndv.getters
|
||||
.parameterInput('name')
|
||||
.should('have.length', 1)
|
||||
@ -128,7 +125,7 @@ describe('Data mapping', () => {
|
||||
.find('.json-data')
|
||||
.should(
|
||||
'have.text',
|
||||
'[{"input":[{"count":0,"with space":"!!","with.dot":"!!","with"quotes":"!!"}]},{"input":[{"count":1}]}]',
|
||||
'[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]',
|
||||
)
|
||||
.find('span')
|
||||
.contains('"count"')
|
||||
@ -178,6 +175,7 @@ describe('Data mapping', () => {
|
||||
|
||||
it('maps expressions from previous nodes', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_3.json', `My test workflow`);
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.openNode('Set1');
|
||||
|
||||
ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
@ -245,7 +243,8 @@ describe('Data mapping', () => {
|
||||
workflowPage.actions.addNodeToCanvas('Item Lists');
|
||||
workflowPage.actions.openNode('Item Lists');
|
||||
|
||||
ndv.getters.parameterInput('operation').click().find('li').contains('Sort').click();
|
||||
ndv.getters.parameterInput('operation').click();
|
||||
getVisibleSelect().find('li').contains('Sort').click();
|
||||
|
||||
ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click();
|
||||
|
||||
@ -274,6 +273,8 @@ describe('Data mapping', () => {
|
||||
|
||||
ndv.actions.typeIntoParameterInput('value', 'fun');
|
||||
ndv.actions.clearParameterInput('value'); // keep focus on param
|
||||
ndv.actions.dismissMappingTooltip();
|
||||
cy.wait(300);
|
||||
|
||||
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { WorkflowPage, WorkflowsPage, NDV } from '../pages';
|
||||
import { BACKEND_BASE_URL } from '../constants';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowsPage = new WorkflowsPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
@ -24,11 +25,7 @@ describe('Schedule Trigger node', async () => {
|
||||
workflowPage.actions.openNode('Schedule Trigger');
|
||||
|
||||
cy.getByTestId('parameter-input-field').click();
|
||||
cy.getByTestId('parameter-input-field')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains('Seconds')
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains('Seconds').click();
|
||||
cy.getByTestId('parameter-input-secondsInterval').clear().type('1');
|
||||
|
||||
ndv.getters.backToCanvas().click();
|
||||
|
@ -2,6 +2,7 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { cowBase64 } from '../support/binaryTestFiles';
|
||||
import { BACKEND_BASE_URL } from '../constants';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
@ -34,11 +35,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
|
||||
workflowPage.actions.openNode('Webhook');
|
||||
|
||||
cy.getByTestId('parameter-input-httpMethod').click();
|
||||
cy.getByTestId('parameter-input-httpMethod')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains(method)
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains(method).click();
|
||||
cy.getByTestId('parameter-input-path')
|
||||
.find('.parameter-input')
|
||||
.find('input')
|
||||
@ -47,11 +44,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
|
||||
|
||||
if (authentication) {
|
||||
cy.getByTestId('parameter-input-authentication').click();
|
||||
cy.getByTestId('parameter-input-authentication')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains(authentication)
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains(authentication).click();
|
||||
}
|
||||
|
||||
if (responseCode) {
|
||||
@ -64,20 +57,12 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
|
||||
|
||||
if (respondWith) {
|
||||
cy.getByTestId('parameter-input-responseMode').click();
|
||||
cy.getByTestId('parameter-input-responseMode')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains(respondWith)
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains(respondWith).click();
|
||||
}
|
||||
|
||||
if (responseData) {
|
||||
cy.getByTestId('parameter-input-responseData').click();
|
||||
cy.getByTestId('parameter-input-responseData')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains(responseData)
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains(responseData).click();
|
||||
}
|
||||
|
||||
if (executeNow) {
|
||||
@ -136,13 +121,13 @@ describe('Webhook Trigger node', async () => {
|
||||
workflowPage.actions.addNodeToCanvas('Set');
|
||||
workflowPage.actions.openNode('Set');
|
||||
cy.get('.add-option').click();
|
||||
cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click();
|
||||
cy.get('.fixed-collection-parameter')
|
||||
.getByTestId('parameter-input-name')
|
||||
.clear()
|
||||
.type('MyValue');
|
||||
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234');
|
||||
ndv.getters.backToCanvas().click();
|
||||
ndv.getters.backToCanvas().click({ force: true });
|
||||
|
||||
workflowPage.actions.addNodeToCanvas('Respond to Webhook');
|
||||
|
||||
@ -185,13 +170,18 @@ describe('Webhook Trigger node', async () => {
|
||||
workflowPage.actions.addNodeToCanvas('Set');
|
||||
workflowPage.actions.openNode('Set');
|
||||
cy.get('.add-option').click();
|
||||
cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click();
|
||||
cy.get('.fixed-collection-parameter')
|
||||
.getByTestId('parameter-input-name')
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('MyValue');
|
||||
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234');
|
||||
ndv.getters.backToCanvas().click();
|
||||
cy.get('.fixed-collection-parameter')
|
||||
.getByTestId('parameter-input-value')
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('1234');
|
||||
ndv.getters.backToCanvas().click({ force: true });
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
cy.wait(waitForWebhook);
|
||||
@ -216,7 +206,7 @@ describe('Webhook Trigger node', async () => {
|
||||
workflowPage.actions.addNodeToCanvas('Set');
|
||||
workflowPage.actions.openNode('Set');
|
||||
cy.get('.add-option').click();
|
||||
cy.get('.add-option').find('.el-select-dropdown__item').contains('String').click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains('String').click();
|
||||
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('data');
|
||||
cy.get('.fixed-collection-parameter')
|
||||
.getByTestId('parameter-input-value')
|
||||
@ -231,11 +221,7 @@ describe('Webhook Trigger node', async () => {
|
||||
|
||||
workflowPage.actions.openNode('Move Binary Data');
|
||||
cy.getByTestId('parameter-input-mode').click();
|
||||
cy.getByTestId('parameter-input-mode')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains('JSON to Binary')
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains('JSON to Binary').click();
|
||||
ndv.getters.backToCanvas().click();
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
@ -274,7 +260,7 @@ describe('Webhook Trigger node', async () => {
|
||||
});
|
||||
// add credentials
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
|
||||
@ -317,7 +303,7 @@ describe('Webhook Trigger node', async () => {
|
||||
});
|
||||
// add credentials
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
|
||||
|
@ -48,7 +48,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
||||
workflowPage.actions.setWorkflowName('Workflow W1');
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
|
||||
workflowPage.actions.addNodeToCanvas('Notion', true, true);
|
||||
ndv.getters.credentialInput().should('contain', 'Credential C1');
|
||||
ndv.getters.credentialInput().find('input').should('have.value', 'Credential C1');
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.openShareModal();
|
||||
@ -87,16 +87,12 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
||||
workflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
workflowsPage.getters.workflowCard('Workflow W1').click();
|
||||
workflowPage.actions.addNodeToCanvas('Airtable', true, true);
|
||||
ndv.getters.credentialInput().should('contain', 'Credential C2');
|
||||
ndv.getters.credentialInput().find('input').should('have.value', 'Credential C2');
|
||||
ndv.actions.close();
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
workflowPage.actions.openNode('Notion');
|
||||
ndv.getters
|
||||
.credentialInput()
|
||||
.find('input')
|
||||
.should('have.value', 'Credential C1')
|
||||
.should('be.disabled');
|
||||
ndv.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled');
|
||||
ndv.actions.close();
|
||||
});
|
||||
|
||||
@ -116,11 +112,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
||||
workflowsPage.getters.workflowCards().should('have.length', 2);
|
||||
workflowsPage.getters.workflowCard('Workflow W1').click();
|
||||
workflowPage.actions.openNode('Notion');
|
||||
ndv.getters
|
||||
.credentialInput()
|
||||
.find('input')
|
||||
.should('have.value', 'Credential C1')
|
||||
.should('be.disabled');
|
||||
ndv.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled');
|
||||
ndv.actions.close();
|
||||
|
||||
cy.waitForLoad();
|
||||
|
@ -30,7 +30,7 @@ describe('Workflow tags', () => {
|
||||
}
|
||||
cy.contains('Done').click();
|
||||
|
||||
wf.getters.createTagButton().click();
|
||||
wf.getters.tagsDropdown().click();
|
||||
wf.getters.tagsInDropdown().should('have.length', 5);
|
||||
wf.getters.tagPills().should('have.length', 0); // none attached
|
||||
});
|
||||
@ -45,7 +45,7 @@ describe('Workflow tags', () => {
|
||||
});
|
||||
|
||||
cy.contains('Done').click();
|
||||
wf.getters.createTagButton().click();
|
||||
wf.getters.tagsDropdown().click();
|
||||
wf.getters.tagsInDropdown().should('have.length', 0); // none stored
|
||||
wf.getters.tagPills().should('have.length', 0); // none attached
|
||||
});
|
||||
@ -57,7 +57,8 @@ describe('Workflow tags', () => {
|
||||
|
||||
cy.contains('Create a tag').click();
|
||||
cy.getByTestId('tags-table').find('input').type(first).type('{enter}');
|
||||
cy.getByTestId('edit-tag-button').click({ force: true });
|
||||
cy.getByTestId('tags-table').should('contain.text', first);
|
||||
cy.getByTestId('edit-tag-button').eq(-1).click({ force: true });
|
||||
cy.wait(300);
|
||||
cy.getByTestId('tags-table')
|
||||
.find('.el-input--large')
|
||||
@ -65,7 +66,7 @@ describe('Workflow tags', () => {
|
||||
.type(' Updated')
|
||||
.type('{enter}');
|
||||
cy.contains('Done').click();
|
||||
wf.getters.createTagButton().click();
|
||||
wf.getters.tagsDropdown().click();
|
||||
wf.getters.tagsInDropdown().should('have.length', 1); // one stored
|
||||
wf.getters.tagsInDropdown().contains('Updated').should('exist');
|
||||
wf.getters.tagPills().should('have.length', 0); // none attached
|
||||
@ -76,7 +77,7 @@ describe('Workflow tags', () => {
|
||||
wf.actions.addTags(TEST_TAGS);
|
||||
wf.getters.nthTagPill(1).click();
|
||||
wf.getters.tagsDropdown().find('.el-tag__close').first().click();
|
||||
cy.get('body').type('{enter}');
|
||||
cy.get('body').click(0, 0);
|
||||
wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1);
|
||||
});
|
||||
|
||||
@ -84,8 +85,8 @@ describe('Workflow tags', () => {
|
||||
wf.getters.createTagButton().click();
|
||||
wf.actions.addTags(TEST_TAGS);
|
||||
wf.getters.nthTagPill(1).click();
|
||||
wf.getters.tagsDropdown().find('li.selected').first().click();
|
||||
cy.get('body').type('{enter}');
|
||||
wf.getters.tagsInDropdown().filter('.selected').first().click();
|
||||
cy.get('body').click(0, 0);
|
||||
wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1);
|
||||
});
|
||||
});
|
||||
|
@ -58,19 +58,19 @@ describe('User Management', { disableAutoLogin: true }, () => {
|
||||
it('should delete user and their data', () => {
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
|
||||
usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[0].email);
|
||||
usersSettingsPage.getters.deleteDataRadioButton().realClick();
|
||||
usersSettingsPage.getters.deleteDataRadioButton().click();
|
||||
usersSettingsPage.getters.deleteDataInput().type('delete all data');
|
||||
usersSettingsPage.getters.deleteUserButton().realClick();
|
||||
usersSettingsPage.getters.deleteUserButton().click();
|
||||
workflowPage.getters.successToast().should('contain', 'User deleted');
|
||||
});
|
||||
|
||||
it('should delete user and transfer their data', () => {
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
|
||||
usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[1].email);
|
||||
usersSettingsPage.getters.transferDataRadioButton().realClick();
|
||||
usersSettingsPage.getters.userSelectDropDown().realClick();
|
||||
usersSettingsPage.getters.userSelectOptions().first().realClick();
|
||||
usersSettingsPage.getters.deleteUserButton().realClick();
|
||||
usersSettingsPage.getters.transferDataRadioButton().click();
|
||||
usersSettingsPage.getters.userSelectDropDown().click();
|
||||
usersSettingsPage.getters.userSelectOptions().first().click();
|
||||
usersSettingsPage.getters.deleteUserButton().click();
|
||||
workflowPage.getters.successToast().should('contain', 'User deleted');
|
||||
});
|
||||
|
||||
|
@ -4,8 +4,6 @@ import {
|
||||
PIPEDRIVE_NODE_NAME,
|
||||
HTTP_REQUEST_NODE_NAME,
|
||||
NEW_QUERY_AUTH_ACCOUNT_NAME,
|
||||
} from './../constants';
|
||||
import {
|
||||
GMAIL_NODE_NAME,
|
||||
NEW_GOOGLE_ACCOUNT_NAME,
|
||||
NEW_TRELLO_ACCOUNT_NAME,
|
||||
@ -13,6 +11,7 @@ import {
|
||||
TRELLO_NODE_NAME,
|
||||
} from '../constants';
|
||||
import { CredentialsPage, CredentialsModal, WorkflowPage, NDV } from '../pages';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const credentialsPage = new CredentialsPage();
|
||||
const credentialsModal = new CredentialsModal();
|
||||
@ -90,13 +89,16 @@ describe('Credentials', () => {
|
||||
workflowPage.getters.canvasNodes().last().click();
|
||||
cy.get('body').type('{enter}');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
cy.get('.el-message-box').find('button').contains('Close').click();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_GOOGLE_ACCOUNT_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('have.value', NEW_GOOGLE_ACCOUNT_NAME);
|
||||
});
|
||||
|
||||
it('should show multiple credential types in the same dropdown', () => {
|
||||
@ -107,7 +109,7 @@ describe('Credentials', () => {
|
||||
cy.get('body').type('{enter}');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
// Add oAuth credentials
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
|
||||
@ -115,13 +117,14 @@ describe('Credentials', () => {
|
||||
cy.get('.el-message-box').find('button').contains('Close').click();
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
// Add Service account credentials
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().last().click();
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
// Both (+ the 'Create new' option) should be in the dropdown
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').should('have.length.greaterThan', 3);
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
getVisibleSelect().find('li').should('have.length.greaterThan', 2);
|
||||
});
|
||||
|
||||
it('should correctly render required and optional credentials', () => {
|
||||
@ -132,18 +135,18 @@ describe('Credentials', () => {
|
||||
// Select incoming authentication
|
||||
nodeDetailsView.getters.parameterInput('incomingAuthentication').should('exist');
|
||||
nodeDetailsView.getters.parameterInput('incomingAuthentication').click();
|
||||
nodeDetailsView.getters.parameterInput('incomingAuthentication').find('li').first().click();
|
||||
getVisibleSelect().find('li').first().click();
|
||||
// There should be two credential fields
|
||||
workflowPage.getters.nodeCredentialsSelect().should('have.length', 2);
|
||||
|
||||
workflowPage.getters.nodeCredentialsSelect().first().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().first().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
// This one should show auth type selector
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
workflowPage.getters.nodeCredentialsSelect().last().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().last().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
// This one should not show auth type selector
|
||||
credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist');
|
||||
});
|
||||
@ -155,10 +158,13 @@ describe('Credentials', () => {
|
||||
workflowPage.getters.canvasNodes().last().click();
|
||||
cy.get('body').type('{enter}');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist');
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('have.value', NEW_TRELLO_ACCOUNT_NAME);
|
||||
});
|
||||
|
||||
it('should delete credentials from NDV', () => {
|
||||
@ -168,16 +174,22 @@ describe('Credentials', () => {
|
||||
workflowPage.getters.canvasNodes().last().click();
|
||||
cy.get('body').type('{enter}');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_NOTION_ACCOUNT_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('have.value', NEW_NOTION_ACCOUNT_NAME);
|
||||
|
||||
workflowPage.getters.nodeCredentialsEditButton().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.deleteButton().click();
|
||||
cy.get('.el-message-box').find('button').contains('Yes').click();
|
||||
workflowPage.getters.successToast().contains('Credential deleted');
|
||||
workflowPage.getters.nodeCredentialsSelect().should('not.contain', NEW_TRELLO_ACCOUNT_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('not.have.value', NEW_TRELLO_ACCOUNT_NAME);
|
||||
});
|
||||
|
||||
it('should rename credentials from NDV', () => {
|
||||
@ -187,17 +199,18 @@ describe('Credentials', () => {
|
||||
workflowPage.getters.canvasNodes().last().click();
|
||||
cy.get('body').type('{enter}');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME);
|
||||
|
||||
workflowPage.getters.nodeCredentialsEditButton().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.name().click();
|
||||
credentialsModal.actions.renameCredential(NEW_CREDENTIAL_NAME);
|
||||
credentialsModal.getters.saveButton().click();
|
||||
credentialsModal.getters.closeButton().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_CREDENTIAL_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('have.value', NEW_CREDENTIAL_NAME);
|
||||
});
|
||||
|
||||
it('should setup generic authentication for HTTP node', () => {
|
||||
@ -207,20 +220,20 @@ describe('Credentials', () => {
|
||||
workflowPage.getters.canvasNodes().last().click();
|
||||
cy.get('body').type('{enter}');
|
||||
nodeDetailsView.getters.parameterInput('authentication').click();
|
||||
nodeDetailsView.getters.parameterInput('authentication').find('li').should('have.length', 3);
|
||||
nodeDetailsView.getters.parameterInput('authentication').find('li').last().click();
|
||||
getVisibleSelect().find('li').should('have.length', 3);
|
||||
getVisibleSelect().find('li').last().click();
|
||||
nodeDetailsView.getters.parameterInput('genericAuthType').should('exist');
|
||||
nodeDetailsView.getters.parameterInput('genericAuthType').click();
|
||||
nodeDetailsView.getters
|
||||
.parameterInput('genericAuthType')
|
||||
.find('li')
|
||||
.should('have.length.greaterThan', 0);
|
||||
nodeDetailsView.getters.parameterInput('genericAuthType').find('li').last().click();
|
||||
getVisibleSelect().find('li').should('have.length.greaterThan', 0);
|
||||
getVisibleSelect().find('li').last().click();
|
||||
|
||||
workflowPage.getters.nodeCredentialsSelect().should('exist');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.actions.fillCredentialsForm();
|
||||
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_QUERY_AUTH_ACCOUNT_NAME);
|
||||
workflowPage.getters
|
||||
.nodeCredentialsSelect()
|
||||
.find('input')
|
||||
.should('have.value', NEW_QUERY_AUTH_ACCOUNT_NAME);
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import { CredentialsModal, WorkflowPage } from '../pages';
|
||||
import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json';
|
||||
import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json';
|
||||
import CustomCredential from '../fixtures/Custom_credential.json';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const credentialsModal = new CredentialsModal();
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
@ -20,9 +21,13 @@ describe('Community Nodes', () => {
|
||||
req.on('response', (res) => {
|
||||
const nodes = res.body || [];
|
||||
|
||||
nodes.push(CustomNodeFixture, CustomNodeWithN8nCredentialFixture, CustomNodeWithCustomCredentialFixture);
|
||||
nodes.push(
|
||||
CustomNodeFixture,
|
||||
CustomNodeWithN8nCredentialFixture,
|
||||
CustomNodeWithCustomCredentialFixture,
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
cy.intercept('/types/credentials.json', { middleware: true }, (req) => {
|
||||
req.headers['cache-control'] = 'no-cache, no-store';
|
||||
@ -31,8 +36,8 @@ describe('Community Nodes', () => {
|
||||
const credentials = res.body || [];
|
||||
|
||||
credentials.push(CustomCredential);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
workflowPage.actions.visit();
|
||||
});
|
||||
@ -45,7 +50,7 @@ describe('Community Nodes', () => {
|
||||
|
||||
nodeCreatorFeature.getters
|
||||
.getCreatorItem(customNode)
|
||||
.findChildByTestId('node-creator-item-tooltip')
|
||||
.find('.el-tooltip__trigger')
|
||||
.should('exist');
|
||||
nodeCreatorFeature.actions.selectNode(customNode);
|
||||
|
||||
@ -65,16 +70,9 @@ describe('Community Nodes', () => {
|
||||
secondParameter().find('label').contains('Resource').should('exist');
|
||||
secondParameter().find('input.el-input__inner').should('have.value', 'option2');
|
||||
secondParameter().find('.el-select').click();
|
||||
secondParameter().find('.el-select-dropdown__list').should('exist');
|
||||
// Check if all options are rendered and select the fourth one
|
||||
secondParameter().find('.el-select-dropdown__list').children().should('have.length', 4);
|
||||
secondParameter()
|
||||
.find('.el-select-dropdown__list')
|
||||
.children()
|
||||
.eq(3)
|
||||
.contains('option4')
|
||||
.should('exist')
|
||||
.click();
|
||||
getVisibleSelect().find('li').should('have.length', 4);
|
||||
getVisibleSelect().find('li').eq(3).contains('option4').should('exist').click();
|
||||
secondParameter().find('input.el-input__inner').should('have.value', 'option4');
|
||||
});
|
||||
|
||||
|
@ -28,59 +28,36 @@ describe('NDV', () => {
|
||||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
// input to output
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('exist')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
.realHover();
|
||||
ndv.getters.outputTableRow(4)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.inputTableRow(1).realHover();
|
||||
ndv.getters.outputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.inputTableRow(2)
|
||||
.realHover();
|
||||
ndv.getters.outputTableRow(2)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.inputTableRow(3)
|
||||
.realHover();
|
||||
ndv.getters.outputTableRow(6)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.inputTableRow(2).realHover();
|
||||
ndv.getters.outputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.inputTableRow(3).realHover();
|
||||
ndv.getters.outputTableRow(6).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
// output to input
|
||||
ndv.getters.outputTableRow(1)
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(4)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(1).realHover();
|
||||
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(4)
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(1)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(4).realHover();
|
||||
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(2)
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(2)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(6)
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(3)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(2).realHover();
|
||||
ndv.getters.inputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(1)
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(4)
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(6).realHover();
|
||||
ndv.getters.inputTableRow(3).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(1).realHover();
|
||||
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
|
||||
});
|
||||
|
||||
it('maps paired input and output items based on selected input node', () => {
|
||||
@ -92,9 +69,11 @@ describe('NDV', () => {
|
||||
workflowPage.actions.openNode('Set2');
|
||||
|
||||
ndv.getters.inputPanel().contains('6 items').should('exist');
|
||||
ndv.getters.outputRunSelector()
|
||||
ndv.getters
|
||||
.outputRunSelector()
|
||||
.find('input')
|
||||
.should('exist')
|
||||
.should('include.text', '2 of 2 (6 items)');
|
||||
.should('have.value', '2 of 2 (6 items)');
|
||||
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.actions.switchOutputMode('Table');
|
||||
@ -106,7 +85,8 @@ describe('NDV', () => {
|
||||
ndv.actions.selectInputNode('Set1');
|
||||
ndv.getters.backToCanvas().realHover(); // reset to default hover
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '1000')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -119,7 +99,8 @@ describe('NDV', () => {
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.backToCanvas().realHover(); // reset to default hover
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -137,11 +118,13 @@ describe('NDV', () => {
|
||||
workflowPage.actions.executeWorkflow();
|
||||
workflowPage.actions.openNode('Set3');
|
||||
|
||||
ndv.getters.inputRunSelector()
|
||||
ndv.getters
|
||||
.inputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
ndv.getters
|
||||
.outputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
@ -150,23 +133,19 @@ describe('NDV', () => {
|
||||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector().find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.realHover();
|
||||
ndv.getters.outputTableRow(1).should('have.text', '1111').realHover();
|
||||
|
||||
ndv.getters.outputTableRow(3)
|
||||
.should('have.text', '4444')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(3)
|
||||
ndv.getters.outputTableRow(3).should('have.text', '4444').realHover();
|
||||
ndv.getters
|
||||
.inputTableRow(3)
|
||||
.should('have.text', '4444')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -174,18 +153,16 @@ describe('NDV', () => {
|
||||
ndv.actions.changeOutputRunSelector('2 of 2 (6 items)');
|
||||
cy.wait(50);
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
.should('have.text', '1000')
|
||||
.realHover();
|
||||
ndv.getters.outputTableRow(1)
|
||||
ndv.getters.inputTableRow(1).should('have.text', '1000').realHover();
|
||||
ndv.getters
|
||||
.outputTableRow(1)
|
||||
.should('have.text', '1000')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(3)
|
||||
.should('have.text', '2000')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(3)
|
||||
ndv.getters.outputTableRow(3).should('have.text', '2000').realHover();
|
||||
ndv.getters
|
||||
.inputTableRow(3)
|
||||
.should('have.text', '2000')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -200,15 +177,18 @@ describe('NDV', () => {
|
||||
workflowPage.actions.openNode('Set2');
|
||||
|
||||
ndv.getters.inputPanel().contains('6 items').should('exist');
|
||||
ndv.getters.outputRunSelector()
|
||||
ndv.getters
|
||||
.outputRunSelector()
|
||||
.find('input')
|
||||
.should('exist')
|
||||
.should('include.text', '2 of 2 (6 items)');
|
||||
.should('have.value', '2 of 2 (6 items)');
|
||||
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
ndv.getters.backToCanvas().realHover(); // reset to default hover
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -218,28 +198,32 @@ describe('NDV', () => {
|
||||
|
||||
ndv.actions.selectInputNode('Code1');
|
||||
ndv.getters.inputTableRow(1).realHover();
|
||||
ndv.getters.inputTableRow(1)
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '1000')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputTableRow(1)
|
||||
.should('have.text', '1000');
|
||||
ndv.getters.outputTableRow(1).should('have.text', '1000');
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
|
||||
ndv.actions.selectInputNode('Code');
|
||||
|
||||
ndv.getters.inputTableRow(1).realHover();
|
||||
ndv.getters.inputTableRow(1)
|
||||
.should('have.text', '6666')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', '6666')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
ndv.getters.outputHoveringItem().should('not.exist');
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
|
||||
ndv.actions.selectInputNode('When clicking');
|
||||
|
||||
ndv.getters.inputTableRow(1).realHover();
|
||||
ndv.getters.inputTableRow(1).should('have.text', "This is an item, but it's empty.").realHover();
|
||||
ndv.getters
|
||||
.inputTableRow(1)
|
||||
.should('have.text', "This is an item, but it's empty.")
|
||||
.realHover();
|
||||
ndv.getters.outputHoveringItem().should('have.length', 6);
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
});
|
||||
@ -256,18 +240,16 @@ describe('NDV', () => {
|
||||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
ndv.actions.switchOutputBranch('False Branch (2 items)');
|
||||
ndv.getters.outputTableRow(1)
|
||||
.should('have.text', '8888')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(5)
|
||||
ndv.getters.outputTableRow(1).should('have.text', '8888').realHover();
|
||||
ndv.getters
|
||||
.inputTableRow(5)
|
||||
.should('have.text', '8888')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
|
||||
ndv.getters.outputTableRow(2)
|
||||
.should('have.text', '9999')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(6)
|
||||
ndv.getters.outputTableRow(2).should('have.text', '9999').realHover();
|
||||
ndv.getters
|
||||
.inputTableRow(6)
|
||||
.should('have.text', '9999')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'hovering-item');
|
||||
@ -277,31 +259,21 @@ describe('NDV', () => {
|
||||
workflowPage.actions.openNode('Set5');
|
||||
|
||||
ndv.actions.switchInputBranch('True Branch');
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)')
|
||||
ndv.getters.outputTableRow(1)
|
||||
.should('have.text', '8888')
|
||||
.realHover();
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
|
||||
ndv.getters.outputTableRow(1).should('have.text', '8888').realHover();
|
||||
ndv.getters.inputHoveringItem().should('not.exist');
|
||||
|
||||
ndv.getters.inputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(1).should('have.text', '1111').realHover();
|
||||
ndv.getters.outputHoveringItem().should('not.exist');
|
||||
|
||||
ndv.actions.switchInputBranch('False Branch');
|
||||
ndv.getters.inputTableRow(1)
|
||||
.should('have.text', '8888')
|
||||
.realHover();
|
||||
ndv.getters.inputTableRow(1).should('have.text', '8888').realHover();
|
||||
|
||||
ndv.actions.changeOutputRunSelector('2 of 2 (4 items)')
|
||||
ndv.getters.outputTableRow(1)
|
||||
.should('have.text', '1111')
|
||||
.realHover();
|
||||
ndv.actions.changeOutputRunSelector('2 of 2 (4 items)');
|
||||
ndv.getters.outputTableRow(1).should('have.text', '1111').realHover();
|
||||
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)')
|
||||
ndv.getters.inputTableRow(1)
|
||||
.should('have.text', '8888')
|
||||
.realHover();
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
|
||||
ndv.getters.inputTableRow(1).should('have.text', '8888').realHover();
|
||||
ndv.getters.outputHoveringItem().should('have.text', '8888');
|
||||
// todo there's a bug here need to fix ADO-534
|
||||
// ndv.getters.outputHoveringItem().should('not.exist');
|
||||
|
@ -2,7 +2,13 @@ import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
|
||||
function checkStickiesStyle( top: number, left: number, height: number, width: number, zIndex?: number) {
|
||||
function checkStickiesStyle(
|
||||
top: number,
|
||||
left: number,
|
||||
height: number,
|
||||
width: number,
|
||||
zIndex?: number,
|
||||
) {
|
||||
workflowPage.getters.stickies().should(($el) => {
|
||||
expect($el).to.have.css('top', `${top}px`);
|
||||
expect($el).to.have.css('left', `${left}px`);
|
||||
@ -18,22 +24,23 @@ describe('Canvas Actions', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
|
||||
cy.window().then(
|
||||
(win) => {
|
||||
// @ts-ignore
|
||||
win.preventNodeViewBeforeUnload = true;
|
||||
},
|
||||
);
|
||||
cy.window().then((win) => {
|
||||
// @ts-ignore
|
||||
win.preventNodeViewBeforeUnload = true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('adds sticky to canvas with default text and position', () => {
|
||||
workflowPage.getters.addStickyButton().should('not.be.visible');
|
||||
|
||||
addDefaultSticky()
|
||||
workflowPage.getters.stickies().eq(0)
|
||||
addDefaultSticky();
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
.should('have.text', 'I’m a note\nDouble click to edit me. Guide\n')
|
||||
.find('a').contains('Guide').should('have.attr', 'href');
|
||||
.find('a')
|
||||
.contains('Guide')
|
||||
.should('have.attr', 'href');
|
||||
});
|
||||
|
||||
it('drags sticky around to top left corner', () => {
|
||||
@ -57,18 +64,19 @@ describe('Canvas Actions', () => {
|
||||
|
||||
it('deletes sticky', () => {
|
||||
workflowPage.actions.addSticky();
|
||||
workflowPage.getters.stickies().should('have.length', 1)
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
|
||||
workflowPage.actions.deleteSticky();
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 0)
|
||||
workflowPage.getters.stickies().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('edits sticky and updates content as markdown', () => {
|
||||
workflowPage.actions.addSticky();
|
||||
|
||||
workflowPage.getters.stickies()
|
||||
.should('have.text', 'I’m a note\nDouble click to edit me. Guide\n')
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.should('have.text', 'I’m a note\nDouble click to edit me. Guide\n');
|
||||
|
||||
workflowPage.getters.stickies().dblclick();
|
||||
workflowPage.actions.editSticky('# hello world \n ## text text');
|
||||
@ -159,32 +167,41 @@ describe('Canvas Actions', () => {
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-150, -150]);
|
||||
checkStickiesStyle(124, 256, 316, 384, -121);
|
||||
|
||||
workflowPage.getters.canvasNodes().eq(0)
|
||||
workflowPage.getters
|
||||
.canvasNodes()
|
||||
.eq(0)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('z-index', 'auto');
|
||||
});
|
||||
|
||||
workflowPage.actions.addSticky();
|
||||
workflowPage.getters.stickies().eq(0)
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('z-index', '-121');
|
||||
});
|
||||
workflowPage.getters.stickies().eq(1)
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(1)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('z-index', '-38');
|
||||
});
|
||||
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-200, -200], { index: 1 });
|
||||
workflowPage.getters.stickies().eq(0)
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('z-index', '-121');
|
||||
});
|
||||
|
||||
workflowPage.getters.stickies().eq(1)
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(1)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('z-index', '-158');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -198,15 +215,20 @@ type BoundingBox = {
|
||||
width: number;
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
};
|
||||
|
||||
function dragRightEdge(curr: BoundingBox, move: number) {
|
||||
workflowPage.getters.stickies().first().then(($el) => {
|
||||
const { left, top, height, width } = curr;
|
||||
cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], { abs: true });
|
||||
stickyShouldBePositionedCorrectly({ top, left });
|
||||
stickyShouldHaveCorrectSize([height, width * 1.5 + move]);
|
||||
});
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.first()
|
||||
.then(($el) => {
|
||||
const { left, top, height, width } = curr;
|
||||
cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], {
|
||||
abs: true,
|
||||
});
|
||||
stickyShouldBePositionedCorrectly({ top, left });
|
||||
stickyShouldHaveCorrectSize([height, width * 1.5 + move]);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldHaveOneSticky() {
|
||||
@ -214,17 +236,20 @@ function shouldHaveOneSticky() {
|
||||
}
|
||||
|
||||
function shouldBeInDefaultLocation() {
|
||||
workflowPage.getters.stickies().eq(0).should(($el) => {
|
||||
expect($el).to.have.css('height', '160px');
|
||||
expect($el).to.have.css('width', '240px');
|
||||
})
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('height', '160px');
|
||||
expect($el).to.have.css('width', '240px');
|
||||
});
|
||||
}
|
||||
|
||||
function shouldHaveDefaultSize() {
|
||||
workflowPage.getters.stickies().should(($el) => {
|
||||
expect($el).to.have.css('height', '160px');
|
||||
expect($el).to.have.css('width', '240px');
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function addDefaultSticky() {
|
||||
@ -237,21 +262,19 @@ function addDefaultSticky() {
|
||||
function stickyShouldBePositionedCorrectly(position: Position) {
|
||||
const yOffset = -100;
|
||||
const xOffset = -180;
|
||||
workflowPage.getters.stickies()
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('top', `${yOffset + position.top}px`);
|
||||
expect($el).to.have.css('left', `${xOffset + position.left}px`);
|
||||
});
|
||||
workflowPage.getters.stickies().should(($el) => {
|
||||
expect($el).to.have.css('top', `${yOffset + position.top}px`);
|
||||
expect($el).to.have.css('left', `${xOffset + position.left}px`);
|
||||
});
|
||||
}
|
||||
|
||||
function stickyShouldHaveCorrectSize(size: [number, number]) {
|
||||
const yOffset = 0;
|
||||
const xOffset = 0;
|
||||
workflowPage.getters.stickies()
|
||||
.should(($el) => {
|
||||
expect($el).to.have.css('height', `${yOffset + size[0]}px`);
|
||||
expect($el).to.have.css('width', `${xOffset + size[1]}px`);
|
||||
});
|
||||
workflowPage.getters.stickies().should(($el) => {
|
||||
expect($el).to.have.css('height', `${yOffset + size[0]}px`);
|
||||
expect($el).to.have.css('width', `${xOffset + size[1]}px`);
|
||||
});
|
||||
}
|
||||
|
||||
function moveSticky(target: Position) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
@ -32,7 +33,7 @@ describe('Resource Locator', () => {
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
// Add oAuth credentials
|
||||
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
credentialsModal.getters.credentialsEditModal().should('be.visible');
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { NodeCreator } from '../pages/features/node-creator';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { NDV } from '../pages/ndv';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
@ -85,7 +86,7 @@ describe('Node Creator', () => {
|
||||
nodeCreatorFeature.getters.getCreatorItem(editImageNode).click();
|
||||
nodeCreatorFeature.getters.activeSubcategory().should('have.text', editImageNode);
|
||||
nodeCreatorFeature.getters.getCreatorItem('Crop Image').click();
|
||||
NDVModal.getters.parameterInput('operation').should('contain.text', 'Crop');
|
||||
NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Crop');
|
||||
});
|
||||
|
||||
it('should search through actions and confirm added action', () => {
|
||||
@ -95,9 +96,9 @@ describe('Node Creator', () => {
|
||||
nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'FTP');
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('file');
|
||||
// Navigate to rename action which should be the 4th item
|
||||
nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{uparrow}{rightarrow}');
|
||||
NDVModal.getters.parameterInput('operation').should('contain.text', 'Rename');
|
||||
})
|
||||
nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{rightarrow}');
|
||||
NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Rename');
|
||||
});
|
||||
|
||||
it('should not show actions for single action nodes', () => {
|
||||
const singleActionNodes = [
|
||||
@ -110,19 +111,22 @@ describe('Node Creator', () => {
|
||||
'Spontit',
|
||||
'Vonage',
|
||||
'Send Email',
|
||||
'Toggl Trigger'
|
||||
]
|
||||
const doubleActionNode = 'OpenWeatherMap'
|
||||
'Toggl Trigger',
|
||||
];
|
||||
const doubleActionNode = 'OpenWeatherMap';
|
||||
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
singleActionNodes.forEach((node) => {
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type(node);
|
||||
nodeCreatorFeature.getters.getCreatorItem(node).find('button[class*="panelIcon"]').should('not.exist');
|
||||
})
|
||||
nodeCreatorFeature.getters
|
||||
.getCreatorItem(node)
|
||||
.find('button[class*="panelIcon"]')
|
||||
.should('not.exist');
|
||||
});
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type(doubleActionNode);
|
||||
nodeCreatorFeature.getters.getCreatorItem(doubleActionNode).click();
|
||||
nodeCreatorFeature.getters.creatorItem().should('have.length', 4);
|
||||
})
|
||||
});
|
||||
|
||||
it('should have "Actions" section collapsed when opening actions view from Trigger root view', () => {
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
@ -131,10 +135,19 @@ describe('Node Creator', () => {
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').should('exist');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').should('exist');
|
||||
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed', 'true');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click()
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Triggers')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'false');
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Actions')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'true');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Actions')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'false');
|
||||
});
|
||||
|
||||
it('should have "Triggers" section collapsed when opening actions view from Regular root view', () => {
|
||||
@ -145,17 +158,33 @@ describe('Node Creator', () => {
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
|
||||
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
|
||||
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click()
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').click()
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed');
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Actions')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'false');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Actions')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'true');
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Triggers')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'true');
|
||||
nodeCreatorFeature.getters.getCategoryItem('Triggers').click();
|
||||
nodeCreatorFeature.getters
|
||||
.getCategoryItem('Triggers')
|
||||
.parent()
|
||||
.should('have.attr', 'data-category-collapsed', 'false');
|
||||
});
|
||||
|
||||
it('should show callout and two suggested nodes if node has no trigger actions', () => {
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters
|
||||
.searchBar()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
|
||||
|
||||
cy.getByTestId('actions-panel-no-triggers-callout').should('be.visible');
|
||||
@ -165,28 +194,32 @@ describe('Node Creator', () => {
|
||||
|
||||
it('should show intro callout if user has not made a production execution', () => {
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters
|
||||
.searchBar()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
|
||||
|
||||
cy.getByTestId('actions-panel-activation-callout').should('be.visible');
|
||||
nodeCreatorFeature.getters.activeSubcategory().find('button').click();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear()
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear();
|
||||
|
||||
nodeCreatorFeature.getters.getCreatorItem('On a schedule').click();
|
||||
|
||||
// Setup 1s interval execution
|
||||
cy.getByTestId('parameter-input-field').click();
|
||||
cy.getByTestId('parameter-input-field')
|
||||
.find('.el-select-dropdown')
|
||||
.find('.option-headline')
|
||||
.contains('Seconds')
|
||||
.click();
|
||||
getVisibleSelect().find('.option-headline').contains('Seconds').click();
|
||||
cy.getByTestId('parameter-input-secondsInterval').clear().type('1');
|
||||
|
||||
NDVModal.actions.close();
|
||||
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters
|
||||
.searchBar()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
|
||||
nodeCreatorFeature.getters.getCreatorItem('Get All People').click();
|
||||
NDVModal.actions.close();
|
||||
@ -197,11 +230,15 @@ describe('Node Creator', () => {
|
||||
|
||||
// Wait for schedule 1s execution to mark user as having made a production execution
|
||||
cy.wait(1500);
|
||||
cy.reload()
|
||||
cy.reload();
|
||||
|
||||
// Action callout should not be visible after user has made a production execution
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters
|
||||
.searchBar()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
|
||||
|
||||
cy.getByTestId('actions-panel-activation-callout').should('not.exist');
|
||||
@ -210,7 +247,11 @@ describe('Node Creator', () => {
|
||||
it('should show Trigger and Actions sections during search', () => {
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters
|
||||
.searchBar()
|
||||
.find('input')
|
||||
.clear()
|
||||
.type('Customer Datastore (n8n training)');
|
||||
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Non existent action name');
|
||||
@ -228,7 +269,8 @@ describe('Node Creator', () => {
|
||||
{
|
||||
name: 'canvas add button',
|
||||
handler: () => nodeCreatorFeature.getters.canvasAddButton().click(),
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: 'plus button',
|
||||
handler: () => nodeCreatorFeature.getters.plusButton().click(),
|
||||
},
|
||||
@ -238,10 +280,10 @@ describe('Node Creator', () => {
|
||||
// name: 'tab key',
|
||||
// handler: () => cy.realPress('Tab'),
|
||||
// },
|
||||
]
|
||||
];
|
||||
sourcesWithAppend.forEach((source) => {
|
||||
it(`should append manual trigger when source is ${source.name}`, () => {
|
||||
source.handler()
|
||||
source.handler();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
|
||||
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
|
||||
@ -251,6 +293,7 @@ describe('Node Creator', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO FIX ADDING 2 NODES IN ONE GO
|
||||
it('should not append manual trigger when source is canvas related', () => {
|
||||
nodeCreatorFeature.getters.canvasAddButton().click();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
|
||||
@ -258,8 +301,8 @@ describe('Node Creator', () => {
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
|
||||
nodeCreatorFeature.getters.getCreatorItem('Create a credential').click();
|
||||
NDVModal.actions.close();
|
||||
WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"')
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click()
|
||||
WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"');
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
|
||||
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
|
||||
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
|
||||
@ -267,8 +310,8 @@ describe('Node Creator', () => {
|
||||
NDVModal.actions.close();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize')
|
||||
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -117,7 +117,7 @@ describe('NDV', () => {
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().children().should('have.length', 3);
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.switchOutputMode('Schema');
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
|
||||
schemaKeys.forEach((key) => {
|
||||
@ -130,7 +130,7 @@ describe('NDV', () => {
|
||||
});
|
||||
it('should preserve schema view after execution', () => {
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.switchOutputMode('Schema');
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
});
|
||||
@ -142,7 +142,7 @@ describe('NDV', () => {
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.filter(':contains("objectValue")');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.switchOutputMode('Schema');
|
||||
|
||||
expandedObjectProps.forEach((key) => {
|
||||
ndv.getters
|
||||
@ -173,9 +173,9 @@ describe('NDV', () => {
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputPanel().contains('25 items').should('exist');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.switchOutputMode('Schema');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters.outputDisplayMode().contains('JSON').click();
|
||||
ndv.actions.switchOutputMode('JSON');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
});
|
||||
it('should display large schema', () => {
|
||||
@ -188,7 +188,7 @@ describe('NDV', () => {
|
||||
|
||||
ndv.getters.outputPanel().contains('20 items').should('exist');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.switchOutputMode('Schema');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
|
@ -6,9 +6,11 @@ import {
|
||||
} from '../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||
import { getVisibleDropdown, getVisibleSelect } from '../utils';
|
||||
|
||||
const NEW_WORKFLOW_NAME = 'Something else';
|
||||
const IMPORT_WORKFLOW_URL = 'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json';
|
||||
const IMPORT_WORKFLOW_URL =
|
||||
'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json';
|
||||
const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow';
|
||||
const DUPLICATE_WORKFLOW_TAG = 'Duplicate';
|
||||
|
||||
@ -67,11 +69,11 @@ describe('Workflow Actions', () => {
|
||||
it('should not save workflow if canvas is loading', () => {
|
||||
let interceptCalledCount = 0;
|
||||
|
||||
// There's no way in Cypress to check if intercept was not called
|
||||
// There's no way in Cypress to check if intercept was not called
|
||||
// so we'll count the number of times it was called
|
||||
cy.intercept('PATCH', '/rest/workflows/*', () => {
|
||||
interceptCalledCount++;
|
||||
}).as('saveWorkflow');
|
||||
cy.intercept('PATCH', '/rest/workflows/*', () => {
|
||||
interceptCalledCount++;
|
||||
}).as('saveWorkflow');
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||
@ -84,11 +86,11 @@ describe('Workflow Actions', () => {
|
||||
(req) => {
|
||||
// Delay the response to give time for the save to be triggered
|
||||
req.on('response', async (res) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
res.send();
|
||||
})
|
||||
}
|
||||
)
|
||||
});
|
||||
},
|
||||
);
|
||||
cy.reload();
|
||||
cy.get('.el-loading-mask').should('exist');
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
@ -99,7 +101,7 @@ describe('Workflow Actions', () => {
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.wait('@saveWorkflow');
|
||||
cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(1));
|
||||
})
|
||||
});
|
||||
it('should copy nodes', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
@ -127,7 +129,7 @@ describe('Workflow Actions', () => {
|
||||
cy.get('.el-message-box').should('be.visible');
|
||||
cy.get('.el-message-box').find('input').type(IMPORT_WORKFLOW_URL);
|
||||
cy.get('body').type('{enter}');
|
||||
cy.waitForLoad(false)
|
||||
cy.waitForLoad(false);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
@ -137,7 +139,7 @@ describe('Workflow Actions', () => {
|
||||
WorkflowPage.getters
|
||||
.workflowImportInput()
|
||||
.selectFile('cypress/fixtures/Test_workflow-actions_paste-data.json', { force: true });
|
||||
cy.waitForLoad(false)
|
||||
cy.waitForLoad(false);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
@ -157,57 +159,33 @@ describe('Workflow Actions', () => {
|
||||
WorkflowPage.getters.workflowMenuItemSettings().click();
|
||||
// Change all settings
|
||||
// totalWorkflows + 1 (current workflow) + 1 (no workflow option)
|
||||
WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().find('li').should('have.length', totalWorkflows + 2);
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsErrorWorkflowSelect()
|
||||
WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().click();
|
||||
getVisibleSelect()
|
||||
.find('li')
|
||||
.last()
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').should('exist');
|
||||
WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').eq(1).click({ force: true });
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveFiledExecutionsSelect()
|
||||
.find('li')
|
||||
.should('have.length', 3);
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveFiledExecutionsSelect()
|
||||
.find('li')
|
||||
.last()
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveSuccessExecutionsSelect()
|
||||
.find('li')
|
||||
.should('have.length', 3);
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveSuccessExecutionsSelect()
|
||||
.find('li')
|
||||
.last()
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveManualExecutionsSelect()
|
||||
.find('li')
|
||||
.should('have.length', 3);
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveManualExecutionsSelect()
|
||||
.find('li')
|
||||
.last()
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveExecutionProgressSelect()
|
||||
.find('li')
|
||||
.should('have.length', 3);
|
||||
WorkflowPage.getters
|
||||
.workflowSettingsSaveExecutionProgressSelect()
|
||||
.find('li')
|
||||
.last()
|
||||
.click({ force: true });
|
||||
.should('have.length', totalWorkflows + 2);
|
||||
getVisibleSelect().find('li').last().click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsTimezoneSelect().click();
|
||||
getVisibleSelect().find('li').should('exist');
|
||||
getVisibleSelect().find('li').eq(1).click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsSaveFiledExecutionsSelect().click();
|
||||
getVisibleSelect().find('li').should('have.length', 3);
|
||||
getVisibleSelect().find('li').last().click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsSaveSuccessExecutionsSelect().click();
|
||||
getVisibleSelect().find('li').should('have.length', 3);
|
||||
getVisibleSelect().find('li').last().click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsSaveManualExecutionsSelect().click();
|
||||
getVisibleSelect().find('li').should('have.length', 3);
|
||||
getVisibleSelect().find('li').last().click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsSaveExecutionProgressSelect().click();
|
||||
getVisibleSelect().find('li').should('have.length', 3);
|
||||
getVisibleSelect().find('li').last().click({ force: true });
|
||||
WorkflowPage.getters.workflowSettingsTimeoutWorkflowSwitch().click();
|
||||
WorkflowPage.getters.workflowSettingsTimeoutForm().find('input').first().type('1');
|
||||
// Save settings
|
||||
WorkflowPage.getters.workflowSettingsSaveButton().click();
|
||||
WorkflowPage.getters.workflowSettingsModal().should('not.exist');
|
||||
WorkflowPage.getters.successToast().should('exist');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to delete unsaved workflow', () => {
|
||||
@ -245,7 +223,7 @@ describe('Workflow Actions', () => {
|
||||
.find('.el-select__tags input')
|
||||
.type(DUPLICATE_WORKFLOW_TAG);
|
||||
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}');
|
||||
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}');
|
||||
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{esc}');
|
||||
WorkflowPage.getters
|
||||
.duplicateWorkflowModal()
|
||||
.find('button')
|
||||
|
@ -5,7 +5,7 @@ export class CredentialsPage extends BasePage {
|
||||
getters = {
|
||||
emptyListCreateCredentialButton: () => cy.getByTestId('empty-resources-list').find('button'),
|
||||
createCredentialButton: () => cy.getByTestId('resources-list-add'),
|
||||
searchInput: () => cy.getByTestId('resources-list-search').find('input'),
|
||||
searchInput: () => cy.getByTestId('resources-list-search'),
|
||||
emptyList: () => cy.getByTestId('resources-list-empty'),
|
||||
credentialCards: () => cy.getByTestId('resources-list-item'),
|
||||
credentialCard: (credentialName: string) =>
|
||||
@ -17,8 +17,8 @@ export class CredentialsPage extends BasePage {
|
||||
this.getters.credentialCard(credentialName).findChildByTestId('credential-card-actions'),
|
||||
credentialDeleteButton: () =>
|
||||
cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'),
|
||||
sort: () => cy.getByTestId('resources-list-sort'),
|
||||
sortOption: (label: string) => this.getters.sort().contains(label).first(),
|
||||
sort: () => cy.getByTestId('resources-list-sort').first(),
|
||||
sortOption: (label: string) => cy.getByTestId('resources-list-sort-item').contains(label).first(),
|
||||
filtersTrigger: () => cy.getByTestId('resources-list-filters-trigger'),
|
||||
filtersDropdown: () => cy.getByTestId('resources-list-filters-dropdown'),
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ export class CredentialsModal extends BasePage {
|
||||
credentialsEditModal: () => cy.getByTestId('credential-edit-dialog'),
|
||||
credentialsAuthTypeSelector: () => cy.getByTestId('node-auth-type-selector'),
|
||||
credentialAuthTypeRadioButtons: () =>
|
||||
this.getters.credentialsAuthTypeSelector().find('label[role=radio]'),
|
||||
this.getters.credentialsAuthTypeSelector().find('label.el-radio'),
|
||||
credentialInputs: () => cy.getByTestId('credential-connection-parameter'),
|
||||
menu: () => this.getters.editCredentialModal().get('.menu-container'),
|
||||
menuItem: (name: string) => this.getters.menu().get('.n8n-menu-item').contains(name),
|
||||
@ -42,7 +42,7 @@ export class CredentialsModal extends BasePage {
|
||||
},
|
||||
save: (test = false) => {
|
||||
cy.intercept('POST', '/rest/credentials').as('saveCredential');
|
||||
this.getters.saveButton().click();
|
||||
this.getters.saveButton().click({ force: true });
|
||||
|
||||
cy.wait('@saveCredential');
|
||||
if (test) cy.wait('@testCredential');
|
||||
|
@ -5,15 +5,15 @@ export class MessageBox extends BasePage {
|
||||
modal: () => cy.get('.el-message-box', { withinSubject: null }),
|
||||
header: () => this.getters.modal().find('.el-message-box__title'),
|
||||
content: () => this.getters.modal().find('.el-message-box__content'),
|
||||
confirm: () => this.getters.modal().find('.btn--confirm'),
|
||||
cancel: () => this.getters.modal().find('.btn--cancel'),
|
||||
confirm: () => this.getters.modal().find('.btn--confirm').first(),
|
||||
cancel: () => this.getters.modal().find('.btn--cancel').first(),
|
||||
};
|
||||
actions = {
|
||||
confirm: () => {
|
||||
this.getters.confirm().click();
|
||||
this.getters.confirm().click({ force: true});
|
||||
},
|
||||
cancel: () => {
|
||||
this.getters.cancel().click();
|
||||
this.getters.cancel().click({ force: true});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BasePage } from './base';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
export class NDV extends BasePage {
|
||||
getters = {
|
||||
@ -101,10 +102,11 @@ export class NDV extends BasePage {
|
||||
this.getters.parameterInput(parameterName).type(content);
|
||||
},
|
||||
selectOptionInParameterDropdown: (parameterName: string, content: string) => {
|
||||
this.getters.parameterInput(parameterName).find('.option-headline').contains(content).click();
|
||||
getVisibleSelect().find('.option-headline').contains(content).click();
|
||||
},
|
||||
dismissMappingTooltip: () => {
|
||||
cy.getByTestId('dismiss-mapping-tooltip').click();
|
||||
cy.getByTestId('dismiss-mapping-tooltip').should('not.be.visible');
|
||||
},
|
||||
rename: (newName: string) => {
|
||||
this.getters.nodeNameContainer().click();
|
||||
@ -139,11 +141,11 @@ export class NDV extends BasePage {
|
||||
},
|
||||
changeInputRunSelector: (runName: string) => {
|
||||
this.getters.inputRunSelector().click();
|
||||
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
|
||||
},
|
||||
changeOutputRunSelector: (runName: string) => {
|
||||
this.getters.outputRunSelector().click();
|
||||
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
|
||||
},
|
||||
toggleOutputRunLinking: () => {
|
||||
this.getters.outputRunSelector().find('button').click();
|
||||
@ -159,7 +161,7 @@ export class NDV extends BasePage {
|
||||
},
|
||||
setRLCValue: (paramName: string, value: string) => {
|
||||
this.getters.resourceLocatorModeSelector(paramName).click();
|
||||
this.getters.resourceLocatorModeSelector(paramName).find('li').last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
this.getters.resourceLocatorInput(paramName).type(value);
|
||||
},
|
||||
validateExpressionPreview: (paramName: string, value: string) => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BasePage } from './base';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
export class SettingsLogStreamingPage extends BasePage {
|
||||
url = '/settings/log-streaming';
|
||||
@ -6,11 +7,9 @@ export class SettingsLogStreamingPage extends BasePage {
|
||||
getActionBoxUnlicensed: () => cy.getByTestId('action-box-unlicensed'),
|
||||
getActionBoxLicensed: () => cy.getByTestId('action-box-licensed'),
|
||||
getDestinationModal: () => cy.getByTestId('destination-modal'),
|
||||
getDestinationModalDialog: () => this.getters.getDestinationModal().find('.el-dialog'),
|
||||
getSelectDestinationType: () => cy.getByTestId('select-destination-type'),
|
||||
getDestinationNameInput: () => cy.getByTestId('subtitle-showing-type'),
|
||||
getSelectDestinationTypeItems: () =>
|
||||
this.getters.getSelectDestinationType().find('.el-select-dropdown__item'),
|
||||
getSelectDestinationTypeItems: () => getVisibleSelect().find('.el-select-dropdown__item'),
|
||||
getSelectDestinationButton: () => cy.getByTestId('select-destination-button'),
|
||||
getContactUsButton: () => this.getters.getActionBoxUnlicensed().find('button'),
|
||||
getAddFirstDestinationButton: () => this.getters.getActionBoxLicensed().find('button'),
|
||||
|
@ -11,7 +11,7 @@ export class PersonalSettingsPage extends BasePage {
|
||||
lastNameInput: () => cy.getByTestId('lastName').find('input').first(),
|
||||
emailInputContainer: () => cy.getByTestId('email'),
|
||||
emailInput: () => cy.getByTestId('email').find('input').first(),
|
||||
changePasswordLink: () => cy.getByTestId('change-password-link').find('a').first(),
|
||||
changePasswordLink: () => cy.getByTestId('change-password-link').first(),
|
||||
saveSettingsButton: () => cy.getByTestId('save-settings-button'),
|
||||
};
|
||||
actions = {
|
||||
@ -34,7 +34,10 @@ export class PersonalSettingsPage extends BasePage {
|
||||
},
|
||||
tryToSetWeakPassword: (oldPassword: string, newPassword: string) => {
|
||||
this.actions.updatePassword(oldPassword, newPassword);
|
||||
changePasswordModal.getters.newPasswordInputContainer().find('div[class^="_errorInput"]').should('exist');
|
||||
changePasswordModal.getters
|
||||
.newPasswordInputContainer()
|
||||
.find('div[class^="_errorInput"]')
|
||||
.should('exist');
|
||||
},
|
||||
updateEmail: (newEmail: string) => {
|
||||
this.getters.emailInput().type('{selectall}').type(newEmail).type('{enter}');
|
||||
|
@ -4,8 +4,8 @@ import { WorkflowPage } from './workflow';
|
||||
import { WorkflowsPage } from './workflows';
|
||||
import { BasePage } from './base';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const workflowsPage = new WorkflowsPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
const workflowsPage = new WorkflowsPage();
|
||||
const mainSidebar = new MainSidebar();
|
||||
const settingsSidebar = new SettingsSidebar();
|
||||
|
||||
@ -18,11 +18,15 @@ export class SettingsUsersPage extends BasePage {
|
||||
inviteUsersModalEmailsInput: () => cy.getByTestId('emails').find('input').first(),
|
||||
userListItems: () => cy.get('[data-test-id^="user-list-item"]'),
|
||||
userItem: (email: string) => cy.getByTestId(`user-list-item-${email.toLowerCase()}`),
|
||||
userActionsToggle: (email: string) => this.getters.userItem(email).find('[data-test-id="action-toggle"]'),
|
||||
deleteUserAction: () => cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'),
|
||||
userActionsToggle: (email: string) =>
|
||||
this.getters.userItem(email).find('[data-test-id="action-toggle"]'),
|
||||
deleteUserAction: () =>
|
||||
cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'),
|
||||
confirmDeleteModal: () => cy.getByTestId('deleteUser-modal').last(),
|
||||
transferDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').first(),
|
||||
deleteDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').last(),
|
||||
transferDataRadioButton: () =>
|
||||
this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').first(),
|
||||
deleteDataRadioButton: () =>
|
||||
this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').last(),
|
||||
userSelectDropDown: () => this.getters.confirmDeleteModal().find('.n8n-select'),
|
||||
userSelectOptions: () => cy.get('.el-select-dropdown:visible .el-select-dropdown__item'),
|
||||
deleteUserButton: () => this.getters.confirmDeleteModal().find('button:contains("Delete")'),
|
||||
|
@ -10,7 +10,7 @@ export class VariablesPage extends BasePage {
|
||||
goToUpgrade: () => cy.getByTestId('go-to-upgrade'),
|
||||
actionBox: () => cy.getByTestId('action-box'),
|
||||
emptyResourcesListNewVariableButton: () => this.getters.emptyResourcesList().find('button'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search').find('input'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search'),
|
||||
createVariableButton: () => cy.getByTestId('resources-list-add'),
|
||||
variablesRows: () => cy.getByTestId('variables-row'),
|
||||
variablesEditableRows: () =>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { META_KEY } from '../constants';
|
||||
import { BasePage } from './base';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
export class WorkflowPage extends BasePage {
|
||||
url = '/workflow/new';
|
||||
@ -16,7 +17,7 @@ export class WorkflowPage extends BasePage {
|
||||
nthTagPill: (n: number) =>
|
||||
cy.get(`[data-test-id="workflow-tags-container"] span.tags > span:nth-child(${n})`),
|
||||
tagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'),
|
||||
tagsInDropdown: () => cy.getByTestId('workflow-tags-dropdown').find('li').filter('.tag'),
|
||||
tagsInDropdown: () => getVisibleSelect().find('li').filter('.tag'),
|
||||
createTagButton: () => cy.getByTestId('new-tag-link'),
|
||||
saveButton: () => cy.getByTestId('workflow-save-button'),
|
||||
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
|
||||
@ -37,8 +38,8 @@ export class WorkflowPage extends BasePage {
|
||||
canvasNodePlusEndpointByName: (nodeName: string, index = 0) => {
|
||||
return cy.get(this.getters.getEndpointSelector('plus', nodeName, index));
|
||||
},
|
||||
successToast: () => cy.get('.el-notification .el-icon-success').parent(),
|
||||
errorToast: () => cy.get('.el-notification .el-icon-error'),
|
||||
successToast: () => cy.get('.el-notification .el-notification--success').parent(),
|
||||
errorToast: () => cy.get('.el-notification .el-notification--error'),
|
||||
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
|
||||
workflowMenu: () => cy.getByTestId('workflow-menu'),
|
||||
firstStepButton: () => cy.getByTestId('canvas-add-button'),
|
||||
@ -84,7 +85,8 @@ export class WorkflowPage extends BasePage {
|
||||
duplicateWorkflowModal: () => cy.getByTestId('duplicate-modal'),
|
||||
nodeViewBackground: () => cy.getByTestId('node-view-background'),
|
||||
nodeView: () => cy.getByTestId('node-view'),
|
||||
inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
|
||||
inlineExpressionEditorInput: () =>
|
||||
cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
|
||||
inlineExpressionEditorOutput: () => cy.getByTestId('inline-expression-editor-output'),
|
||||
zoomInButton: () => cy.getByTestId('zoom-in-button'),
|
||||
zoomOutButton: () => cy.getByTestId('zoom-out-button'),
|
||||
@ -92,8 +94,10 @@ export class WorkflowPage extends BasePage {
|
||||
executeWorkflowButton: () => cy.getByTestId('execute-workflow-button'),
|
||||
clearExecutionDataButton: () => cy.getByTestId('clear-execution-data-button'),
|
||||
stopExecutionButton: () => cy.getByTestId('stop-execution-button'),
|
||||
stopExecutionWaitingForWebhookButton: () => cy.getByTestId('stop-execution-waiting-for-webhook-button'),
|
||||
stopExecutionWaitingForWebhookButton: () =>
|
||||
cy.getByTestId('stop-execution-waiting-for-webhook-button'),
|
||||
nodeCredentialsSelect: () => cy.getByTestId('node-credentials-select'),
|
||||
nodeCredentialsCreateOption: () => cy.getByTestId('node-credentials-select-item-new'),
|
||||
nodeCredentialsEditButton: () => cy.getByTestId('credential-edit-button'),
|
||||
nodeCreatorItems: () => cy.getByTestId('item-iterator-item'),
|
||||
ndvParameters: () => cy.getByTestId('parameter-item'),
|
||||
@ -134,17 +138,17 @@ export class WorkflowPage extends BasePage {
|
||||
|
||||
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
|
||||
this.getters.nodeCreatorSearchBar().type('{enter}');
|
||||
cy.wait(500)
|
||||
cy.wait(500);
|
||||
cy.get('body').then((body) => {
|
||||
if(body.find('[data-test-id=node-creator]').length > 0) {
|
||||
if(action) {
|
||||
cy.contains(action).click()
|
||||
if (body.find('[data-test-id=node-creator]').length > 0) {
|
||||
if (action) {
|
||||
cy.contains(action).click();
|
||||
} else {
|
||||
// Select the first action
|
||||
cy.get('[data-keyboard-nav-type="action"]').eq(0).click()
|
||||
cy.get('[data-keyboard-nav-type="action"]').eq(0).click();
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!preventNdvClose) cy.get('body').type('{esc}');
|
||||
},
|
||||
@ -157,7 +161,8 @@ export class WorkflowPage extends BasePage {
|
||||
},
|
||||
openTagManagerModal: () => {
|
||||
this.getters.createTagButton().click();
|
||||
this.getters.tagsDropdown().find('li.manage-tags').first().click();
|
||||
this.getters.tagsDropdown().click();
|
||||
getVisibleSelect().find('li.manage-tags').first().click();
|
||||
},
|
||||
openInlineExpressionEditor: () => {
|
||||
cy.contains('Expression').invoke('show').click();
|
||||
@ -209,7 +214,7 @@ export class WorkflowPage extends BasePage {
|
||||
this.getters.workflowTagsInput().type(tag);
|
||||
this.getters.workflowTagsInput().type('{enter}');
|
||||
});
|
||||
cy.get('body').type('{enter}');
|
||||
cy.get('body').click(0, 0);
|
||||
// For a brief moment the Element UI tag component shows the tags as(+X) string
|
||||
// so we need to wait for it to disappear
|
||||
this.getters.workflowTagsContainer().should('not.contain', `+${tags.length}`);
|
||||
@ -241,7 +246,12 @@ export class WorkflowPage extends BasePage {
|
||||
executeWorkflow: () => {
|
||||
this.getters.executeWorkflowButton().click();
|
||||
},
|
||||
addNodeBetweenNodes: (sourceNodeName: string, targetNodeName: string, newNodeName: string, action?: string) => {
|
||||
addNodeBetweenNodes: (
|
||||
sourceNodeName: string,
|
||||
targetNodeName: string,
|
||||
newNodeName: string,
|
||||
action?: string,
|
||||
) => {
|
||||
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
|
||||
this.getters
|
||||
.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName)
|
||||
@ -268,18 +278,10 @@ export class WorkflowPage extends BasePage {
|
||||
this.getters.addStickyButton().click();
|
||||
},
|
||||
deleteSticky: () => {
|
||||
this.getters.stickies().eq(0)
|
||||
.realHover()
|
||||
.find('[data-test-id="delete-sticky"]')
|
||||
.click();
|
||||
this.getters.stickies().eq(0).realHover().find('[data-test-id="delete-sticky"]').click();
|
||||
},
|
||||
editSticky: (content: string) => {
|
||||
this.getters.stickies()
|
||||
.dblclick()
|
||||
.find('textarea')
|
||||
.clear()
|
||||
.type(content)
|
||||
.type('{esc}');
|
||||
this.getters.stickies().dblclick().find('textarea').clear().type(content).type('{esc}');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ export class WorkflowsPage extends BasePage {
|
||||
getters = {
|
||||
newWorkflowButtonCard: () => cy.getByTestId('new-workflow-card'),
|
||||
newWorkflowTemplateCard: () => cy.getByTestId('new-workflow-template-card'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search').find('input'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search'),
|
||||
createWorkflowButton: () => cy.getByTestId('resources-list-add'),
|
||||
workflowCards: () => cy.getByTestId('resources-list-item'),
|
||||
workflowCard: (workflowName: string) =>
|
||||
|
@ -53,12 +53,12 @@ Cypress.Commands.add('signin', ({ email, password }) => {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('signout', () => {
|
||||
cy.request('POST', '/rest/logout');
|
||||
cy.request('POST', `${BACKEND_BASE_URL}/rest/logout`);
|
||||
cy.getCookie(N8N_AUTH_COOKIE).should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('interceptREST', (method, url) => {
|
||||
cy.intercept(method, `http://localhost:5678/rest${url}`);
|
||||
cy.intercept(method, `${BACKEND_BASE_URL}/rest${url}`);
|
||||
});
|
||||
|
||||
const setFeature = (feature: string, enabled: boolean) =>
|
||||
|
@ -6,6 +6,10 @@ before(() => {
|
||||
owner: INSTANCE_OWNER,
|
||||
members: INSTANCE_MEMBERS,
|
||||
});
|
||||
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
return !err.message.includes('ResizeObserver');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
1
cypress/utils/index.ts
Normal file
1
cypress/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './popper';
|
3
cypress/utils/modal.ts
Normal file
3
cypress/utils/modal.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function getVisibleModalOverlay() {
|
||||
return cy.get('.el-overlay .el-overlay-dialog').filter(':visible');
|
||||
}
|
11
cypress/utils/popper.ts
Normal file
11
cypress/utils/popper.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export function getVisiblePopper() {
|
||||
return cy.get('.el-popper').filter(':visible');
|
||||
}
|
||||
|
||||
export function getVisibleSelect() {
|
||||
return getVisiblePopper().filter('.el-select__popper');
|
||||
}
|
||||
|
||||
export function getVisibleDropdown() {
|
||||
return getVisiblePopper().filter('.el-dropdown__popper');
|
||||
}
|
@ -40,7 +40,6 @@
|
||||
"@ngneat/falso": "^6.1.0",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@vitejs/plugin-vue2": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.28.5",
|
||||
"c8": "^7.12.0",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -64,7 +63,6 @@
|
||||
"typescript": "*",
|
||||
"vite": "^4.0.4",
|
||||
"vitest": "^0.28.5",
|
||||
"vue-template-compiler": "^2.7.14",
|
||||
"vue-tsc": "^1.0.24"
|
||||
},
|
||||
"pnpm": {
|
||||
@ -92,7 +90,6 @@
|
||||
"qqjs>globby": "^11.1.0"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"element-ui@2.15.12": "patches/element-ui@2.15.12.patch",
|
||||
"typedi@0.10.0": "patches/typedi@0.10.0.patch",
|
||||
"@sentry/cli@2.17.0": "patches/@sentry__cli@2.17.0.patch",
|
||||
"pkce-challenge@3.0.0": "patches/pkce-challenge@3.0.0.patch",
|
||||
|
@ -4,7 +4,7 @@
|
||||
module.exports = {
|
||||
plugins: ['vue'],
|
||||
|
||||
extends: ['plugin:vue/essential', '@vue/typescript', './base'],
|
||||
extends: ['plugin:vue/vue3-essential', '@vue/typescript', './base'],
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
@ -37,6 +37,12 @@ module.exports = {
|
||||
'vue/no-unused-components': 'error',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
|
||||
// TODO: fix these
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
|
||||
// TODO: remove these
|
||||
'vue/no-mutating-props': 'warn',
|
||||
'vue/no-side-effects-in-computed-properties': 'warn',
|
||||
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* These icons are only defined for storybook build
|
||||
* Editor icons are defined seperately
|
||||
*/
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
library.add(fas);
|
@ -1,59 +1,42 @@
|
||||
const path = require('path');
|
||||
const { mergeConfig } = require('vite');
|
||||
const { resolve } = require('path');
|
||||
|
||||
/**
|
||||
* @type {import('@storybook/types').StorybookConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
framework: {
|
||||
name: '@storybook/vue-webpack5',
|
||||
options: {},
|
||||
},
|
||||
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.{ts,js}'],
|
||||
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||
addons: [
|
||||
'@storybook/addon-styling',
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
{
|
||||
name: '@storybook/addon-postcss',
|
||||
options: {
|
||||
postcssLoaderOptions: {
|
||||
implementation: require('postcss'),
|
||||
},
|
||||
},
|
||||
},
|
||||
'storybook-addon-themes',
|
||||
// Disabled until this is actually used rather otherwise its a blank tab
|
||||
// '@storybook/addon-interactions',
|
||||
'@storybook/addon-a11y',
|
||||
'storybook-dark-mode',
|
||||
],
|
||||
webpackFinal: async (config) => {
|
||||
config.module.rules.push({
|
||||
test: /\.scss$/,
|
||||
oneOf: [
|
||||
{
|
||||
resourceQuery: /module/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: {
|
||||
localIdentName: '[path][name]__[local]--[hash:base64:5]',
|
||||
},
|
||||
},
|
||||
},
|
||||
'sass-loader',
|
||||
],
|
||||
include: path.resolve(__dirname, '../'),
|
||||
},
|
||||
{
|
||||
use: ['vue-style-loader', 'css-loader', 'sass-loader'],
|
||||
include: path.resolve(__dirname, '../'),
|
||||
},
|
||||
],
|
||||
staticDirs: ['../public'],
|
||||
framework: {
|
||||
name: '@storybook/vue3-vite',
|
||||
options: {},
|
||||
},
|
||||
disableTelemetry: true,
|
||||
async viteFinal(config, { configType }) {
|
||||
// return the customized config
|
||||
return mergeConfig(config, {
|
||||
// customize the Vite config here
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^@n8n-design-system\//,
|
||||
replacement: `${resolve(__dirname, '..')}/src/`,
|
||||
},
|
||||
{
|
||||
find: /^n8n-design-system$/,
|
||||
replacement: `${resolve(__dirname, '..')}/src/main.ts`,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
'@/': path.resolve(__dirname, '../src/'),
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
docs: {
|
||||
autodocs: true,
|
||||
},
|
||||
};
|
||||
|
@ -1,23 +1,24 @@
|
||||
import './font-awesome-icons';
|
||||
import { setup } from '@storybook/vue3';
|
||||
|
||||
import './storybook.scss';
|
||||
|
||||
import ElementUI from 'element-ui';
|
||||
import lang from 'element-ui/lib/locale/lang/en';
|
||||
import locale from 'element-ui/lib/locale';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import ElementPlus from 'element-plus';
|
||||
import lang from 'element-plus/lib/locale/lang/en';
|
||||
|
||||
import { N8nPlugin } from '../src/plugin';
|
||||
|
||||
import Vue from 'vue';
|
||||
setup((app) => {
|
||||
library.add(fas);
|
||||
|
||||
Vue.use(ElementUI);
|
||||
Vue.use(N8nPlugin);
|
||||
app.use(ElementPlus, {
|
||||
locale: lang,
|
||||
});
|
||||
|
||||
locale.use(lang);
|
||||
|
||||
// https://github.com/storybookjs/storybook/issues/6153
|
||||
Vue.prototype.toJSON = function () {
|
||||
return this;
|
||||
};
|
||||
app.use(N8nPlugin);
|
||||
});
|
||||
|
||||
export const parameters = {
|
||||
actions: {
|
||||
|
@ -1,12 +1,17 @@
|
||||
@use './fonts.scss';
|
||||
|
||||
@use '~/src/css/base.scss' with (
|
||||
$font-path: '~element-ui/lib/theme-chalk/fonts'
|
||||
);
|
||||
@use '../src/css/base.scss'; // @TODO CHECK IF NEEDED with (
|
||||
// $font-path: 'element-ui/lib/theme-chalk/fonts'
|
||||
//);
|
||||
|
||||
@use '~/src/css/reset.scss';
|
||||
@use '~/src/css/index.scss';
|
||||
@use '../src/css/reset.scss';
|
||||
@use '../src/css/index.scss';
|
||||
|
||||
.multi-container > * {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#storybook-root > div:not([class]) > *,
|
||||
#storybook-root > * {
|
||||
margin: var(--spacing-5xs);
|
||||
}
|
||||
|
@ -40,38 +40,43 @@
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.9",
|
||||
"@storybook/addon-actions": "^7.0.7",
|
||||
"@storybook/addon-docs": "^7.0.7",
|
||||
"@storybook/addon-essentials": "^7.0.7",
|
||||
"@storybook/addon-links": "^7.0.7",
|
||||
"@storybook/addon-postcss": "^3.0.0-alpha.1",
|
||||
"@storybook/vue": "^7.0.7",
|
||||
"@storybook/vue-webpack5": "^7.0.7",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||
"@storybook/addon-a11y": "^7.0.21",
|
||||
"@storybook/addon-actions": "^7.0.21",
|
||||
"@storybook/addon-docs": "^7.0.21",
|
||||
"@storybook/addon-essentials": "^7.0.21",
|
||||
"@storybook/addon-links": "^7.0.21",
|
||||
"@storybook/addon-postcss": "3.0.0-alpha.1",
|
||||
"@storybook/addon-styling": "^1.3.0",
|
||||
"@storybook/vue3": "^7.0.21",
|
||||
"@storybook/vue3-vite": "^7.0.21",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@testing-library/vue": "^5.8.3",
|
||||
"@testing-library/vue": "^6.6.1",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/markdown-it-emoji": "^2.0.2",
|
||||
"@types/markdown-it-link-attributes": "^3.0.1",
|
||||
"@types/sanitize-html": "^2.8.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"core-js": "^3.27.2",
|
||||
"@types/sanitize-html": "^2.9.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vue/test-utils": "^2.4.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"core-js": "^3.31.0",
|
||||
"jsdom": "21.1.0",
|
||||
"sass": "^1.58.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"storybook": "^7.0.7",
|
||||
"storybook-addon-themes": "^6.1.0"
|
||||
"sass": "^1.63.4",
|
||||
"sass-loader": "^13.3.2",
|
||||
"storybook": "^7.0.21",
|
||||
"storybook-addon-themes": "^6.1.0",
|
||||
"storybook-dark-mode": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"element-ui": "~2.15.12",
|
||||
"element-plus": "^2.3.6",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-emoji": "^2.0.2",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"sanitize-html": "2.10.0",
|
||||
"vue": "^2.7.14",
|
||||
"vue2-boring-avatars": "^0.3.8",
|
||||
"vue": "^3.3.4",
|
||||
"vue-boring-avatars": "^1.3.0",
|
||||
"xss": "^1.0.14"
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
@ -1 +1,5 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { config } from '@vue/test-utils';
|
||||
import { N8nPlugin } from '@/plugin';
|
||||
|
||||
config.global.plugins = [N8nPlugin];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nActionBox from './ActionBox.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/ActionBox',
|
||||
@ -9,8 +9,8 @@ export default {
|
||||
calloutTheme: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['info', 'success', 'warning', 'danger', 'custom'],
|
||||
},
|
||||
options: ['info', 'success', 'warning', 'danger', 'custom'],
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
@ -23,11 +23,12 @@ const methods = {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nActionBox,
|
||||
},
|
||||
template: '<n8n-action-box v-bind="$props" @click="onClick" />',
|
||||
template: '<n8n-action-box v-bind="args" @click="onClick" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
:label="buttonText"
|
||||
:type="buttonType"
|
||||
size="large"
|
||||
@click="$emit('click', $event)"
|
||||
@click="$emit('click:button', $event)"
|
||||
/>
|
||||
<n8n-callout
|
||||
v-if="calloutText"
|
||||
|
@ -11,7 +11,9 @@ describe('N8NActionBox', () => {
|
||||
'Long description that you should know something is the way it is because of how it is. ',
|
||||
buttonText: 'Do something',
|
||||
},
|
||||
stubs: ['n8n-heading', 'n8n-text', 'n8n-button', 'n8n-callout'],
|
||||
global: {
|
||||
stubs: ['n8n-heading', 'n8n-text', 'n8n-button', 'n8n-callout'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,15 +1,15 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`N8NActionBox > should render correctly 1`] = `
|
||||
"<div data-test-id=\\"action-box\\" class=\\"n8n-action-box container\\">
|
||||
<div class=\\"emoji\\"> 😿 </div>
|
||||
"<div class=\\"n8n-action-box container\\" data-test-id=\\"action-box\\">
|
||||
<div class=\\"emoji\\">😿</div>
|
||||
<div class=\\"heading\\">
|
||||
<n8n-heading-stub tag=\\"span\\" size=\\"xlarge\\" align=\\"center\\">Headline you need to know</n8n-heading-stub>
|
||||
<n8n-heading-stub align=\\"center\\" tag=\\"span\\" bold=\\"false\\" size=\\"xlarge\\"></n8n-heading-stub>
|
||||
</div>
|
||||
<div class=\\"description\\">
|
||||
<n8n-text-stub size=\\"medium\\" color=\\"text-base\\" tag=\\"span\\"><span>Long description that you should know something is the way it is because of how it is. </span></n8n-text-stub>
|
||||
<n8n-text-stub color=\\"text-base\\" bold=\\"false\\" size=\\"medium\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
<n8n-button-stub label=\\"Do something\\" type=\\"primary\\" size=\\"large\\"></n8n-button-stub>
|
||||
<!---->
|
||||
<n8n-button-stub label=\\"Do something\\" type=\\"primary\\" size=\\"large\\" loading=\\"false\\" disabled=\\"false\\" outline=\\"false\\" text=\\"false\\" block=\\"false\\" active=\\"false\\" square=\\"false\\"></n8n-button-stub>
|
||||
<!--v-if-->
|
||||
</div>"
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import N8nActionDropdown from './ActionDropdown.vue';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/ActionDropdown',
|
||||
@ -8,8 +8,8 @@ export default {
|
||||
placement: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'],
|
||||
},
|
||||
options: ['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'],
|
||||
},
|
||||
activatorIcon: {
|
||||
control: {
|
||||
@ -19,18 +19,19 @@ export default {
|
||||
trigger: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['click', 'hover'],
|
||||
},
|
||||
options: ['click', 'hover'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nActionDropdown,
|
||||
},
|
||||
template: '<n8n-action-dropdown v-bind="$props" />',
|
||||
template: '<n8n-action-dropdown v-bind="args" />',
|
||||
});
|
||||
|
||||
export const defaultActionDropdown = template.bind({});
|
||||
|
@ -6,7 +6,7 @@
|
||||
@command="onSelect"
|
||||
ref="elementDropdown"
|
||||
>
|
||||
<div :class="$style.activator" @click.prevent @blur="onButtonBlur">
|
||||
<div :class="$style.activator" @click.stop.prevent @blur="onButtonBlur">
|
||||
<n8n-icon :icon="activatorIcon" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
@ -36,11 +36,7 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import {
|
||||
Dropdown as ElDropdown,
|
||||
DropdownMenu as ElDropdownMenu,
|
||||
DropdownItem as ElDropdownItem,
|
||||
} from 'element-ui';
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
|
||||
export interface IActionDropdownItem {
|
||||
@ -108,12 +104,11 @@ export default defineComponent({
|
||||
this.$emit('select', action);
|
||||
},
|
||||
onButtonBlur(event: FocusEvent): void {
|
||||
const elementDropdown = this.$refs.elementDropdown as
|
||||
| (Vue & { hide: () => void })
|
||||
| undefined;
|
||||
const elementDropdown = this.$refs.elementDropdown as InstanceType<ElDropdown>;
|
||||
|
||||
// Hide dropdown when clicking outside of current document
|
||||
if (elementDropdown && event.relatedTarget === null) {
|
||||
elementDropdown.hide();
|
||||
if (elementDropdown?.handleClose && event.relatedTarget === null) {
|
||||
elementDropdown.handleClose();
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -121,6 +116,10 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.userActionsMenu {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.activator {
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-2xs);
|
||||
|
@ -6,6 +6,7 @@ describe('components', () => {
|
||||
it('should render default styling correctly', () => {
|
||||
const wrapper = render(N8nActionDropdown, {
|
||||
props: {
|
||||
teleported: false,
|
||||
items: [
|
||||
{
|
||||
id: 'item1',
|
||||
@ -17,10 +18,13 @@ describe('components', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
stubs: ['n8n-icon', 'el-dropdown', 'el-dropdown-menu', 'el-dropdown-item'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'el-tooltip', 'el-dropdown', 'el-dropdown-menu', 'el-dropdown-item'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render custom styling correctly', () => {
|
||||
const wrapper = render(N8nActionDropdown, {
|
||||
props: {
|
||||
@ -44,7 +48,9 @@ describe('components', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
stubs: ['n8n-icon', 'el-dropdown', 'el-dropdown-menu', 'el-dropdown-item'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'el-dropdown', 'el-dropdown-menu', 'el-dropdown-item'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
@ -2,20 +2,12 @@
|
||||
|
||||
exports[`components > N8nActionDropdown > should render custom styling correctly 1`] = `
|
||||
"<div class=\\"action-dropdown-container actionDropdownContainer\\">
|
||||
<el-dropdown-stub trigger=\\"click\\" size=\\"\\" hideonclick=\\"true\\" placement=\\"bottom\\" visiblearrow=\\"true\\" showtimeout=\\"250\\" hidetimeout=\\"150\\" tabindex=\\"0\\">
|
||||
<div class=\\"activator\\">
|
||||
<n8n-icon-stub icon=\\"ellipsis-v\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
</div>
|
||||
</el-dropdown-stub>
|
||||
<el-dropdown-stub trigger=\\"click\\" effect=\\"light\\" placement=\\"bottom\\" popperoptions=\\"[object Object]\\" size=\\"\\" splitbutton=\\"false\\" hideonclick=\\"true\\" loop=\\"true\\" showtimeout=\\"150\\" hidetimeout=\\"150\\" tabindex=\\"0\\" maxheight=\\"\\" popperclass=\\"\\" disabled=\\"false\\" role=\\"menu\\" teleported=\\"true\\"></el-dropdown-stub>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nActionDropdown > should render default styling correctly 1`] = `
|
||||
"<div class=\\"action-dropdown-container actionDropdownContainer\\">
|
||||
<el-dropdown-stub trigger=\\"click\\" size=\\"\\" hideonclick=\\"true\\" placement=\\"bottom\\" visiblearrow=\\"true\\" showtimeout=\\"250\\" hidetimeout=\\"150\\" tabindex=\\"0\\">
|
||||
<div class=\\"activator\\">
|
||||
<n8n-icon-stub icon=\\"ellipsis-v\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
</div>
|
||||
</el-dropdown-stub>
|
||||
"<div class=\\"action-dropdown-container actionDropdownContainer\\" teleported=\\"false\\">
|
||||
<el-dropdown-stub trigger=\\"click\\" effect=\\"light\\" placement=\\"bottom\\" popperoptions=\\"[object Object]\\" size=\\"\\" splitbutton=\\"false\\" hideonclick=\\"true\\" loop=\\"true\\" showtimeout=\\"150\\" hidetimeout=\\"150\\" tabindex=\\"0\\" maxheight=\\"\\" popperclass=\\"\\" disabled=\\"false\\" role=\\"menu\\" teleported=\\"true\\"></el-dropdown-stub>
|
||||
</div>"
|
||||
`;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nActionToggle from './ActionToggle.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/ActionToggle',
|
||||
@ -25,12 +25,14 @@ const methods = {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nActionToggle,
|
||||
},
|
||||
template:
|
||||
'<div style="height:300px;width:300px;display:flex;align-items:center;justify-content:center"><n8n-action-toggle v-bind="$props" @action="onAction" /></div>',
|
||||
template: `<div style="height:300px; width:300px; display:flex; align-items:center; justify-content:center">
|
||||
<n8n-action-toggle v-bind="args" @action="onAction" />
|
||||
</div>`,
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<span :class="$style.container" data-test-id="action-toggle">
|
||||
<span @click.stop.prevent :class="$style.container" data-test-id="action-toggle">
|
||||
<el-dropdown
|
||||
:placement="placement"
|
||||
:size="size"
|
||||
trigger="click"
|
||||
@click.native.stop
|
||||
@command="onCommand"
|
||||
@visible-change="onVisibleChange"
|
||||
>
|
||||
@ -42,11 +41,7 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import {
|
||||
Dropdown as ElDropdown,
|
||||
DropdownMenu as ElDropdownMenu,
|
||||
DropdownItem as ElDropdownItem,
|
||||
} from 'element-ui';
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
import type { UserAction } from '@/types';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nAlert from './Alert.vue';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
|
||||
@ -18,12 +18,13 @@ export default {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nAlert,
|
||||
},
|
||||
template:
|
||||
'<div style="position: relative; width: 100%; height: 300px;"><n8n-alert v-bind="$props"><template #aside>custom content slot</template></n8n-alert></div>',
|
||||
'<div style="position: relative; width: 100%; height: 300px;"><n8n-alert v-bind="args"><template #aside>custom content slot</template></n8n-alert></div>',
|
||||
});
|
||||
|
||||
export const ContentAsProps = Template.bind({});
|
||||
@ -38,15 +39,16 @@ ContentAsProps.args = {
|
||||
};
|
||||
|
||||
const TemplateForSlots: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nAlert,
|
||||
N8nIcon,
|
||||
},
|
||||
template: `<div style="position: relative; width: 100%; height: 300px;">
|
||||
<n8n-alert v-bind="$props">
|
||||
<n8n-alert v-bind="args">
|
||||
<template #title>Title</template>
|
||||
<template>Description</template>
|
||||
Description
|
||||
<template #aside><button>Button</button></template>
|
||||
<template #icon>
|
||||
<n8n-icon icon="grin-stars" size="xlarge" />
|
||||
|
@ -14,21 +14,20 @@ describe('components', () => {
|
||||
});
|
||||
|
||||
it('should render slots instead of props', () => {
|
||||
const { container } = render(
|
||||
N8nAlert,
|
||||
{
|
||||
props: { showIcon: false },
|
||||
slots: {
|
||||
title: 'Title',
|
||||
default: 'Message',
|
||||
aside: '<button>Click me</button>',
|
||||
icon: '<n8n-icon icon="plus-circle" />',
|
||||
const { container } = render(N8nAlert, {
|
||||
props: { showIcon: false },
|
||||
slots: {
|
||||
title: 'Title',
|
||||
default: 'Message',
|
||||
aside: '<button>Click me</button>',
|
||||
icon: '<n8n-icon icon="plus-circle" />',
|
||||
},
|
||||
global: {
|
||||
components: {
|
||||
'n8n-icon': N8nIcon,
|
||||
},
|
||||
},
|
||||
(localVue) => {
|
||||
localVue.component('n8n-icon', N8nIcon);
|
||||
},
|
||||
);
|
||||
});
|
||||
expect(screen.getByRole('alert')).toBeVisible();
|
||||
expect(screen.getByText('Title')).toBeVisible();
|
||||
expect(screen.getByText('Message')).toBeVisible();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nAvatar from './Avatar.vue';
|
||||
|
||||
export default {
|
||||
@ -13,14 +13,16 @@ export default {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nAvatar,
|
||||
},
|
||||
template: '<n8n-avatar v-bind="$props" />',
|
||||
template: '<n8n-avatar v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Avatar = Template.bind({});
|
||||
Avatar.args = {
|
||||
name: 'Sunny Side',
|
||||
firstName: 'Sunny',
|
||||
lastName: 'Side',
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<span :class="['n8n-avatar', $style.container]" v-on="$listeners">
|
||||
<avatar
|
||||
<span :class="['n8n-avatar', $style.container]" v-bind="$attrs">
|
||||
<Avatar
|
||||
v-if="firstName"
|
||||
:size="getSize(size)"
|
||||
:name="firstName + ' ' + lastName"
|
||||
@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Avatar from 'vue2-boring-avatars';
|
||||
import Avatar from 'vue-boring-avatars';
|
||||
|
||||
const sizes: { [size: string]: number } = {
|
||||
small: 28,
|
||||
@ -28,9 +28,11 @@ export default defineComponent({
|
||||
props: {
|
||||
firstName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
lastName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
@ -47,7 +49,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Avatar, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
||||
Avatar,
|
||||
},
|
||||
computed: {
|
||||
initials() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nBadge from './Badge.vue';
|
||||
|
||||
export default {
|
||||
@ -17,11 +17,12 @@ export default {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nBadge,
|
||||
},
|
||||
template: '<n8n-badge v-bind="$props">Badge</n8n-badge>',
|
||||
template: '<n8n-badge v-bind="args">Badge</n8n-badge>',
|
||||
});
|
||||
|
||||
export const Badge = Template.bind({});
|
||||
|
@ -14,7 +14,9 @@ describe('components', () => {
|
||||
slots: {
|
||||
default: '<n8n-text>Default badge</n8n-text>',
|
||||
},
|
||||
stubs: ['n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-text'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -28,7 +30,9 @@ describe('components', () => {
|
||||
slots: {
|
||||
default: '<n8n-text>Secondary badge</n8n-text>',
|
||||
},
|
||||
stubs: ['n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-text'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -37,7 +41,9 @@ describe('components', () => {
|
||||
slots: {
|
||||
default: '<n8n-text>A Badge</n8n-text>',
|
||||
},
|
||||
stubs: ['n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-text'],
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`components > N8nBadge > props > should render default theme correctly 1`] = `"<span class=\\"n8n-badge default\\"><n8n-text-stub bold=\\"true\\" size=\\"large\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Default badge</n8n-text-stub></n8n-text-stub></span>"`;
|
||||
exports[`components > N8nBadge > props > should render default theme correctly 1`] = `"<span class=\\"n8n-badge default\\"><n8n-text-stub bold=\\"true\\" size=\\"large\\" compact=\\"true\\" tag=\\"span\\"></n8n-text-stub></span>"`;
|
||||
|
||||
exports[`components > N8nBadge > props > should render secondary theme correctly 1`] = `"<span class=\\"n8n-badge secondary\\"><n8n-text-stub size=\\"medium\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Secondary badge</n8n-text-stub></n8n-text-stub></span>"`;
|
||||
exports[`components > N8nBadge > props > should render secondary theme correctly 1`] = `"<span class=\\"n8n-badge secondary\\"><n8n-text-stub bold=\\"false\\" size=\\"medium\\" compact=\\"true\\" tag=\\"span\\"></n8n-text-stub></span>"`;
|
||||
|
||||
exports[`components > N8nBadge > props > should render with default values correctly 1`] = `"<span class=\\"n8n-badge default\\"><n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">A Badge</n8n-text-stub></n8n-text-stub></span>"`;
|
||||
exports[`components > N8nBadge > props > should render with default values correctly 1`] = `"<span class=\\"n8n-badge default\\"><n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"true\\" tag=\\"span\\"></n8n-text-stub></span>"`;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nBlockUi from './BlockUi.vue';
|
||||
|
||||
export default {
|
||||
@ -7,12 +7,13 @@ export default {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nBlockUi,
|
||||
},
|
||||
template:
|
||||
'<div style="position: relative; width: 100%; height: 300px;"><n8n-block-ui v-bind="$props" /></div>',
|
||||
'<div style="position: relative; width: 100%; height: 300px;"><n8n-block-ui v-bind="args" /></div>',
|
||||
});
|
||||
|
||||
export const BlockUi = Template.bind({});
|
||||
|
@ -38,7 +38,7 @@ withDefaults(defineProps<BlockUiProps>(), {
|
||||
.fade-leave-active {
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
81
packages/design-system/src/components/N8nButton/Button.scss
Normal file
81
packages/design-system/src/components/N8nButton/Button.scss
Normal file
@ -0,0 +1,81 @@
|
||||
@import '../../css/mixins/utils';
|
||||
@import '../../css/common/var';
|
||||
|
||||
@mixin n8n-button($override: false) {
|
||||
$important: if($override, !important, '');
|
||||
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
border: var(--border-width-base) $button-border-color var(--border-style-base) unquote($important);
|
||||
color: $button-font-color unquote($important);
|
||||
background-color: $button-background-color unquote($important);
|
||||
font-weight: var(--font-weight-bold) unquote($important);
|
||||
border-radius: $button-border-radius unquote($important);
|
||||
padding: $button-padding-vertical $button-padding-horizontal unquote($important);
|
||||
font-size: $button-font-size unquote($important);
|
||||
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
transition: 0.3s;
|
||||
|
||||
@include utils-user-select(none);
|
||||
|
||||
&:hover {
|
||||
color: $button-hover-color unquote($important);
|
||||
border-color: $button-hover-border-color unquote($important);
|
||||
background-color: $button-hover-background-color unquote($important);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $button-focus-outline-color unquote($important);
|
||||
outline: $focus-outline-width solid $button-focus-outline-color unquote($important);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
color: $button-active-color unquote($important);
|
||||
border-color: $button-active-border-color unquote($important);
|
||||
background-color: $button-active-background-color unquote($important);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
> i {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
span + span {
|
||||
margin-left: var(--spacing-3xs);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin n8n-button-secondary {
|
||||
--button-color: var(--color-primary);
|
||||
--button-border-color: var(--color-primary);
|
||||
--button-background-color: var(--color-background-xlight);
|
||||
|
||||
--button-active-background-color: var(--color-primary-tint-2);
|
||||
--button-active-color: var(--color-primary);
|
||||
--button-active-border-color: var(--color-primary);
|
||||
|
||||
--button-hover-background-color: var(--color-primary-tint-3);
|
||||
--button-hover-color: var(--color-primary);
|
||||
--button-hover-border-color: var(--color-primary);
|
||||
|
||||
--button-focus-outline-color: var(--color-primary-tint-1);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import N8nButton from './Button.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Button',
|
||||
@ -13,8 +13,8 @@ export default {
|
||||
size: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['mini', 'small', 'medium', 'large', 'xlarge'],
|
||||
},
|
||||
options: ['mini', 'small', 'medium', 'large', 'xlarge'],
|
||||
},
|
||||
float: {
|
||||
type: 'select',
|
||||
@ -34,11 +34,12 @@ const methods = {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nButton,
|
||||
},
|
||||
template: '<n8n-button v-bind="$props" @click="onClick" />',
|
||||
template: '<n8n-button v-bind="args" @click="onClick" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
@ -48,48 +49,50 @@ Button.args = {
|
||||
};
|
||||
|
||||
const AllSizesTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nButton,
|
||||
},
|
||||
template: `<div>
|
||||
<n8n-button v-bind="$props" size="large" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" @click="onClick" />
|
||||
<n8n-button v-bind="$props" :loading="true" @click="onClick" />
|
||||
<n8n-button v-bind="$props" :disabled="true" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" @click="onClick" />
|
||||
<n8n-button v-bind="args" :loading="true" @click="onClick" />
|
||||
<n8n-button v-bind="args" :disabled="true" @click="onClick" />
|
||||
</div>`,
|
||||
methods,
|
||||
});
|
||||
|
||||
const AllColorsAndSizesTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nButton,
|
||||
},
|
||||
template: `<div>
|
||||
<n8n-button v-bind="$props" size="large" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="large" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="large" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="large" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="large" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="large" type="danger" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="large" type="danger" @click="onClick" />
|
||||
<br/>
|
||||
<br/>
|
||||
<n8n-button v-bind="$props" size="medium" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="medium" type="danger" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="medium" type="danger" @click="onClick" />
|
||||
<br/>
|
||||
<br/>
|
||||
<n8n-button v-bind="$props" size="small" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="$props" size="small" type="danger" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="primary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="secondary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="tertiary" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="success" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="warning" @click="onClick" />
|
||||
<n8n-button v-bind="args" size="small" type="danger" @click="onClick" />
|
||||
</div>`,
|
||||
methods,
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<button
|
||||
:class="classes"
|
||||
:disabled="disabled || loading"
|
||||
:disabled="isDisabled"
|
||||
:aria-disabled="ariaDisabled"
|
||||
:aria-busy="ariaBusy"
|
||||
aria-live="polite"
|
||||
v-on="$listeners"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<span :class="$style.icon" v-if="loading || icon">
|
||||
<n8n-spinner v-if="loading" :size="size" />
|
||||
@ -17,158 +17,112 @@
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import N8nIcon from '../N8nIcon';
|
||||
import N8nSpinner from '../N8nSpinner';
|
||||
import { useCssModule, computed, useAttrs } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'n8n-button',
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
validator: (value: string): boolean =>
|
||||
['primary', 'secondary', 'tertiary', 'success', 'warning', 'danger'].includes(value),
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
validator: (value: string): boolean =>
|
||||
['xmini', 'mini', 'small', 'medium', 'large', 'xlarge'].includes(value),
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
icon: {
|
||||
type: [String, Array],
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
float: {
|
||||
type: String,
|
||||
validator: (value: string): boolean => ['left', 'right'].includes(value),
|
||||
},
|
||||
square: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const $style = useCssModule();
|
||||
const $attrs = useAttrs();
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
components: {
|
||||
N8nSpinner,
|
||||
N8nIcon,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
computed: {
|
||||
ariaBusy(): 'true' | undefined {
|
||||
return this.loading ? 'true' : undefined;
|
||||
},
|
||||
ariaDisabled(): 'true' | undefined {
|
||||
return this.disabled ? 'true' : undefined;
|
||||
},
|
||||
classes(): string {
|
||||
return (
|
||||
`button ${this.$style.button} ${this.$style[this.type]}` +
|
||||
`${this.size ? ` ${this.$style[this.size]}` : ''}` +
|
||||
`${this.outline ? ` ${this.$style.outline}` : ''}` +
|
||||
`${this.loading ? ` ${this.$style.loading}` : ''}` +
|
||||
`${this.float ? ` ${this.$style[`float-${this.float}`]}` : ''}` +
|
||||
`${this.text ? ` ${this.$style.text}` : ''}` +
|
||||
`${this.disabled ? ` ${this.$style.disabled}` : ''}` +
|
||||
`${this.block ? ` ${this.$style.block}` : ''}` +
|
||||
`${this.active ? ` ${this.$style.active}` : ''}` +
|
||||
`${this.icon || this.loading ? ` ${this.$style.withIcon}` : ''}` +
|
||||
`${this.square ? ` ${this.$style.square}` : ''}`
|
||||
);
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
icon: {
|
||||
type: [String, Array],
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
float: {
|
||||
type: String,
|
||||
},
|
||||
square: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const ariaBusy = computed(() => (props.loading ? 'true' : undefined));
|
||||
const ariaDisabled = computed(() => (props.disabled ? 'true' : undefined));
|
||||
const isDisabled = computed(() => props.disabled || props.loading);
|
||||
|
||||
const classes = computed(() => {
|
||||
return (
|
||||
`button ${$style.button} ${$style[props.type]}` +
|
||||
`${props.size ? ` ${$style[props.size]}` : ''}` +
|
||||
`${props.outline ? ` ${$style.outline}` : ''}` +
|
||||
`${props.loading ? ` ${$style.loading}` : ''}` +
|
||||
`${props.float ? ` ${$style[`float-${props.float}`]}` : ''}` +
|
||||
`${props.text ? ` ${$style.text}` : ''}` +
|
||||
`${props.disabled ? ` ${$style.disabled}` : ''}` +
|
||||
`${props.block ? ` ${$style.block}` : ''}` +
|
||||
`${props.active ? ` ${$style.active}` : ''}` +
|
||||
`${props.icon || props.loading ? ` ${$style.withIcon}` : ''}` +
|
||||
`${props.square ? ` ${$style.square}` : ''}`
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './Button';
|
||||
|
||||
.el-button {
|
||||
@include n8n-button(true);
|
||||
|
||||
--button-padding-vertical: var(--spacing-2xs);
|
||||
--button-padding-horizontal: var(--spacing-xs);
|
||||
--button-font-size: var(--font-size-2xs);
|
||||
|
||||
+ .el-button {
|
||||
margin-left: var(--spacing-2xs);
|
||||
}
|
||||
|
||||
&.btn--cancel,
|
||||
&.el-color-dropdown__link-btn {
|
||||
@include n8n-button-secondary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" module>
|
||||
@import './Button';
|
||||
@import '../../css/mixins/utils';
|
||||
@import '../../css/common/var';
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
border: var(--border-width-base) $button-border-color var(--border-style-base);
|
||||
color: $button-font-color;
|
||||
background-color: $button-background-color;
|
||||
font-weight: var(--font-weight-bold);
|
||||
border-radius: $button-border-radius;
|
||||
padding: $button-padding-vertical $button-padding-horizontal;
|
||||
font-size: $button-font-size;
|
||||
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
transition: 0.3s;
|
||||
|
||||
@include utils-user-select(none);
|
||||
|
||||
&:hover {
|
||||
color: $button-hover-color;
|
||||
border-color: $button-hover-border-color;
|
||||
background-color: $button-hover-background-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $button-focus-outline-color;
|
||||
outline: $focus-outline-width solid $button-focus-outline-color;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
color: $button-active-color;
|
||||
border-color: $button-active-border-color;
|
||||
background-color: $button-active-background-color;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
> i {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
span + span {
|
||||
margin-left: var(--spacing-3xs);
|
||||
}
|
||||
@include n8n-button;
|
||||
}
|
||||
|
||||
$loading-overlay-background-color: rgba(255, 255, 255, 0);
|
||||
@ -178,19 +132,7 @@ $loading-overlay-background-color: rgba(255, 255, 255, 0);
|
||||
*/
|
||||
|
||||
.secondary {
|
||||
--button-color: var(--color-primary);
|
||||
--button-border-color: var(--color-primary);
|
||||
--button-background-color: var(--color-background-xlight);
|
||||
|
||||
--button-active-background-color: var(--color-primary-tint-2);
|
||||
--button-active-color: var(--color-primary);
|
||||
--button-active-border-color: var(--color-primary);
|
||||
|
||||
--button-hover-background-color: var(--color-primary-tint-3);
|
||||
--button-hover-color: var(--color-primary);
|
||||
--button-hover-border-color: var(--color-primary);
|
||||
|
||||
--button-focus-outline-color: var(--color-primary-tint-1);
|
||||
@include n8n-button-secondary;
|
||||
}
|
||||
|
||||
.tertiary {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { render } from '@testing-library/vue';
|
||||
import N8nButton from '../Button.vue';
|
||||
import ElButton from '../overrides/ElButton.vue';
|
||||
|
||||
const slots = {
|
||||
default: 'Button',
|
||||
@ -12,7 +11,9 @@ describe('components', () => {
|
||||
it('should render correctly', () => {
|
||||
const wrapper = render(N8nButton, {
|
||||
slots,
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -25,7 +26,9 @@ describe('components', () => {
|
||||
loading: true,
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -38,7 +41,9 @@ describe('components', () => {
|
||||
icon: 'plus-circle',
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -51,64 +56,13 @@ describe('components', () => {
|
||||
square: true,
|
||||
label: '48',
|
||||
},
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('overrides', () => {
|
||||
it('should use default (`primary`) type when no type is given', () => {
|
||||
const wrapper = render(ElButton, {
|
||||
props: {
|
||||
icon: 'plus-circle',
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should use given (`secondary`) type', () => {
|
||||
const wrapper = render(ElButton, {
|
||||
props: {
|
||||
icon: 'plus-circle',
|
||||
type: 'secondary',
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render as `secondary` when `text` is given as type', () => {
|
||||
const wrapper = render(ElButton, {
|
||||
props: {
|
||||
icon: 'plus-circle',
|
||||
type: 'text',
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render as `tertiary` when `info` is given as type', () => {
|
||||
const wrapper = render(ElButton, {
|
||||
props: {
|
||||
icon: 'plus-circle',
|
||||
type: 'info',
|
||||
},
|
||||
slots,
|
||||
stubs,
|
||||
});
|
||||
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,23 +1,17 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`components > N8nButton > overrides > should render as \`secondary\` when \`text\` is given as type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button secondary medium withIcon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
exports[`components > N8nButton > props > icon > should render icon button 1`] = `"<button class=\\"button button primary medium withIcon\\" aria-live=\\"polite\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > overrides > should render as \`tertiary\` when \`info\` is given as type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button tertiary medium withIcon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > overrides > should use default (\`primary\`) type when no type is given 1`] = `"<button aria-live=\\"polite\\" class=\\"button button primary medium withIcon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > overrides > should use given (\`secondary\`) type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button secondary medium withIcon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > props > icon > should render icon button 1`] = `"<button aria-live=\\"polite\\" class=\\"button button primary medium withIcon\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > props > loading > should render loading spinner 1`] = `"<button disabled=\\"disabled\\" aria-busy=\\"true\\" aria-live=\\"polite\\" class=\\"button button primary medium loading withIcon\\"><span class=\\"icon\\"><n8n-spinner-stub size=\\"medium\\" type=\\"dots\\"></n8n-spinner-stub></span><span>Button</span></button>"`;
|
||||
exports[`components > N8nButton > props > loading > should render loading spinner 1`] = `"<button class=\\"button button primary medium loading withIcon\\" disabled=\\"\\" aria-busy=\\"true\\" aria-live=\\"polite\\"><span class=\\"icon\\"><n8n-spinner-stub size=\\"medium\\" type=\\"dots\\"></n8n-spinner-stub></span><span>Button</span></button>"`;
|
||||
|
||||
exports[`components > N8nButton > props > square > should render square button 1`] = `
|
||||
"<button aria-live=\\"polite\\" class=\\"button button primary medium square\\">
|
||||
<!----><span>48</span></button>"
|
||||
"<button class=\\"button button primary medium square\\" aria-live=\\"polite\\">
|
||||
<!--v-if--><span>48</span>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nButton > should render correctly 1`] = `
|
||||
"<button aria-live=\\"polite\\" class=\\"button button primary medium\\">
|
||||
<!----><span>Button</span></button>"
|
||||
"<button class=\\"button button primary medium\\" aria-live=\\"polite\\">
|
||||
<!--v-if--><span>Button</span>
|
||||
</button>"
|
||||
`;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import ElButton from './ElButton.vue';
|
||||
|
||||
export default ElButton;
|
@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<n8n-button ref="button" v-bind="attrs" v-on="$listeners">
|
||||
<slot />
|
||||
</n8n-button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import N8nButton from '../Button.vue';
|
||||
|
||||
const classToTypeMap = {
|
||||
'btn--cancel': 'secondary',
|
||||
'el-picker-panel__link-btn': 'secondary',
|
||||
};
|
||||
|
||||
type ButtonRef = InstanceType<typeof N8nButton>;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
N8nButton,
|
||||
},
|
||||
computed: {
|
||||
attrs() {
|
||||
let type = this.$attrs.type || 'primary';
|
||||
|
||||
/* Element UI Button can have 'text' or 'info' type which is not supported by n8n-button
|
||||
so render it as 'secondary' or 'tertiary' */
|
||||
if (type === 'text') {
|
||||
type = 'secondary';
|
||||
}
|
||||
|
||||
if (type === 'info') {
|
||||
type = 'tertiary';
|
||||
}
|
||||
|
||||
Object.entries(classToTypeMap).forEach(([className, mappedType]) => {
|
||||
if ((this.$refs.button as ButtonRef)?.$el.classList.contains(className)) {
|
||||
type = mappedType;
|
||||
}
|
||||
});
|
||||
|
||||
delete this.$attrs.type;
|
||||
|
||||
return {
|
||||
...this.$attrs,
|
||||
type,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1 +0,0 @@
|
||||
export { default as N8nElButton } from './ElButton.vue';
|
@ -1,7 +1,7 @@
|
||||
import N8nCallout from './Callout.vue';
|
||||
import N8nLink from '../N8nLink';
|
||||
import N8nText from '../N8nText';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Callout',
|
||||
@ -10,8 +10,8 @@ export default {
|
||||
theme: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['info', 'secondary', 'success', 'warning', 'danger', 'custom'],
|
||||
},
|
||||
options: ['info', 'secondary', 'success', 'warning', 'danger', 'custom'],
|
||||
},
|
||||
message: {
|
||||
control: {
|
||||
@ -41,6 +41,7 @@ interface Args {
|
||||
}
|
||||
|
||||
const template: StoryFn<Args> = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nLink,
|
||||
@ -48,12 +49,12 @@ const template: StoryFn<Args> = (args, { argTypes }) => ({
|
||||
N8nCallout,
|
||||
},
|
||||
template: `
|
||||
<n8n-callout v-bind="$props">
|
||||
<n8n-callout v-bind="args">
|
||||
${args.default}
|
||||
<template #actions v-if="actions">
|
||||
<template #actions v-if="args.actions">
|
||||
${args.actions}
|
||||
</template>
|
||||
<template #trailingContent v-if="trailingContent">
|
||||
<template #trailingContent v-if="args.trailingContent">
|
||||
${args.trailingContent}
|
||||
</template>
|
||||
</n8n-callout>
|
||||
|
@ -8,7 +8,9 @@ describe('components', () => {
|
||||
props: {
|
||||
theme: 'info',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is an info callout.</n8n-text>',
|
||||
},
|
||||
@ -20,7 +22,9 @@ describe('components', () => {
|
||||
props: {
|
||||
theme: 'success',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a success callout.</n8n-text>',
|
||||
},
|
||||
@ -32,7 +36,9 @@ describe('components', () => {
|
||||
props: {
|
||||
theme: 'warning',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a warning callout.</n8n-text>',
|
||||
},
|
||||
@ -44,7 +50,9 @@ describe('components', () => {
|
||||
props: {
|
||||
theme: 'danger',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a danger callout.</n8n-text>',
|
||||
},
|
||||
@ -56,7 +64,9 @@ describe('components', () => {
|
||||
props: {
|
||||
theme: 'secondary',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a secondary callout.</n8n-text>',
|
||||
},
|
||||
@ -69,7 +79,9 @@ describe('components', () => {
|
||||
theme: 'custom',
|
||||
icon: 'code-branch',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a secondary callout.</n8n-text>',
|
||||
},
|
||||
@ -82,7 +94,9 @@ describe('components', () => {
|
||||
theme: 'custom',
|
||||
icon: 'code-branch',
|
||||
},
|
||||
stubs: ['n8n-icon', 'n8n-text', 'n8n-link'],
|
||||
global: {
|
||||
stubs: ['n8n-icon', 'n8n-text', 'n8n-link'],
|
||||
},
|
||||
slots: {
|
||||
default: '<n8n-text size="small">This is a secondary callout.</n8n-text>',
|
||||
actions: '<n8n-link size="small">Do something!</n8n-link>',
|
||||
|
@ -1,93 +1,79 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`components > N8nCallout > should render additional slots correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout custom round\\">
|
||||
"<div class=\\"n8n-callout callout custom round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"code-branch\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"code-branch\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>
|
||||
</n8n-text-stub> <n8n-link-stub size=\\"small\\">Do something!</n8n-link-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub> <n8n-link-stub size=\\"small\\"></n8n-link-stub>
|
||||
</div>
|
||||
<n8n-link-stub theme=\\"secondary\\" size=\\"small\\" bold=\\"true\\" underline=\\"true\\" to=\\"https://n8n.io\\">Learn more</n8n-link-stub>
|
||||
<n8n-link-stub theme=\\"secondary\\" size=\\"small\\" bold=\\"true\\" underline=\\"true\\" to=\\"https://n8n.io\\"></n8n-link-stub>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render custom theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout custom round\\">
|
||||
"<div class=\\"n8n-callout callout custom round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"code-branch\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"code-branch\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render danger theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout danger round\\">
|
||||
"<div class=\\"n8n-callout callout danger round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"times-circle\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"times-circle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a danger callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render info theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout info round\\">
|
||||
"<div class=\\"n8n-callout callout info round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is an info callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render secondary theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout secondary round\\">
|
||||
"<div class=\\"n8n-callout callout secondary round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render success theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout success round\\">
|
||||
"<div class=\\"n8n-callout callout success round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"check-circle\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"check-circle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a success callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCallout > should render warning theme correctly 1`] = `
|
||||
"<div role=\\"alert\\" class=\\"n8n-callout callout warning round\\">
|
||||
"<div class=\\"n8n-callout callout warning round\\" role=\\"alert\\">
|
||||
<div class=\\"messageSection\\">
|
||||
<div class=\\"icon\\">
|
||||
<n8n-icon-stub icon=\\"exclamation-triangle\\" size=\\"medium\\"></n8n-icon-stub>
|
||||
<n8n-icon-stub icon=\\"exclamation-triangle\\" size=\\"medium\\" spin=\\"false\\"></n8n-icon-stub>
|
||||
</div>
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">
|
||||
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a warning callout.</n8n-text-stub>
|
||||
</n8n-text-stub>
|
||||
<n8n-text-stub bold=\\"false\\" size=\\"small\\" compact=\\"false\\" tag=\\"span\\"></n8n-text-stub>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import N8nCard from './Card.vue';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nButton from '../N8nButton/Button.vue';
|
||||
import N8nIcon from '../N8nIcon/Icon.vue';
|
||||
import N8nText from '../N8nText/Text.vue';
|
||||
@ -10,14 +10,16 @@ export default {
|
||||
};
|
||||
|
||||
export const Default: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nCard,
|
||||
},
|
||||
template: '<n8n-card v-bind="$props">This is a card.</n8n-card>',
|
||||
template: '<n8n-card v-bind="args">This is a card.</n8n-card>',
|
||||
});
|
||||
|
||||
export const Hoverable: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nCard,
|
||||
@ -25,7 +27,7 @@ export const Hoverable: StoryFn = (args, { argTypes }) => ({
|
||||
N8nText,
|
||||
},
|
||||
template: `<div style="width: 140px; text-align: center;">
|
||||
<n8n-card v-bind="$props">
|
||||
<n8n-card v-bind="args">
|
||||
<n8n-icon icon="plus" size="xlarge" />
|
||||
<n8n-text size="large" class="mt-2xs">Add</n8n-text>
|
||||
</n8n-card>
|
||||
@ -37,6 +39,7 @@ Hoverable.args = {
|
||||
};
|
||||
|
||||
export const WithSlots: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nCard,
|
||||
@ -44,22 +47,22 @@ export const WithSlots: StoryFn = (args, { argTypes }) => ({
|
||||
N8nIcon,
|
||||
N8nText,
|
||||
},
|
||||
template: `<n8n-card v-bind="$props">
|
||||
<template slot="prepend">
|
||||
template: `<n8n-card v-bind="args">
|
||||
<template #prepend>
|
||||
<n8n-icon icon="check" size="large" />
|
||||
</template>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<strong>Card header</strong>
|
||||
</template>
|
||||
<n8n-text color="text-light" size="medium" class="mt-2xs mb-2xs">
|
||||
This is the card body.
|
||||
</n8n-text>
|
||||
<template slot="footer">
|
||||
<template #footer>
|
||||
<n8n-text size="medium">
|
||||
Card footer
|
||||
</n8n-text>
|
||||
</template>
|
||||
<template slot="append">
|
||||
<template #append>
|
||||
<n8n-button>Click me</n8n-button>
|
||||
</template>
|
||||
</n8n-card>`,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="classes" v-on="$listeners">
|
||||
<div :class="classes" v-bind="$attrs">
|
||||
<div :class="$style.icon" v-if="$slots.prepend">
|
||||
<slot name="prepend" />
|
||||
</div>
|
||||
@ -14,7 +14,7 @@
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.actions" v-if="$slots.append">
|
||||
<div v-if="$slots.append">
|
||||
<slot name="append" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,24 +2,24 @@
|
||||
|
||||
exports[`components > N8nCard > should render correctly 1`] = `
|
||||
"<div class=\\"card\\">
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<div class=\\"content\\">
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<div class=\\"body\\">This is a card.</div>
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`components > N8nCard > should render correctly with header and footer 1`] = `
|
||||
"<div class=\\"card\\">
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<div class=\\"content\\">
|
||||
<div class=\\"header\\">Header</div>
|
||||
<div class=\\"body\\">This is a card.</div>
|
||||
<div class=\\"footer\\">Footer</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
</div>"
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import N8nCheckbox from './Checkbox.vue';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
export default {
|
||||
@ -8,12 +8,13 @@ export default {
|
||||
};
|
||||
|
||||
const methods = {
|
||||
onInput: action('input'),
|
||||
onUpdateModelValue: action('update:modelValue'),
|
||||
onFocus: action('focus'),
|
||||
onChange: action('change'),
|
||||
};
|
||||
|
||||
const DefaultTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nCheckbox,
|
||||
@ -21,7 +22,8 @@ const DefaultTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
data: () => ({
|
||||
isChecked: false,
|
||||
}),
|
||||
template: '<n8n-checkbox v-model="isChecked" v-bind="$props" @input="onInput"></n8n-checkbox>',
|
||||
template:
|
||||
'<n8n-checkbox v-model="isChecked" v-bind="args" @update:modelValue="onUpdateModelValue"></n8n-checkbox>',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
:class="['n8n-checkbox', $style.n8nCheckbox]"
|
||||
:disabled="disabled"
|
||||
:indeterminate="indeterminate"
|
||||
:value="value"
|
||||
@change="onChange"
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="onUpdateModelValue"
|
||||
>
|
||||
<slot></slot>
|
||||
<n8n-input-label
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Checkbox as ElCheckbox } from 'element-ui';
|
||||
import { ElCheckbox } from 'element-plus';
|
||||
import N8nInputLabel from '../N8nInputLabel';
|
||||
|
||||
export default defineComponent({
|
||||
@ -46,7 +46,7 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
@ -57,8 +57,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event: Event) {
|
||||
this.$emit('input', event);
|
||||
onUpdateModelValue(value: boolean) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
onLabelClick() {
|
||||
const checkboxComponent = this.$refs.checkbox as ElCheckbox;
|
||||
|
@ -9,22 +9,25 @@ exports[`components > N8nCheckbox > should render with both child and label 1`]
|
||||
<span
|
||||
class="el-checkbox__input"
|
||||
>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value="Checkbox"
|
||||
/>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="el-checkbox__label"
|
||||
>
|
||||
|
||||
|
||||
<strong>
|
||||
Bold text
|
||||
</strong>
|
||||
|
||||
<div
|
||||
class="container"
|
||||
data-test-id="input-label"
|
||||
@ -38,16 +41,21 @@ exports[`components > N8nCheckbox > should render with both child and label 1`]
|
||||
<span
|
||||
class="n8n-text size-medium regular"
|
||||
>
|
||||
Checkbox
|
||||
<!---->
|
||||
|
||||
Checkbox
|
||||
<!--v-if-->
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</label>
|
||||
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
|
||||
<!--v-if-->
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@ -62,24 +70,27 @@ exports[`components > N8nCheckbox > should render with child 1`] = `
|
||||
<span
|
||||
class="el-checkbox__input"
|
||||
>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="el-checkbox__label"
|
||||
>
|
||||
|
||||
|
||||
<strong>
|
||||
Bold text
|
||||
</strong>
|
||||
<!---->
|
||||
<!---->
|
||||
|
||||
<!--v-if-->
|
||||
|
||||
<!--v-if-->
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@ -94,19 +105,22 @@ exports[`components > N8nCheckbox > should render with label 1`] = `
|
||||
<span
|
||||
class="el-checkbox__input"
|
||||
>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value="Checkbox"
|
||||
/>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="el-checkbox__label"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
class="container"
|
||||
data-test-id="input-label"
|
||||
@ -120,16 +134,21 @@ exports[`components > N8nCheckbox > should render with label 1`] = `
|
||||
<span
|
||||
class="n8n-text size-medium regular"
|
||||
>
|
||||
Checkbox
|
||||
<!---->
|
||||
|
||||
Checkbox
|
||||
<!--v-if-->
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</label>
|
||||
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
|
||||
<!--v-if-->
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@ -144,17 +163,22 @@ exports[`components > N8nCheckbox > should render without label and child conten
|
||||
<span
|
||||
class="el-checkbox__input"
|
||||
>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="el-checkbox__inner"
|
||||
/>
|
||||
</span>
|
||||
<!---->
|
||||
<span
|
||||
class="el-checkbox__label"
|
||||
>
|
||||
|
||||
|
||||
<!--v-if-->
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nColorPicker from './ColorPicker.vue';
|
||||
|
||||
export default {
|
||||
@ -32,10 +32,11 @@ export default {
|
||||
const methods = {
|
||||
onChange: action('change'),
|
||||
onActiveChange: action('active-change'),
|
||||
onInput: action('input'),
|
||||
onInput: action('update:modelValue'),
|
||||
};
|
||||
|
||||
const DefaultTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nColorPicker,
|
||||
@ -44,7 +45,7 @@ const DefaultTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
color: null,
|
||||
}),
|
||||
template:
|
||||
'<n8n-color-picker v-model="color" v-bind="$props" @input="onInput" @change="onChange" @active-change="onActiveChange" />',
|
||||
'<n8n-color-picker v-model="color" v-bind="args" @update:modelValue="onInput" @change="onChange" @active-change="onActiveChange" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { ColorPicker } from 'element-ui';
|
||||
import { uid } from '../../utils';
|
||||
import { ElColorPicker } from 'element-plus';
|
||||
import N8nInput from '../N8nInput';
|
||||
|
||||
export type Props = {
|
||||
@ -10,8 +11,9 @@ export type Props = {
|
||||
colorFormat?: 'hex' | 'rgb' | 'hsl' | 'hsv';
|
||||
popperClass?: string;
|
||||
predefine?: string[];
|
||||
value?: string;
|
||||
modelValue?: string;
|
||||
showInput?: boolean;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@ -21,10 +23,11 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
colorFormat: 'hex',
|
||||
popperClass: '',
|
||||
showInput: true,
|
||||
value: null,
|
||||
modelValue: null,
|
||||
name: uid('color-picker'),
|
||||
});
|
||||
|
||||
const color = ref(props.value);
|
||||
const color = ref(props.modelValue);
|
||||
|
||||
const colorPickerProps = computed(() => {
|
||||
const { value, showInput, ...rest } = props;
|
||||
@ -32,7 +35,7 @@ const colorPickerProps = computed(() => {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'input', value: string): void;
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
(event: 'change', value: string): void;
|
||||
(event: 'active-change', value: string): void;
|
||||
}>();
|
||||
@ -43,7 +46,7 @@ const model = computed({
|
||||
},
|
||||
set(value: string) {
|
||||
color.value = value;
|
||||
emit('input', value);
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
@ -61,7 +64,7 @@ const onActiveChange = (value: string) => {
|
||||
</script>
|
||||
<template>
|
||||
<span :class="['n8n-color-picker', $style.component]">
|
||||
<color-picker
|
||||
<el-color-picker
|
||||
v-model="model"
|
||||
v-bind="colorPickerProps"
|
||||
@change="onChange"
|
||||
@ -72,8 +75,9 @@ const onActiveChange = (value: string) => {
|
||||
:class="$style.input"
|
||||
:disabled="props.disabled"
|
||||
:size="props.size"
|
||||
:value="color"
|
||||
@input="onInput"
|
||||
:modelValue="color"
|
||||
:name="name"
|
||||
@update:modelValue="onInput"
|
||||
type="text"
|
||||
/>
|
||||
</span>
|
||||
|
@ -4,12 +4,21 @@ import N8nColorPicker from '../ColorPicker.vue';
|
||||
describe('components', () => {
|
||||
describe('N8nColorPicker', () => {
|
||||
it('should render with input', () => {
|
||||
const { container } = render(N8nColorPicker);
|
||||
const { container } = render(N8nColorPicker, {
|
||||
props: {
|
||||
name: 'color-picker',
|
||||
},
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render without input', () => {
|
||||
const { container } = render(N8nColorPicker, { props: { showInput: false } });
|
||||
const { container } = render(N8nColorPicker, {
|
||||
props: {
|
||||
name: 'color-picker',
|
||||
showInput: false,
|
||||
},
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -6,11 +6,15 @@ exports[`components > N8nColorPicker > should render with input 1`] = `
|
||||
class="n8n-color-picker component"
|
||||
data-v-dab78bb8=""
|
||||
>
|
||||
|
||||
<div
|
||||
class="el-color-picker el-color-picker--medium"
|
||||
data-v-dab78bb8=""
|
||||
aria-description="current color is . press enter to select a new color."
|
||||
aria-label="color picker"
|
||||
class="el-color-picker el-color-picker--medium el-tooltip__trigger el-tooltip__trigger"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="el-color-picker__trigger"
|
||||
>
|
||||
@ -20,122 +24,75 @@ exports[`components > N8nColorPicker > should render with input 1`] = `
|
||||
<span
|
||||
class="el-color-picker__color-inner"
|
||||
style="background-color: transparent;"
|
||||
/>
|
||||
<span
|
||||
class="el-color-picker__empty el-icon-close"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="el-color-picker__icon el-icon-arrow-down"
|
||||
style="display: none;"
|
||||
/>
|
||||
</div>
|
||||
<transition-stub
|
||||
class="el-color-picker__panel"
|
||||
name="el-zoom-in-top"
|
||||
>
|
||||
<div
|
||||
class="el-color-dropdown"
|
||||
style="display: none;"
|
||||
>
|
||||
<div
|
||||
class="el-color-dropdown__main-wrapper"
|
||||
>
|
||||
<div
|
||||
class="el-color-hue-slider is-vertical"
|
||||
style="float: right;"
|
||||
<i
|
||||
class="el-icon el-color-picker__icon is-icon-arrow-down"
|
||||
style="display: none;"
|
||||
>
|
||||
<div
|
||||
class="el-color-hue-slider__bar"
|
||||
/>
|
||||
<div
|
||||
class="el-color-hue-slider__thumb"
|
||||
style="left: 0px; top: 0px;"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="el-color-svpanel"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
>
|
||||
<div
|
||||
class="el-color-svpanel__white"
|
||||
/>
|
||||
<div
|
||||
class="el-color-svpanel__black"
|
||||
/>
|
||||
<div
|
||||
class="el-color-svpanel__cursor"
|
||||
style="top: 0px; left: 0px;"
|
||||
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div
|
||||
class="el-color-dropdown__btns"
|
||||
>
|
||||
<span
|
||||
class="el-color-dropdown__value"
|
||||
>
|
||||
<div
|
||||
class="el-input el-input--mini"
|
||||
>
|
||||
<!---->
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="el-input__inner"
|
||||
type="text"
|
||||
<path
|
||||
d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</span>
|
||||
<button
|
||||
class="el-button el-color-dropdown__link-btn el-button--text el-button--mini"
|
||||
type="button"
|
||||
</svg>
|
||||
|
||||
</i>
|
||||
<i
|
||||
class="el-icon el-color-picker__empty is-icon-close"
|
||||
>
|
||||
<!---->
|
||||
<!---->
|
||||
<span>
|
||||
|
||||
清空
|
||||
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="el-button el-color-dropdown__btn el-button--default el-button--mini is-plain"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
<!---->
|
||||
<span>
|
||||
|
||||
确定
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition-stub>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!--teleport start-->
|
||||
<!--teleport end-->
|
||||
|
||||
<div
|
||||
class="el-input el-input--medium n8n-input input"
|
||||
class="el-input el-input--medium n8n-input input input"
|
||||
data-v-dab78bb8=""
|
||||
>
|
||||
<!---->
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="el-input__inner"
|
||||
type="text"
|
||||
/>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!-- input -->
|
||||
|
||||
<!-- prepend slot -->
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="el-input__wrapper"
|
||||
>
|
||||
<!-- prefix slot -->
|
||||
<!--v-if-->
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="el-input__inner"
|
||||
maxlength="Infinity"
|
||||
name="color-picker"
|
||||
placeholder=""
|
||||
rows="2"
|
||||
tabindex="0"
|
||||
title=""
|
||||
type="text"
|
||||
/>
|
||||
<!-- suffix slot -->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!-- append slot -->
|
||||
<!--v-if-->
|
||||
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
@ -147,11 +104,15 @@ exports[`components > N8nColorPicker > should render without input 1`] = `
|
||||
class="n8n-color-picker component"
|
||||
data-v-dab78bb8=""
|
||||
>
|
||||
|
||||
<div
|
||||
class="el-color-picker el-color-picker--medium"
|
||||
data-v-dab78bb8=""
|
||||
aria-description="current color is . press enter to select a new color."
|
||||
aria-label="color picker"
|
||||
class="el-color-picker el-color-picker--medium el-tooltip__trigger el-tooltip__trigger"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<!---->
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="el-color-picker__trigger"
|
||||
>
|
||||
@ -161,109 +122,46 @@ exports[`components > N8nColorPicker > should render without input 1`] = `
|
||||
<span
|
||||
class="el-color-picker__color-inner"
|
||||
style="background-color: transparent;"
|
||||
/>
|
||||
<span
|
||||
class="el-color-picker__empty el-icon-close"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="el-color-picker__icon el-icon-arrow-down"
|
||||
style="display: none;"
|
||||
/>
|
||||
</div>
|
||||
<transition-stub
|
||||
class="el-color-picker__panel"
|
||||
name="el-zoom-in-top"
|
||||
>
|
||||
<div
|
||||
class="el-color-dropdown"
|
||||
style="display: none;"
|
||||
>
|
||||
<div
|
||||
class="el-color-dropdown__main-wrapper"
|
||||
>
|
||||
<div
|
||||
class="el-color-hue-slider is-vertical"
|
||||
style="float: right;"
|
||||
<i
|
||||
class="el-icon el-color-picker__icon is-icon-arrow-down"
|
||||
style="display: none;"
|
||||
>
|
||||
<div
|
||||
class="el-color-hue-slider__bar"
|
||||
/>
|
||||
<div
|
||||
class="el-color-hue-slider__thumb"
|
||||
style="left: 0px; top: 0px;"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="el-color-svpanel"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
>
|
||||
<div
|
||||
class="el-color-svpanel__white"
|
||||
/>
|
||||
<div
|
||||
class="el-color-svpanel__black"
|
||||
/>
|
||||
<div
|
||||
class="el-color-svpanel__cursor"
|
||||
style="top: 0px; left: 0px;"
|
||||
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div
|
||||
class="el-color-dropdown__btns"
|
||||
>
|
||||
<span
|
||||
class="el-color-dropdown__value"
|
||||
>
|
||||
<div
|
||||
class="el-input el-input--mini"
|
||||
>
|
||||
<!---->
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="el-input__inner"
|
||||
type="text"
|
||||
<path
|
||||
d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</span>
|
||||
<button
|
||||
class="el-button el-color-dropdown__link-btn el-button--text el-button--mini"
|
||||
type="button"
|
||||
</svg>
|
||||
|
||||
</i>
|
||||
<i
|
||||
class="el-icon el-color-picker__empty is-icon-close"
|
||||
>
|
||||
<!---->
|
||||
<!---->
|
||||
<span>
|
||||
|
||||
清空
|
||||
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="el-button el-color-dropdown__btn el-button--default el-button--mini is-plain"
|
||||
type="button"
|
||||
>
|
||||
<!---->
|
||||
<!---->
|
||||
<span>
|
||||
|
||||
确定
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition-stub>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
</i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!--teleport start-->
|
||||
<!--teleport end-->
|
||||
|
||||
<!--v-if-->
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import N8nDatatable from './Datatable.vue';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import { rows, columns } from './__tests__/data';
|
||||
|
||||
export default {
|
||||
@ -8,11 +8,12 @@ export default {
|
||||
};
|
||||
|
||||
export const Default: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nDatatable,
|
||||
},
|
||||
template: '<n8n-datatable v-bind="$props"></n8n-datatable>',
|
||||
template: '<n8n-datatable v-bind="args"></n8n-datatable>',
|
||||
});
|
||||
|
||||
Default.args = {
|
||||
|
@ -114,7 +114,7 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="classes" v-on="$listeners">
|
||||
<div :class="classes" v-bind="$attrs">
|
||||
<table :class="$style.datatable">
|
||||
<thead :class="$style.datatableHeader">
|
||||
<tr>
|
||||
@ -157,9 +157,9 @@ export default defineComponent({
|
||||
<div :class="$style.pageSizeSelector">
|
||||
<n8n-select
|
||||
size="mini"
|
||||
:value="rowsPerPage"
|
||||
@input="onRowsPerPageChange"
|
||||
popper-append-to-body
|
||||
:modelValue="rowsPerPage"
|
||||
@update:modelValue="onRowsPerPageChange"
|
||||
teleported
|
||||
>
|
||||
<template #prepend>{{ t('datatable.pageSize') }}</template>
|
||||
<n8n-option
|
||||
|
@ -10,12 +10,14 @@ describe('components', () => {
|
||||
|
||||
it('should render correctly', () => {
|
||||
const wrapper = render(N8nDatatable, {
|
||||
propsData: {
|
||||
props: {
|
||||
columns,
|
||||
rows,
|
||||
rowsPerPage,
|
||||
},
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.container.querySelectorAll('thead tr').length).toEqual(1);
|
||||
@ -28,12 +30,14 @@ describe('components', () => {
|
||||
|
||||
it('should add column classes', () => {
|
||||
const wrapper = render(N8nDatatable, {
|
||||
propsData: {
|
||||
props: {
|
||||
columns: columns.map((column) => ({ ...column, classes: ['example'] })),
|
||||
rows,
|
||||
rowsPerPage,
|
||||
},
|
||||
stubs,
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.container.querySelectorAll('.example').length).toEqual(
|
||||
@ -43,14 +47,16 @@ describe('components', () => {
|
||||
|
||||
it('should render row slot', () => {
|
||||
const wrapper = render(N8nDatatable, {
|
||||
propsData: {
|
||||
props: {
|
||||
columns,
|
||||
rows,
|
||||
rowsPerPage,
|
||||
},
|
||||
stubs,
|
||||
scopedSlots: {
|
||||
row: '<main><td v-for="column in props.columns" :key="column.id">Row slot</td></main>', // Wrapper is necessary for looping
|
||||
global: {
|
||||
stubs,
|
||||
},
|
||||
slots: {
|
||||
row: '<template #row="props"><td v-for="column in props.columns" :key="column.id">Row slot</td></template>', // Wrapper is necessary for looping
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -5,95 +5,99 @@ exports[`components > N8nDatatable > should render correctly 1`] = `
|
||||
<table class=\\"datatable\\">
|
||||
<thead class=\\"datatableHeader\\">
|
||||
<tr>
|
||||
<th> ID </th>
|
||||
<th> Name </th>
|
||||
<th> Age </th>
|
||||
<th> Action </th>
|
||||
<th class=\\"\\">ID</th>
|
||||
<th class=\\"\\">Name</th>
|
||||
<th class=\\"\\">Age</th>
|
||||
<th class=\\"\\">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span>1</span></td>
|
||||
<td><span>Richard Hendricks</span></td>
|
||||
<td><span>29</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 1</span></button></td>
|
||||
<td class=\\"\\"><span>1</span></td>
|
||||
<td class=\\"\\"><span>Richard Hendricks</span></td>
|
||||
<td class=\\"\\"><span>29</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 1</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>2</span></td>
|
||||
<td><span>Bertram Gilfoyle</span></td>
|
||||
<td><span>44</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 2</span></button></td>
|
||||
<td class=\\"\\"><span>2</span></td>
|
||||
<td class=\\"\\"><span>Bertram Gilfoyle</span></td>
|
||||
<td class=\\"\\"><span>44</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 2</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>3</span></td>
|
||||
<td><span>Dinesh Chugtai</span></td>
|
||||
<td><span>31</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 3</span></button></td>
|
||||
<td class=\\"\\"><span>3</span></td>
|
||||
<td class=\\"\\"><span>Dinesh Chugtai</span></td>
|
||||
<td class=\\"\\"><span>31</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 3</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>4</span></td>
|
||||
<td><span>Jared Dunn </span></td>
|
||||
<td><span>38</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 4</span></button></td>
|
||||
<td class=\\"\\"><span>4</span></td>
|
||||
<td class=\\"\\"><span>Jared Dunn </span></td>
|
||||
<td class=\\"\\"><span>38</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 4</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>5</span></td>
|
||||
<td><span>Richard Hendricks</span></td>
|
||||
<td><span>29</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 5</span></button></td>
|
||||
<td class=\\"\\"><span>5</span></td>
|
||||
<td class=\\"\\"><span>Richard Hendricks</span></td>
|
||||
<td class=\\"\\"><span>29</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 5</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>6</span></td>
|
||||
<td><span>Bertram Gilfoyle</span></td>
|
||||
<td><span>44</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 6</span></button></td>
|
||||
<td class=\\"\\"><span>6</span></td>
|
||||
<td class=\\"\\"><span>Bertram Gilfoyle</span></td>
|
||||
<td class=\\"\\"><span>44</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 6</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>7</span></td>
|
||||
<td><span>Dinesh Chugtai</span></td>
|
||||
<td><span>31</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 7</span></button></td>
|
||||
<td class=\\"\\"><span>7</span></td>
|
||||
<td class=\\"\\"><span>Dinesh Chugtai</span></td>
|
||||
<td class=\\"\\"><span>31</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 7</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>8</span></td>
|
||||
<td><span>Jared Dunn </span></td>
|
||||
<td><span>38</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 8</span></button></td>
|
||||
<td class=\\"\\"><span>8</span></td>
|
||||
<td class=\\"\\"><span>Jared Dunn </span></td>
|
||||
<td class=\\"\\"><span>38</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 8</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>9</span></td>
|
||||
<td><span>Richard Hendricks</span></td>
|
||||
<td><span>29</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 9</span></button></td>
|
||||
<td class=\\"\\"><span>9</span></td>
|
||||
<td class=\\"\\"><span>Richard Hendricks</span></td>
|
||||
<td class=\\"\\"><span>29</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 9</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span>10</span></td>
|
||||
<td><span>Bertram Gilfoyle</span></td>
|
||||
<td><span>44</span></td>
|
||||
<td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
|
||||
<!----><span>Button 10</span></button></td>
|
||||
<td class=\\"\\"><span>10</span></td>
|
||||
<td class=\\"\\"><span>Bertram Gilfoyle</span></td>
|
||||
<td class=\\"\\"><span>44</span></td>
|
||||
<td class=\\"\\"><button class=\\"button button primary medium\\" aria-live=\\"polite\\" column=\\"[object Object]\\">
|
||||
<!--v-if--><span>Button 10</span>
|
||||
</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class=\\"pagination\\">
|
||||
<n8n-pagination-stub pagesize=\\"10\\" total=\\"15\\" pagercount=\\"5\\" currentpage=\\"1\\" layout=\\"prev, pager, next\\" pagesizes=\\"10,20,30,40,50,100\\" background=\\"true\\"></n8n-pagination-stub>
|
||||
<n8n-pagination-stub pagesize=\\"10\\" total=\\"15\\" currentpage=\\"1\\" pagercount=\\"5\\" layout=\\"prev, pager, next\\" pagesizes=\\"10,20,30,40,50,100\\" popperclass=\\"\\" prevtext=\\"\\" previcon=\\"[object Object]\\" nexttext=\\"\\" nexticon=\\"[object Object]\\" small=\\"false\\" background=\\"true\\" disabled=\\"false\\" hideonsinglepage=\\"false\\"></n8n-pagination-stub>
|
||||
<div class=\\"pageSizeSelector\\">
|
||||
<n8n-select-stub value=\\"10\\" size=\\"mini\\" popperappendtobody=\\"true\\">
|
||||
<n8n-option-stub value=\\"10\\" label=\\"10\\"></n8n-option-stub>
|
||||
<n8n-option-stub value=\\"25\\" label=\\"25\\"></n8n-option-stub>
|
||||
<n8n-option-stub value=\\"50\\" label=\\"50\\"></n8n-option-stub>
|
||||
<n8n-option-stub value=\\"100\\" label=\\"100\\"></n8n-option-stub>
|
||||
<n8n-option-stub value=\\"*\\" label=\\"All\\"></n8n-option-stub>
|
||||
</n8n-select-stub>
|
||||
<n8n-select-stub modelvalue=\\"10\\" autocomplete=\\"off\\" automaticdropdown=\\"false\\" size=\\"mini\\" effect=\\"light\\" disabled=\\"false\\" clearable=\\"false\\" filterable=\\"false\\" allowcreate=\\"false\\" loading=\\"false\\" popperoptions=\\"[object Object]\\" remote=\\"false\\" multiple=\\"false\\" multiplelimit=\\"0\\" defaultfirstoption=\\"false\\" reservekeyword=\\"true\\" valuekey=\\"value\\" collapsetags=\\"false\\" collapsetagstooltip=\\"false\\" maxcollapsetags=\\"1\\" teleported=\\"true\\" persistent=\\"true\\" clearicon=\\"[object Object]\\" fitinputwidth=\\"false\\" suffixicon=\\"[object Object]\\" tagtype=\\"info\\" validateevent=\\"true\\" remoteshowsuffix=\\"false\\" suffixtransition=\\"true\\" placement=\\"bottom-start\\" popperappendtobody=\\"false\\" limitpopperwidth=\\"false\\"></n8n-select-stub>
|
||||
</div>
|
||||
</div>
|
||||
</div>"
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nFormBox from './FormBox.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Modules/FormBox',
|
||||
@ -13,15 +13,16 @@ export default {
|
||||
|
||||
const methods = {
|
||||
onSubmit: action('submit'),
|
||||
onInput: action('input'),
|
||||
onChange: action('update'),
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nFormBox,
|
||||
},
|
||||
template: '<n8n-form-box v-bind="$props" @submit="onSubmit" @input="onInput" />',
|
||||
template: '<n8n-form-box v-bind="args" @submit="onSubmit" @update="onUpdate" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
:inputs="inputs"
|
||||
:eventBus="formBus"
|
||||
:columnView="true"
|
||||
@input="onInput"
|
||||
@update="onUpdateModelValue"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</div>
|
||||
@ -87,8 +87,8 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onInput(e: { name: string; value: string }) {
|
||||
this.$emit('input', e);
|
||||
onUpdateModelValue(e: { name: string; value: string }) {
|
||||
this.$emit('update', e);
|
||||
},
|
||||
onSubmit(e: { [key: string]: string }) {
|
||||
this.$emit('submit', e);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nFormInput from './FormInput.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Modules/FormInput',
|
||||
@ -9,18 +9,19 @@ export default {
|
||||
};
|
||||
|
||||
const methods = {
|
||||
onInput: action('input'),
|
||||
onUpdateModelValue: action('update:modelValue'),
|
||||
onFocus: action('focus'),
|
||||
onChange: action('change'),
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nFormInput,
|
||||
},
|
||||
template: `
|
||||
<n8n-form-input v-bind="$props" v-model="val" @input="onInput" @change="onChange" @focus="onFocus" />
|
||||
<n8n-form-input v-bind="args" v-model="val" @update:modelValue="onUpdateModelValue" @change="onChange" @focus="onFocus" />
|
||||
`,
|
||||
methods,
|
||||
data() {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<n8n-checkbox
|
||||
v-if="type === 'checkbox'"
|
||||
v-bind="$props"
|
||||
@input="onInput"
|
||||
@update:modelValue="onUpdateModelValue"
|
||||
@focus="onFocus"
|
||||
ref="inputRef"
|
||||
/>
|
||||
@ -17,10 +17,10 @@
|
||||
{{ tooltipText }}
|
||||
</template>
|
||||
<el-switch
|
||||
:value="value"
|
||||
@change="onInput"
|
||||
:modelValue="modelValue"
|
||||
:active-color="activeColor"
|
||||
:inactive-color="inactiveColor"
|
||||
@update:modelValue="onUpdateModelValue"
|
||||
></el-switch>
|
||||
</n8n-input-label>
|
||||
<n8n-input-label
|
||||
@ -34,14 +34,15 @@
|
||||
<slot v-if="hasDefaultSlot" />
|
||||
<n8n-select
|
||||
v-else-if="type === 'select' || type === 'multi-select'"
|
||||
:value="value"
|
||||
:modelValue="modelValue"
|
||||
:placeholder="placeholder"
|
||||
:multiple="type === 'multi-select'"
|
||||
:disabled="disabled"
|
||||
@change="onInput"
|
||||
@update:modelValue="onUpdateModelValue"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
:name="name"
|
||||
:teleported="teleported"
|
||||
ref="inputRef"
|
||||
>
|
||||
<n8n-option
|
||||
@ -56,11 +57,11 @@
|
||||
:name="name"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
:modelValue="modelValue"
|
||||
:maxlength="maxlength"
|
||||
:autocomplete="autocomplete"
|
||||
:disabled="disabled"
|
||||
@input="onInput"
|
||||
@update:modelValue="onUpdateModelValue"
|
||||
@blur="onBlur"
|
||||
@focus="onFocus"
|
||||
ref="inputRef"
|
||||
@ -92,7 +93,7 @@ import N8nSelect from '../N8nSelect';
|
||||
import N8nOption from '../N8nOption';
|
||||
import N8nInputLabel from '../N8nInputLabel';
|
||||
import N8nCheckbox from '../N8nCheckbox';
|
||||
import { Switch as ElSwitch } from 'element-ui';
|
||||
import { ElSwitch } from 'element-plus';
|
||||
|
||||
import { getValidationError, VALIDATORS } from './validators';
|
||||
import type { Rule, RuleGroup, IValidator, Validatable, FormState } from '../../types';
|
||||
@ -100,7 +101,7 @@ import type { Rule, RuleGroup, IValidator, Validatable, FormState } from '../../
|
||||
import { t } from '../../locale';
|
||||
|
||||
export interface Props {
|
||||
value: Validatable;
|
||||
modelValue: Validatable;
|
||||
label: string;
|
||||
infoText?: string;
|
||||
required?: boolean;
|
||||
@ -125,6 +126,7 @@ export interface Props {
|
||||
activeColor?: string;
|
||||
inactiveLabel?: string;
|
||||
inactiveColor?: string;
|
||||
teleported?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@ -133,11 +135,12 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'text',
|
||||
showRequiredAsterisk: true,
|
||||
validateOnBlur: true,
|
||||
teleported: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'validate', shouldValidate: boolean): void;
|
||||
(event: 'input', value: unknown): void;
|
||||
(event: 'update:modelValue', value: unknown): void;
|
||||
(event: 'focus'): void;
|
||||
(event: 'blur'): void;
|
||||
(event: 'enter'): void;
|
||||
@ -160,7 +163,11 @@ function getInputValidationError(): ReturnType<IValidator['validate']> {
|
||||
} as { [key: string]: IValidator | RuleGroup };
|
||||
|
||||
if (props.required) {
|
||||
const error = getValidationError(props.value, validators, validators.REQUIRED as IValidator);
|
||||
const error = getValidationError(
|
||||
props.modelValue,
|
||||
validators,
|
||||
validators.REQUIRED as IValidator,
|
||||
);
|
||||
if (error) return error;
|
||||
}
|
||||
|
||||
@ -169,7 +176,7 @@ function getInputValidationError(): ReturnType<IValidator['validate']> {
|
||||
const rule = rules[i] as Rule;
|
||||
if (validators[rule.name]) {
|
||||
const error = getValidationError(
|
||||
props.value,
|
||||
props.modelValue,
|
||||
validators,
|
||||
validators[rule.name] as IValidator,
|
||||
rule.config,
|
||||
@ -180,7 +187,7 @@ function getInputValidationError(): ReturnType<IValidator['validate']> {
|
||||
|
||||
if (rules[i].hasOwnProperty('rules')) {
|
||||
const rule = rules[i] as RuleGroup;
|
||||
const error = getValidationError(props.value, validators, rule);
|
||||
const error = getValidationError(props.modelValue, validators, rule);
|
||||
if (error) return error;
|
||||
}
|
||||
}
|
||||
@ -194,9 +201,9 @@ function onBlur() {
|
||||
emit('blur');
|
||||
}
|
||||
|
||||
function onInput(value: FormState) {
|
||||
function onUpdateModelValue(value: FormState) {
|
||||
state.isTyping = true;
|
||||
emit('input', value);
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
|
||||
function onFocus() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nFormInputs from './FormInputs.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Modules/FormInputs',
|
||||
@ -12,16 +12,17 @@ export default {
|
||||
};
|
||||
|
||||
const methods = {
|
||||
onInput: action('input'),
|
||||
onChange: action('change'),
|
||||
onSubmit: action('submit'),
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nFormInputs,
|
||||
},
|
||||
template: '<n8n-form-inputs v-bind="$props" @submit="onSubmit" @input="onInput" />',
|
||||
template: '<n8n-form-inputs v-bind="args" @submit="onSubmit" @change="onChange" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -22,12 +22,12 @@
|
||||
v-bind="input.properties"
|
||||
:name="input.name"
|
||||
:label="input.properties.label || ''"
|
||||
:value="values[input.name]"
|
||||
:modelValue="values[input.name]"
|
||||
:data-test-id="input.name"
|
||||
:showValidationWarnings="showValidationWarnings"
|
||||
@input="(value) => onInput(input.name, value)"
|
||||
:teleported="teleported"
|
||||
@update:modelValue="(value) => onUpdateModelValue(input.name, value)"
|
||||
@validate="(value) => onValidate(input.name, value)"
|
||||
@change="(value) => onInput(input.name, value)"
|
||||
@enter="onSubmit"
|
||||
/>
|
||||
</div>
|
||||
@ -69,6 +69,10 @@ export default defineComponent({
|
||||
default: '',
|
||||
validator: (value: string): boolean => ['', 'xs', 's', 'm', 'm', 'l', 'xl'].includes(value),
|
||||
},
|
||||
teleported: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -80,7 +84,10 @@ export default defineComponent({
|
||||
mounted() {
|
||||
this.inputs.forEach((input) => {
|
||||
if (input.hasOwnProperty('initialValue')) {
|
||||
this.$set(this.values, input.name, input.initialValue);
|
||||
this.values = {
|
||||
...this.values,
|
||||
[input.name]: input.initialValue,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@ -105,15 +112,18 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onInput(name: string, value: unknown) {
|
||||
onUpdateModelValue(name: string, value: unknown) {
|
||||
this.values = {
|
||||
...this.values,
|
||||
[name]: value,
|
||||
};
|
||||
this.$emit('input', { name, value });
|
||||
this.$emit('update', { name, value });
|
||||
},
|
||||
onValidate(name: string, valid: boolean) {
|
||||
this.$set(this.validity, name, valid);
|
||||
this.validity = {
|
||||
...this.validity,
|
||||
[name]: valid,
|
||||
};
|
||||
},
|
||||
onSubmit() {
|
||||
this.showValidationWarnings = true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nHeading from './Heading.vue';
|
||||
|
||||
export default {
|
||||
@ -8,24 +8,25 @@ export default {
|
||||
size: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['2xlarge', 'xlarge', 'large', 'medium', 'small'],
|
||||
},
|
||||
options: ['2xlarge', 'xlarge', 'large', 'medium', 'small'],
|
||||
},
|
||||
color: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight'],
|
||||
},
|
||||
options: ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nHeading,
|
||||
},
|
||||
template: '<n8n-heading v-bind="$props">hello world</n8n-heading>',
|
||||
template: '<n8n-heading v-bind="args">hello world</n8n-heading>',
|
||||
});
|
||||
|
||||
export const Heading = Template.bind({});
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<component :is="tag" :class="['n8n-heading', ...classes]" v-on="$listeners">
|
||||
<component :is="tag" :class="['n8n-heading', ...classes]" v-bind="$attrs">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
import N8nIcon from './Icon.vue';
|
||||
|
||||
export default {
|
||||
@ -11,8 +11,8 @@ export default {
|
||||
size: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['xsmall', 'small', 'medium', 'large'],
|
||||
},
|
||||
options: ['xsmall', 'small', 'medium', 'large'],
|
||||
},
|
||||
spin: {
|
||||
control: {
|
||||
@ -23,11 +23,12 @@ export default {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nIcon,
|
||||
},
|
||||
template: '<n8n-icon v-bind="$props" />',
|
||||
template: '<n8n-icon v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Clock = Template.bind({});
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n8n-text :size="size" :color="color" :compact="true" class="n8n-icon" v-on="$listeners">
|
||||
<n8n-text :size="size" :color="color" :compact="true" class="n8n-icon" v-bind="$attrs">
|
||||
<font-awesome-icon :icon="icon" :spin="spin" :class="$style[size]" />
|
||||
</n8n-text>
|
||||
</template>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import N8nIconButton from './IconButton.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Icon Button',
|
||||
@ -13,8 +13,8 @@ export default {
|
||||
size: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['mini', 'small', 'medium', 'large', 'xlarge'],
|
||||
},
|
||||
options: ['mini', 'small', 'medium', 'large', 'xlarge'],
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
@ -27,11 +27,12 @@ const methods = {
|
||||
};
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nIconButton,
|
||||
},
|
||||
template: '<n8n-icon-button v-bind="$props" @click="onClick" />',
|
||||
template: '<n8n-icon-button @click="onClick" v-bind="args" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
@ -42,12 +43,13 @@ Button.args = {
|
||||
};
|
||||
|
||||
const ManyTemplate: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
N8nIconButton,
|
||||
},
|
||||
template:
|
||||
'<div> <n8n-icon-button v-bind="$props" size="xlarge" @click="onClick" /> <n8n-icon-button v-bind="$props" size="large" @click="onClick" /> <n8n-icon-button v-bind="$props" size="medium" @click="onClick" /> <n8n-icon-button v-bind="$props" size="small" @click="onClick" /> <n8n-icon-button v-bind="$props" :loading="true" @click="onClick" /> <n8n-icon-button v-bind="$props" :disabled="true" @click="onClick" /></div>',
|
||||
'<div> <n8n-icon-button v-bind="args" size="xlarge" @click="onClick" /> <n8n-icon-button v-bind="args" size="large" @click="onClick" /> <n8n-icon-button v-bind="args" size="medium" @click="onClick" /> <n8n-icon-button v-bind="args" size="small" @click="onClick" /> <n8n-icon-button v-bind="args" :loading="true" @click="onClick" /> <n8n-icon-button v-bind="args" :disabled="true" @click="onClick" /></div>',
|
||||
methods,
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n8n-button square v-bind="$props" v-on="$listeners" />
|
||||
<n8n-button square v-bind="{ ...$attrs, ...$props }" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user