diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-29 03:10:57 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-29 03:10:57 +0300 |
commit | e064cd3f07fbc239fb46106a1eaf71379ea01603 (patch) | |
tree | eb1f478d6f6d5e73bc50639f7af7a99b4d34a392 /app/assets/javascripts/behaviors | |
parent | 9037472904908109f5622a8a1e808c6225eced56 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/behaviors')
3 files changed, 145 insertions, 0 deletions
diff --git a/app/assets/javascripts/behaviors/components/json_table.vue b/app/assets/javascripts/behaviors/components/json_table.vue new file mode 100644 index 00000000000..bb38d80c1b5 --- /dev/null +++ b/app/assets/javascripts/behaviors/components/json_table.vue @@ -0,0 +1,71 @@ +<script> +import { GlTable, GlFormInput } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { + GlTable, + GlFormInput, + }, + props: { + fields: { + type: Array, + required: true, + }, + items: { + type: Array, + required: true, + }, + hasFilter: { + type: Boolean, + required: false, + default: false, + }, + caption: { + type: String, + required: false, + default: __('Generated with JSON data'), + }, + }, + data() { + return { + filterInput: '', + }; + }, + computed: { + cleanedFields() { + return this.fields.map((field) => { + if (typeof field === 'string') { + return field; + } + return { + key: field.key, + label: field.label, + sortable: field.sortable || false, + }; + }); + }, + }, +}; +</script> +<template> + <div class="gl-display-inline-block"> + <gl-form-input + v-if="hasFilter" + v-model="filterInput" + :placeholder="__('Type to search')" + class="gl-mb-2!" + /> + <gl-table + :fields="cleanedFields" + :items="items" + :filter="filterInput" + show-empty + class="gl-mt-0!" + > + <template v-if="caption" #table-caption> + <small>{{ caption }}</small> + </template> + </gl-table> + </div> +</template> diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js index c9ae3706383..ee5c0fe5ef3 100644 --- a/app/assets/javascripts/behaviors/markdown/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -5,6 +5,7 @@ import { renderKroki } from './render_kroki'; import renderMath from './render_math'; import renderSandboxedMermaid from './render_sandboxed_mermaid'; import renderMetrics from './render_metrics'; +import { renderJSONTable } from './render_json_table'; // Render GitLab flavoured Markdown // @@ -15,6 +16,9 @@ $.fn.renderGFM = function renderGFM() { renderKroki(this.find('.js-render-kroki[hidden]').get()); renderMath(this.find('.js-render-math')); renderSandboxedMermaid(this.find('.js-render-mermaid')); + renderJSONTable( + Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode), + ); highlightCurrentUser(this.find('.gfm-project_member').get()); diff --git a/app/assets/javascripts/behaviors/markdown/render_json_table.js b/app/assets/javascripts/behaviors/markdown/render_json_table.js new file mode 100644 index 00000000000..4d9ac1d266b --- /dev/null +++ b/app/assets/javascripts/behaviors/markdown/render_json_table.js @@ -0,0 +1,70 @@ +import { memoize } from 'lodash'; +import Vue from 'vue'; +import { __ } from '~/locale'; +import { createAlert } from '~/flash'; + +// Async import component since we might not need it... +const JSONTable = memoize(() => + import(/* webpackChunkName: 'gfm_json_table' */ '../components/json_table.vue'), +); + +const mountParseError = (element) => { + // Let the error container be a sibling to the element. + // Otherwise, dismissing the alert causes the copy button to be misplaced. + const container = document.createElement('div'); + element.insertAdjacentElement('beforebegin', container); + + // We need to create a child element with a known selector for `createAlert` + const el = document.createElement('div'); + el.classList.add('js-json-table-error'); + + container.insertAdjacentElement('afterbegin', el); + + return createAlert({ + message: __('Unable to parse JSON'), + variant: 'warning', + parent: container, + containerSelector: '.js-json-table-error', + }); +}; + +const mountJSONTableVueComponent = (userData, element) => { + const { fields = [], items = [], filter, caption } = userData; + + const container = document.createElement('div'); + element.innerHTML = ''; + element.appendChild(container); + + return new Vue({ + el: container, + render(h) { + return h(JSONTable, { + props: { + fields, + items, + hasFilter: filter, + caption, + }, + }); + }, + }); +}; + +const renderTable = (element) => { + // Avoid rendering multiple times + if (!element || element.classList.contains('js-json-table')) { + return; + } + + element.classList.add('js-json-table'); + + try { + mountJSONTableVueComponent(JSON.parse(element.textContent), element); + } catch (e) { + mountParseError(element); + } +}; + +export const renderJSONTable = (elements) => { + elements.forEach(renderTable); +}; |