feat(editor): Use i18n component instead od v-html for localization
* feat(editor): Export i18n instance and bind it to Vue instance * feat(editor): Audit usage of v-html and replace with alternatives where possible * 🔀 Fix conflicted element in RunDataTable * ♻️ Refactor issues elements with the new TitledList component * 🐛 Fixing unknown node modal dialog content rendering Co-authored-by: Milorad Filipovic <milorad@n8n.io>
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
<span
|
||||
size="small"
|
||||
:class="[$style.infoText, infoTextErrorMessage ? $style.error : '']"
|
||||
v-html="infoTextErrorMessage"
|
||||
v-text="infoTextErrorMessage"
|
||||
></span>
|
||||
</div>
|
||||
<el-checkbox
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div :class="$style.descriptionContainer" v-if="this.mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE">
|
||||
<n8n-info-tip theme="info" type="note" :bold="false">
|
||||
<template>
|
||||
<span v-html="getModalContent.description"></span>
|
||||
<span v-text="getModalContent.description"></span>
|
||||
</template>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<font-awesome-icon icon="exclamation-triangle" />
|
||||
<span v-html="$locale.baseText('executionDetails.readOnly.readOnly')"></span>
|
||||
<span v-text="$locale.baseText('executionDetails.readOnly.readOnly')"></span>
|
||||
</div>
|
||||
</n8n-tooltip>
|
||||
</template>
|
||||
@@ -22,4 +22,4 @@ export default Vue.extend({
|
||||
svg {
|
||||
margin-right: 6px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<div v-if="!data.disabled" :class="{'node-info-icon': true, 'shift-icon': shiftOutputCount}">
|
||||
<div v-if="hasIssues" class="node-issues">
|
||||
<n8n-tooltip placement="bottom" >
|
||||
<div slot="content" v-html="nodeIssues"></div>
|
||||
<titled-list slot="content" :title="`${$locale.baseText('node.issues')}:`" :items="nodeIssues" />
|
||||
<font-awesome-icon icon="exclamation-triangle" />
|
||||
</n8n-tooltip>
|
||||
</div>
|
||||
<div v-else-if="waiting" class="waiting">
|
||||
<n8n-tooltip placement="bottom">
|
||||
<div slot="content" v-html="waiting"></div>
|
||||
<div slot="content" v-text="waiting"></div>
|
||||
<font-awesome-icon icon="clock" />
|
||||
</n8n-tooltip>
|
||||
</div>
|
||||
@@ -105,6 +105,7 @@ import {
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import NodeIcon from '@/components/NodeIcon.vue';
|
||||
import TitledList from '@/components/TitledList.vue';
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
@@ -121,6 +122,7 @@ export default mixins(
|
||||
).extend({
|
||||
name: 'Node',
|
||||
components: {
|
||||
TitledList,
|
||||
NodeIcon,
|
||||
},
|
||||
computed: {
|
||||
@@ -200,14 +202,12 @@ export default mixins(
|
||||
executing: this.isExecuting,
|
||||
};
|
||||
},
|
||||
nodeIssues (): string {
|
||||
nodeIssues (): string[] {
|
||||
if (this.data.issues === undefined) {
|
||||
return '';
|
||||
return [];
|
||||
}
|
||||
|
||||
const nodeIssues = NodeHelpers.nodeIssuesToString(this.data.issues, this.data);
|
||||
|
||||
return `${this.$locale.baseText('node.issues')}:<br /> - ` + nodeIssues.join('<br /> - ');
|
||||
return NodeHelpers.nodeIssuesToString(this.data.issues, this.data);
|
||||
},
|
||||
nodeDisabledIcon (): string {
|
||||
if (this.data.disabled === false) {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
<div :class="$style.warning" v-if="issues.length">
|
||||
<n8n-tooltip placement="top" >
|
||||
<div slot="content" v-html="`${$locale.baseText('nodeCredentials.issues')}:<br /> - ` + issues.join('<br /> - ')"></div>
|
||||
<titled-list slot="content" :title="`${$locale.baseText('nodeCredentials.issues')}:`" :items="issues" />
|
||||
<font-awesome-icon icon="exclamation-triangle" />
|
||||
</n8n-tooltip>
|
||||
</div>
|
||||
@@ -74,6 +74,8 @@ import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||
import { showMessage } from '@/components/mixins/showMessage';
|
||||
|
||||
import TitledList from '@/components/TitledList.vue';
|
||||
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
@@ -89,6 +91,9 @@ export default mixins(
|
||||
'node', // INodeUi
|
||||
'overrideCredType', // cred type
|
||||
],
|
||||
components: {
|
||||
TitledList,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
NEW_CREDENTIALS_TEXT: `- ${this.$locale.baseText('nodeCredentials.createNew')} -`,
|
||||
|
||||
@@ -28,11 +28,14 @@
|
||||
</div>
|
||||
<div v-if="isCommunityNode" :class="$style.descriptionContainer">
|
||||
<div class="mb-l">
|
||||
<span
|
||||
v-html="$locale.baseText('nodeSettings.communityNodeUnknown.description', { interpolate: { packageName: node.type.split('.')[0] } })"
|
||||
@click="onMissingNodeTextClick"
|
||||
>
|
||||
</span>
|
||||
<i18n path="nodeSettings.communityNodeUnknown.description" tag="span" @click="onMissingNodeTextClick">
|
||||
<template #action>
|
||||
<a
|
||||
:href="`https://www.npmjs.com/package/${node.type.split('.')[0]}`"
|
||||
target="_blank"
|
||||
>{{ node.type.split('.')[0] }}</a>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<n8n-link
|
||||
:to="COMMUNITY_NODES_INSTALLATION_DOCS_URL"
|
||||
@@ -41,14 +44,15 @@
|
||||
{{ $locale.baseText('nodeSettings.communityNodeUnknown.installLink.text') }}
|
||||
</n8n-link>
|
||||
</div>
|
||||
<span v-else
|
||||
v-html="
|
||||
$locale.baseText('nodeSettings.nodeTypeUnknown.description',
|
||||
{
|
||||
interpolate: { docURL: CUSTOM_NODES_DOCS_URL }
|
||||
})
|
||||
">
|
||||
</span>
|
||||
<i18n v-else path="nodeSettings.nodeTypeUnknown.description" tag="span">
|
||||
<template #action>
|
||||
<a
|
||||
:href="CUSTOM_NODES_DOCS_URL"
|
||||
target="_blank"
|
||||
v-text="$locale.baseText('nodeSettings.nodeTypeUnknown.description.customNode')"
|
||||
/>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div class="node-parameters-wrapper" v-if="node && nodeValid">
|
||||
<div v-show="openPanel === 'params'">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div :class="$style['parameter-issues']" v-if="issues.length">
|
||||
<n8n-tooltip placement="top" >
|
||||
<div slot="content" v-html="`${$locale.baseText('parameterInput.issues')}:<br /> - ` + issues.join('<br /> - ')"></div>
|
||||
<titled-list slot="content" :title="`${$locale.baseText('parameterInput.issues')}:`" :items="issues" />
|
||||
<font-awesome-icon icon="exclamation-triangle" />
|
||||
</n8n-tooltip>
|
||||
</div>
|
||||
@@ -9,9 +9,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import TitledList from '@/components/TitledList.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'ParameterIssues',
|
||||
components: {
|
||||
TitledList,
|
||||
},
|
||||
props: [
|
||||
'issues',
|
||||
],
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
:disabled="!mappingEnabled || showHintWithDelay"
|
||||
:open-delay="1000"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
v-html="$locale.baseText('dataMapping.dragColumnToFieldHint')"
|
||||
></div>
|
||||
<div slot="content">
|
||||
<img src='/static/data-mapping-gif.gif'/>
|
||||
{{ $locale.baseText('dataMapping.dragColumnToFieldHint') }}
|
||||
</div>
|
||||
<Draggable
|
||||
type="mapping"
|
||||
:data="getExpression(column)"
|
||||
@@ -48,12 +48,11 @@
|
||||
:class="{
|
||||
[$style.header]: true,
|
||||
[$style.draggableHeader]: mappingEnabled,
|
||||
[$style.activeHeader]:
|
||||
(i === activeColumn || forceShowGrip) && mappingEnabled,
|
||||
[$style.activeHeader]: (i === activeColumn || forceShowGrip) && mappingEnabled,
|
||||
[$style.draggingHeader]: isDragging,
|
||||
}"
|
||||
>
|
||||
<span>
|
||||
<span>{{ column || ' ' }}</span>
|
||||
<n8n-tooltip
|
||||
v-if="mappingEnabled"
|
||||
placement="bottom-start"
|
||||
@@ -61,25 +60,22 @@
|
||||
:value="i === 0 && showHintWithDelay"
|
||||
>
|
||||
<div
|
||||
v-if="focusedMappableInput"
|
||||
slot="content"
|
||||
v-html="
|
||||
$locale.baseText(
|
||||
focusedMappableInput
|
||||
? 'dataMapping.tableHint'
|
||||
: 'dataMapping.dragColumnToFieldHint',
|
||||
{
|
||||
interpolate: { name: focusedMappableInput },
|
||||
},
|
||||
)
|
||||
$locale.baseText('dataMapping.tableHint', {
|
||||
interpolate: { name: focusedMappableInput },
|
||||
})
|
||||
"
|
||||
></div>
|
||||
<span>{{ column || ' ' }}</span>
|
||||
<div v-else slot="content">
|
||||
<img src='/static/data-mapping-gif.gif'/>
|
||||
{{ $locale.baseText('dataMapping.dragColumnToFieldHint') }}
|
||||
</div>
|
||||
<div :class="$style.dragButton">
|
||||
<font-awesome-icon icon="grip-vertical" />
|
||||
</div>
|
||||
</n8n-tooltip>
|
||||
<span v-else>{{ column || ' ' }}</span>
|
||||
</span>
|
||||
<div :class="$style.dragButton">
|
||||
<font-awesome-icon icon="grip-vertical" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
|
||||
39
packages/editor-ui/src/components/TitledList.vue
Normal file
39
packages/editor-ui/src/components/TitledList.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="titled-list">
|
||||
<p v-text="title" />
|
||||
<ul>
|
||||
<li v-for="item in items" class="titled-list-item" :key="item" v-text="item" />
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
name: "TitledList",
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.titled-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.titled-list-item {
|
||||
list-style: none;
|
||||
padding-left: var(--spacing-3xs);
|
||||
&::before {
|
||||
content: "- ";
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -55,7 +55,7 @@
|
||||
{{ header }}
|
||||
</n8n-heading>
|
||||
<n8n-text v-if="subheader">
|
||||
<span v-html="subheader"></span>
|
||||
<span v-text="subheader" />
|
||||
</n8n-text>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.timezone') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.timezone"></div>
|
||||
<div slot="content" v-text="helpTexts.timezone"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -51,7 +51,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.saveDataErrorExecution') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.saveDataErrorExecution"></div>
|
||||
<div slot="content" v-text="helpTexts.saveDataErrorExecution"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -70,7 +70,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.saveDataSuccessExecution') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.saveDataSuccessExecution"></div>
|
||||
<div slot="content" v-text="helpTexts.saveDataSuccessExecution"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -89,7 +89,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.saveManualExecutions') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.saveManualExecutions"></div>
|
||||
<div slot="content" v-text="helpTexts.saveManualExecutions"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -108,7 +108,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.saveExecutionProgress') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.saveExecutionProgress"></div>
|
||||
<div slot="content" v-text="helpTexts.saveExecutionProgress"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -127,7 +127,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.timeoutWorkflow') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.executionTimeoutToggle"></div>
|
||||
<div slot="content" v-text="helpTexts.executionTimeoutToggle"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
@@ -142,7 +142,7 @@
|
||||
<el-col :span="10" class="setting-name">
|
||||
{{ $locale.baseText('workflowSettings.timeoutAfter') + ":" }}
|
||||
<n8n-tooltip class="setting-info" placement="top" >
|
||||
<div slot="content" v-html="helpTexts.executionTimeout"></div>
|
||||
<div slot="content" v-text="helpTexts.executionTimeout"></div>
|
||||
<font-awesome-icon icon="question-circle" />
|
||||
</n8n-tooltip>
|
||||
</el-col>
|
||||
|
||||
Reference in New Issue
Block a user