Commit f344fa1d authored by Mark Florian's avatar Mark Florian

Merge branch 'papa-translations' into 'master'

Build papaparse alert component and translate errors

See merge request gitlab-org/gitlab!66683
parents 89dec6ec 91fd2b5d
<script> <script>
import { GlAlert, GlLoadingIcon, GlTable } from '@gitlab/ui'; import { GlLoadingIcon, GlTable } from '@gitlab/ui';
import Papa from 'papaparse'; import Papa from 'papaparse';
import PapaParseAlert from '~/vue_shared/components/papa_parse_alert.vue';
export default { export default {
components: { components: {
PapaParseAlert,
GlTable, GlTable,
GlAlert,
GlLoadingIcon, GlLoadingIcon,
}, },
props: { props: {
...@@ -17,7 +18,7 @@ export default { ...@@ -17,7 +18,7 @@ export default {
data() { data() {
return { return {
items: [], items: [],
errorMessage: null, papaParseErrors: [],
loading: true, loading: true,
}; };
}, },
...@@ -26,7 +27,7 @@ export default { ...@@ -26,7 +27,7 @@ export default {
this.items = parsed.data; this.items = parsed.data;
if (parsed.errors.length) { if (parsed.errors.length) {
this.errorMessage = parsed.errors.map((e) => e.message).join('. '); this.papaParseErrors = parsed.errors;
} }
this.loading = false; this.loading = false;
...@@ -40,9 +41,7 @@ export default { ...@@ -40,9 +41,7 @@ export default {
<gl-loading-icon class="gl-mt-5" size="lg" /> <gl-loading-icon class="gl-mt-5" size="lg" />
</div> </div>
<div v-else> <div v-else>
<gl-alert v-if="errorMessage" variant="danger" :dismissible="false"> <papa-parse-alert v-if="papaParseErrors.length" :papa-parse-errors="papaParseErrors" />
{{ errorMessage }}
</gl-alert>
<gl-table <gl-table
:empty-text="__('No CSV data to display.')" :empty-text="__('No CSV data to display.')"
:items="items" :items="items"
......
<script>
import { GlAlert } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
components: {
GlAlert,
},
i18n: {
genericErrorMessage: s__('CsvParser|Failed to render the CSV file for the following reasons:'),
MissingQuotes: s__('CsvParser|Quoted field unterminated'),
InvalidQuotes: s__('CsvParser|Trailing quote on quoted field is malformed'),
UndetectableDelimiter: s__('CsvParser|Unable to auto-detect delimiter; defaulted to ","'),
TooManyFields: s__('CsvParser|Too many fields'),
TooFewFields: s__('CsvParser|Too few fields'),
},
props: {
papaParseErrors: {
type: Array,
required: false,
default: () => [],
},
},
computed: {
errorMessages() {
const errorMessages = this.papaParseErrors.map(
(error) => this.$options.i18n[error.code] ?? error.message,
);
return new Set(errorMessages);
},
},
};
</script>
<template>
<gl-alert variant="danger" :dismissible="false">
{{ $options.i18n.genericErrorMessage }}
<ul class="gl-mb-0!">
<li v-for="error in errorMessages" :key="error">
{{ error }}
</li>
</ul>
</gl-alert>
</template>
...@@ -9642,6 +9642,24 @@ msgstr "" ...@@ -9642,6 +9642,24 @@ msgstr ""
msgid "Crowd" msgid "Crowd"
msgstr "" msgstr ""
msgid "CsvParser|Failed to render the CSV file for the following reasons:"
msgstr ""
msgid "CsvParser|Quoted field unterminated"
msgstr ""
msgid "CsvParser|Too few fields"
msgstr ""
msgid "CsvParser|Too many fields"
msgstr ""
msgid "CsvParser|Trailing quote on quoted field is malformed"
msgstr ""
msgid "CsvParser|Unable to auto-detect delimiter; defaulted to \",\""
msgstr ""
msgid "Current" msgid "Current"
msgstr "" msgstr ""
......
import { GlAlert, GlLoadingIcon, GlTable } from '@gitlab/ui'; import { GlLoadingIcon, GlTable } from '@gitlab/ui';
import { getAllByRole } from '@testing-library/dom'; import { getAllByRole } from '@testing-library/dom';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import CSVViewer from '~/blob/csv/csv_viewer.vue'; import CsvViewer from '~/blob/csv/csv_viewer.vue';
import PapaParseAlert from '~/vue_shared/components/papa_parse_alert.vue';
const validCsv = 'one,two,three'; const validCsv = 'one,two,three';
const brokenCsv = '{\n "json": 1,\n "key": [1, 2, 3]\n}'; const brokenCsv = '{\n "json": 1,\n "key": [1, 2, 3]\n}';
...@@ -11,7 +12,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { ...@@ -11,7 +12,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => {
let wrapper; let wrapper;
const createComponent = ({ csv = validCsv, mountFunction = shallowMount } = {}) => { const createComponent = ({ csv = validCsv, mountFunction = shallowMount } = {}) => {
wrapper = mountFunction(CSVViewer, { wrapper = mountFunction(CsvViewer, {
propsData: { propsData: {
csv, csv,
}, },
...@@ -20,7 +21,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { ...@@ -20,7 +21,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => {
const findCsvTable = () => wrapper.findComponent(GlTable); const findCsvTable = () => wrapper.findComponent(GlTable);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(PapaParseAlert);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -35,12 +36,12 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { ...@@ -35,12 +36,12 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => {
}); });
describe('when the CSV contains errors', () => { describe('when the CSV contains errors', () => {
it('should render alert', async () => { it('should render alert with correct props', async () => {
createComponent({ csv: brokenCsv }); createComponent({ csv: brokenCsv });
await nextTick; await nextTick;
expect(findAlert().props()).toMatchObject({ expect(findAlert().props()).toMatchObject({
variant: 'danger', papaParseErrors: [{ code: 'UndetectableDelimiter' }],
}); });
}); });
}); });
......
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import PapaParseAlert from '~/vue_shared/components/papa_parse_alert.vue';
describe('app/assets/javascripts/vue_shared/components/papa_parse_alert.vue', () => {
let wrapper;
const createComponent = ({ errorMessages } = {}) => {
wrapper = shallowMount(PapaParseAlert, {
propsData: {
papaParseErrors: errorMessages,
},
});
};
const findAlert = () => wrapper.findComponent(GlAlert);
afterEach(() => {
wrapper.destroy();
});
it('should render alert with correct props', async () => {
createComponent({ errorMessages: [{ code: 'MissingQuotes' }] });
await nextTick;
expect(findAlert().props()).toMatchObject({
variant: 'danger',
});
expect(findAlert().text()).toContain(
'Failed to render the CSV file for the following reasons:',
);
expect(findAlert().text()).toContain('Quoted field unterminated');
});
it('should render original message if no translation available', async () => {
createComponent({
errorMessages: [{ code: 'NotDefined', message: 'Error code is undefined' }],
});
await nextTick;
expect(findAlert().text()).toContain('Error code is undefined');
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment