Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-21 15:21:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-21 15:21:08 +0300
commite7198b914bf1d6594909e35d3d00d0a0b260f250 (patch)
tree6fe636fedeae9c679839d5bf27dc091af037e765 /app/assets
parent3b80f22aba42e3e424de5c3dd15cc11f96aaac65 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/behaviors/components/json_table.vue1
-rw-r--r--app/assets/javascripts/notebook/cells/output/dataframe_util.js94
-rw-r--r--app/assets/javascripts/pages/import/phabricator/new/index.js3
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/file_finder/index.vue9
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue17
6 files changed, 106 insertions, 23 deletions
diff --git a/app/assets/javascripts/behaviors/components/json_table.vue b/app/assets/javascripts/behaviors/components/json_table.vue
index bb38d80c1b5..9cbaa02f270 100644
--- a/app/assets/javascripts/behaviors/components/json_table.vue
+++ b/app/assets/javascripts/behaviors/components/json_table.vue
@@ -42,6 +42,7 @@ export default {
key: field.key,
label: field.label,
sortable: field.sortable || false,
+ class: field.class || [],
};
});
},
diff --git a/app/assets/javascripts/notebook/cells/output/dataframe_util.js b/app/assets/javascripts/notebook/cells/output/dataframe_util.js
index 2fdaaced0b9..41149875d6c 100644
--- a/app/assets/javascripts/notebook/cells/output/dataframe_util.js
+++ b/app/assets/javascripts/notebook/cells/output/dataframe_util.js
@@ -1,5 +1,70 @@
import { sanitize } from '~/lib/dompurify';
+function parseItems(itemIndexes, itemColumns) {
+ // Fetching items: if the dataframe has a single column index, the table is simple
+ // 0: tr > th(index0 value) th(column0 value) th(column1 value)
+ // 1: tr > th(index0 value) th(column0 value) th(column1 value)
+ //
+ // But if the dataframe has multiple column indexes, it uses rowspan, and the row below won't have a value for that
+ // index.
+ // 0: tr > th(index0 value, rowspan=2) th(index1 value) th(column0 value) th(column1 value)
+ // 1: tr > th(index1 value) th(column0 value) th(column1 value)
+ //
+ // So, when parsing row 1, and the count of <th> elements is less than indexCount, we fill with the first
+ // values of row 0
+ const indexCount = itemIndexes[0].length;
+ const rowCount = itemIndexes.length;
+
+ const filledIndexes = itemIndexes.map((row, rowIndex) => {
+ const indexesInRow = row.length;
+ if (indexesInRow === indexCount) {
+ return row;
+ }
+ return itemIndexes[rowIndex - 1].slice(0, -indexesInRow).concat(row);
+ });
+
+ const items = Array(rowCount);
+
+ for (let row = 0; row < rowCount; row += 1) {
+ items[row] = {
+ ...Object.fromEntries(filledIndexes[row].map((value, counter) => [`index${counter}`, value])),
+ ...Object.fromEntries(itemColumns[row].map((value, counter) => [`column${counter}`, value])),
+ };
+ }
+ return items;
+}
+
+function labelsToFields(labels, isIndex = true) {
+ return labels.map((label, counter) => ({
+ key: isIndex ? `index${counter}` : `column${counter}`,
+ label,
+ sortable: true,
+ class: isIndex ? 'gl-font-weight-bold' : '',
+ }));
+}
+
+function parseFields(columnAndIndexLabels, indexCount, columnCount) {
+ // Fetching the labels: if the dataframe has a single column index, it will be in the format:
+ // thead
+ // tr
+ // th(index0 label) th(column0 label) th(column1 label)
+ //
+ // If there are multiple index columns, it the header will actually have two rows:
+ // thead
+ // tr
+ // th() th() th(column 0 label) th(column1 label)
+ // tr
+ // th(index0 label) th(index1 label) th() th()
+
+ const columnLabels = columnAndIndexLabels[0].slice(-columnCount);
+ const indexLabels = columnAndIndexLabels[columnAndIndexLabels.length - 1].slice(0, indexCount);
+
+ const indexFields = labelsToFields(indexLabels, true);
+ const columnFields = labelsToFields(columnLabels, false);
+
+ return [...indexFields, ...columnFields];
+}
+
/**
* Converts a dataframe in the output of a Jupyter Notebook cell to a json object
*
@@ -13,27 +78,26 @@ export function convertHtmlTableToJson(input, domParser) {
if (!htmlDoc) return { fields: [], items: [] };
- const columnNames = [...htmlDoc.querySelectorAll('table > thead th')].map(
- (head) => head.innerText,
+ const columnAndIndexLabels = [...htmlDoc.querySelectorAll('table > thead tr')].map((row) =>
+ [...row.querySelectorAll('th')].map((item) => item.innerText),
);
- if (!columnNames) return { fields: [], items: [] };
+ if (columnAndIndexLabels.length === 0) return { fields: [], items: [] };
- const itemValues = [...htmlDoc.querySelectorAll('table > tbody > tr')].map((row) =>
+ const tableRows = [...htmlDoc.querySelectorAll('table > tbody > tr')];
+
+ const itemColumns = tableRows.map((row) =>
[...row.querySelectorAll('td')].map((item) => item.innerText),
);
- return {
- fields: columnNames.map((column) => ({
- key: column === '' ? 'index' : column,
- label: column,
- sortable: true,
- })),
- items: itemValues.map((values, itemIndex) => ({
- index: itemIndex,
- ...Object.fromEntries(values.map((value, index) => [columnNames[index + 1], value])),
- })),
- };
+ const itemIndexes = tableRows.map((row) =>
+ [...row.querySelectorAll('th')].map((item) => item.innerText),
+ );
+
+ const fields = parseFields(columnAndIndexLabels, itemIndexes[0].length, itemColumns[0].length);
+ const items = parseItems(itemIndexes, itemColumns);
+
+ return { fields, items };
}
export function isDataframe(output) {
diff --git a/app/assets/javascripts/pages/import/phabricator/new/index.js b/app/assets/javascripts/pages/import/phabricator/new/index.js
deleted file mode 100644
index 0bb70a7364e..00000000000
--- a/app/assets/javascripts/pages/import/phabricator/new/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { initNewProjectUrlSelect } from '~/projects/new';
-
-initNewProjectUrlSelect();
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index 1d5ff5eb16f..d46e9983a44 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -158,6 +158,8 @@ export const LICENSE_COMPLIANCE_HELP_PATH = helpPagePath(
'user/compliance/license_compliance/index',
);
+export const CLUSTER_IMAGE_SCANNING_NAME = s__('ciReport|Cluster Image Scanning');
+
export const SCANNER_NAMES_MAP = {
SAST: SAST_SHORT_NAME,
SAST_IAC: SAST_IAC_NAME,
@@ -167,7 +169,8 @@ export const SCANNER_NAMES_MAP = {
COVERAGE_FUZZING: COVERAGE_FUZZING_NAME,
SECRET_DETECTION: SECRET_DETECTION_NAME,
DEPENDENCY_SCANNING: DEPENDENCY_SCANNING_NAME,
- BAS: BAS_SHORT_NAME,
+ BREACH_AND_ATTACK_SIMULATION: BAS_NAME,
+ CLUSTER_IMAGE_SCANNING: CLUSTER_IMAGE_SCANNING_NAME,
GENERIC: s__('ciReport|Manually added'),
};
diff --git a/app/assets/javascripts/vue_shared/components/file_finder/index.vue b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
index 680f229d5e8..5c892377f40 100644
--- a/app/assets/javascripts/vue_shared/components/file_finder/index.vue
+++ b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
@@ -221,7 +221,12 @@ export default {
</script>
<template>
- <div v-if="visible" class="file-finder-overlay" @mousedown.self="toggle(false)">
+ <div
+ v-if="visible"
+ data-testid="overlay"
+ class="file-finder-overlay"
+ @mousedown.self="toggle(false)"
+ >
<div class="dropdown-menu diff-file-changes file-finder show">
<div :class="{ 'has-value': showClearInputButton }" class="dropdown-input">
<input
@@ -231,6 +236,7 @@ export default {
type="search"
class="dropdown-input-field"
autocomplete="off"
+ data-testid="search-input"
@keydown="onKeydown($event)"
@keyup="onKeyup($event)"
/>
@@ -241,6 +247,7 @@ export default {
/>
<gl-icon
name="close"
+ data-testid="clear-search-input"
class="dropdown-input-clear"
role="button"
:aria-label="__('Clear search input')"
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 361a9ad477b..0e11c04afb1 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -13,6 +13,7 @@ import {
} from '@gitlab/ui';
import noAccessSvg from '@gitlab/svgs/dist/illustrations/analytics/no-access.svg';
import * as Sentry from '@sentry/browser';
+import { fetchPolicies } from '~/lib/graphql';
import { s__ } from '~/locale';
import { parseBoolean } from '~/lib/utils/common_utils';
import { getParameterByName, updateHistory, setUrlParams } from '~/lib/utils/url_utility';
@@ -149,7 +150,13 @@ export default {
error() {
this.setEmptyState();
},
- result() {
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ notifyOnNetworkStatusChange: true,
+ result(res) {
+ // need to handle this when the res is loading: true, netWorkStatus: 1, partial: true
+ if (!res.data) {
+ return;
+ }
if (isEmpty(this.workItem)) {
this.setEmptyState();
}
@@ -211,7 +218,7 @@ export default {
},
computed: {
workItemLoading() {
- return this.$apollo.queries.workItem.loading;
+ return isEmpty(this.workItem) && this.$apollo.queries.workItem.loading;
},
workItemType() {
return this.workItem.workItemType?.name;
@@ -544,7 +551,11 @@ export default {
{{ workItemBreadcrumbReference }}
</li>
</ul>
- <div v-else-if="!error" class="gl-mr-auto" data-testid="work-item-type">
+ <div
+ v-else-if="!error && !workItemLoading"
+ class="gl-mr-auto"
+ data-testid="work-item-type"
+ >
<work-item-type-icon
:work-item-icon-name="workItemIconName"
:work-item-type="workItemType && workItemType.toUpperCase()"