diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components')
40 files changed, 375 insertions, 161 deletions
diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue index 52a5d6e1b86..7b5ded9348f 100644 --- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue +++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue @@ -71,7 +71,7 @@ export default { <ci-icon :status="status" /> <template v-if="showText"> - <span class="gl-ml-2">{{ status.text }}</span> + <span class="gl-ml-2 gl-white-space-nowrap">{{ status.text }}</span> </template> </gl-link> </template> diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue index fb7105bd416..c89e843b660 100644 --- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue +++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue @@ -1,13 +1,11 @@ <script> import { GlAreaChart } from '@gitlab/ui/dist/charts'; -import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; import { CHART_CONTAINER_HEIGHT } from './constants'; export default { name: 'CiCdAnalyticsAreaChart', components: { GlAreaChart, - ResizableChartContainer, }, props: { chartData: { @@ -27,24 +25,21 @@ export default { <p> <slot></slot> </p> - <resizable-chart-container> - <template #default="{ width }"> - <gl-area-chart - v-bind="$attrs" - :width="width" - :height="$options.chartContainerHeight" - :data="chartData" - :include-legend-avg-max="false" - :option="areaChartOptions" - > - <template #tooltip-title> - <slot name="tooltip-title"></slot> - </template> - <template #tooltip-content> - <slot name="tooltip-content"></slot> - </template> - </gl-area-chart> + <gl-area-chart + v-bind="$attrs" + responsive + width="auto" + :height="$options.chartContainerHeight" + :data="chartData" + :include-legend-avg-max="false" + :option="areaChartOptions" + > + <template #tooltip-title> + <slot name="tooltip-title"></slot> </template> - </resizable-chart-container> + <template #tooltip-content> + <slot name="tooltip-content"></slot> + </template> + </gl-area-chart> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue index 75386a3cd01..2f28ae5e0e2 100644 --- a/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue +++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/color_select_root.vue @@ -1,6 +1,6 @@ <script> import { isString } from 'lodash'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { s__ } from '~/locale'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import { DEFAULT_COLOR, COLOR_WIDGET_COLOR, DROPDOWN_VARIANT, ISSUABLE_COLORS } from './constants'; diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue index d0a634d8e54..65a601ed927 100644 --- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue +++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue @@ -60,13 +60,11 @@ export default { actionPrimary() { return { text: this.confirmButtonText, - attributes: [ - { - variant: 'danger', - disabled: !this.isValid, - 'data-qa-selector': 'confirm_danger_modal_button', - }, - ], + attributes: { + variant: 'danger', + disabled: !this.isValid, + 'data-qa-selector': 'confirm_danger_modal_button', + }, }; }, actionCancel() { diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue index dfeb12d5cf5..721f87ff4d6 100644 --- a/app/assets/javascripts/vue_shared/components/file_row.vue +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -168,7 +168,7 @@ export default { .file-row { display: flex; align-items: center; - height: 32px; + height: var(--file-row-height, 32px); padding: 4px 8px; margin-left: -8px; margin-right: -8px; diff --git a/app/assets/javascripts/vue_shared/components/file_row_header.vue b/app/assets/javascripts/vue_shared/components/file_row_header.vue index 5afb2408c7e..b436872e463 100644 --- a/app/assets/javascripts/vue_shared/components/file_row_header.vue +++ b/app/assets/javascripts/vue_shared/components/file_row_header.vue @@ -15,7 +15,7 @@ export default { </script> <template> - <div class="file-row-header bg-white sticky-top p-2 js-file-row-header" :title="path"> + <div class="file-row-header bg-white sticky-top gl-px-2 js-file-row-header" :title="path"> <gl-truncate :text="path" position="middle" class="bold" /> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue index 34f64dddc41..fe4f2d407f7 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue @@ -12,7 +12,7 @@ import { import RecentSearchesStorageKeys from 'ee_else_ce/filtered_search/recent_searches_storage_keys'; import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import { SORT_DIRECTION } from './constants'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/store/modules/filters/actions.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/store/modules/filters/actions.js index 8a6053b7001..f3d46de3437 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/store/modules/filters/actions.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/store/modules/filters/actions.js @@ -1,5 +1,5 @@ import Api from '~/api'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import * as types from './mutation_types'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue index 741395b3193..fff8a95c193 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue @@ -1,6 +1,6 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue index c8aeac75645..63ffded9e8e 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue @@ -1,10 +1,10 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { ITEM_TYPE } from '~/groups/constants'; import { TYPENAME_CRM_CONTACT } from '~/graphql_shared/constants'; import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; +import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants'; import { isPositiveInteger } from '~/lib/utils/number_utils'; import { __ } from '~/locale'; import searchCrmContactsQuery from '../queries/search_crm_contacts.query.graphql'; @@ -43,7 +43,7 @@ export default { return this.config.defaultContacts || OPTIONS_NONE_ANY; }, namespace() { - return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP; + return this.config.isProject ? WORKSPACE_PROJECT : WORKSPACE_GROUP; }, }, methods: { diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue index ff0571031b5..126066fbbbe 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue @@ -1,10 +1,10 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { ITEM_TYPE } from '~/groups/constants'; import { TYPENAME_CRM_ORGANIZATION } from '~/graphql_shared/constants'; import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; +import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants'; import { isPositiveInteger } from '~/lib/utils/number_utils'; import { __ } from '~/locale'; import searchCrmOrganizationsQuery from '../queries/search_crm_organizations.query.graphql'; @@ -43,7 +43,7 @@ export default { return this.config.defaultOrganizations || OPTIONS_NONE_ANY; }, namespace() { - return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP; + return this.config.isProject ? WORKSPACE_PROJECT : WORKSPACE_GROUP; }, }, methods: { diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue index 9c30ec67d5a..c69a2927ec9 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue @@ -1,6 +1,6 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import { OPTIONS_NONE_ANY } from '../constants'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue index 9449e071a0d..6a7dd6131e2 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue @@ -1,7 +1,7 @@ <script> import { GlToken, GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue index b9ee4d51db1..81b8a6c78fc 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue @@ -1,6 +1,6 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import { sortMilestonesByDueDate } from '~/milestones/utils'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue index 6d681aab3ca..a251035b683 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue @@ -1,6 +1,6 @@ <script> import { GlFilteredSearchSuggestion } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import { OPTIONS_NONE_ANY } from '../constants'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue index 28e65c1185f..c294c23abfc 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue @@ -1,7 +1,7 @@ <script> import { GlAvatar, GlFilteredSearchSuggestion } from '@gitlab/ui'; import { compact } from 'lodash'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import { OPTIONS_NONE_ANY } from '../constants'; diff --git a/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue index 26c50345c19..fe221d2fefa 100644 --- a/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue +++ b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue @@ -1,5 +1,4 @@ -<!-- eslint-disable-next-line vue/no-deprecated-functional-template --> -<template functional> +<template> <footer class="form-actions d-flex justify-content-between"> <div><slot name="prepend"></slot></div> <div><slot></slot></div> diff --git a/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/constants.js b/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/constants.js index 9a88ab44f3d..e3eacf4495d 100644 --- a/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/constants.js +++ b/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/constants.js @@ -1,5 +1,4 @@ -import { issuableTypes } from '~/boards/constants'; -import { TYPE_ISSUE } from '~/issues/constants'; +import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants'; import blockingIssuesQuery from './graphql/blocking_issues.query.graphql'; import blockingEpicsQuery from './graphql/blocking_epics.query.graphql'; @@ -7,7 +6,7 @@ export const blockingIssuablesQueries = { [TYPE_ISSUE]: { query: blockingIssuesQuery, }, - [issuableTypes.epic]: { + [TYPE_EPIC]: { query: blockingEpicsQuery, }, }; diff --git a/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue b/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue index f5b4870d59f..7bea4409c03 100644 --- a/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue +++ b/app/assets/javascripts/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue @@ -1,9 +1,8 @@ <script> import { GlIcon, GlLink, GlPopover, GlLoadingIcon } from '@gitlab/ui'; -import { issuableTypes } from '~/boards/constants'; import { TYPENAME_ISSUE, TYPENAME_EPIC } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { TYPE_ISSUE } from '~/issues/constants'; +import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants'; import { truncate } from '~/lib/utils/text_utility'; import { __, n__, s__, sprintf } from '~/locale'; import { blockingIssuablesQueries } from './constants'; @@ -12,12 +11,12 @@ export default { i18n: { issuableType: { [TYPE_ISSUE]: __('issue'), - [issuableTypes.epic]: __('epic'), + [TYPE_EPIC]: __('epic'), }, }, graphQLIdType: { [TYPE_ISSUE]: TYPENAME_ISSUE, - [issuableTypes.epic]: TYPENAME_EPIC, + [TYPE_EPIC]: TYPENAME_EPIC, }, referenceFormatter: { [TYPE_ISSUE]: (r) => r.split('/')[1], @@ -43,7 +42,7 @@ export default { type: String, required: true, validator(value) { - return [TYPE_ISSUE, issuableTypes.epic].includes(value); + return [TYPE_ISSUE, TYPE_EPIC].includes(value); }, }, }, @@ -88,7 +87,7 @@ export default { }, computed: { isEpic() { - return this.issuableType === issuableTypes.epic; + return this.issuableType === TYPE_EPIC; }, displayedIssuables() { const { defaultDisplayLimit, referenceFormatter } = this.$options; diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue index bc6b5d3176f..0f8ff5291a4 100644 --- a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue +++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue @@ -1,5 +1,5 @@ <script> -import { GlFormGroup, GlListbox } from '@gitlab/ui'; +import { GlFormGroup, GlCollapsibleListbox } from '@gitlab/ui'; import { __ } from '~/locale'; const MIN_ITEMS_COUNT_FOR_SEARCHING = 10; @@ -10,9 +10,9 @@ export default { }, components: { GlFormGroup, - GlListbox, + GlCollapsibleListbox, }, - model: GlListbox.model, + model: GlCollapsibleListbox.model, props: { label: { type: String, @@ -39,7 +39,7 @@ export default { default: null, }, items: { - type: GlListbox.props.items.type, + type: GlCollapsibleListbox.props.items.type, required: true, }, disabled: { @@ -116,7 +116,7 @@ export default { <template> <component :is="wrapperComponent" :label="label" :description="description" v-bind="$attrs"> - <gl-listbox + <gl-collapsible-listbox :selected="selected" :toggle-text="toggleText" :items="filteredItems" diff --git a/app/assets/javascripts/vue_shared/components/markdown/drawio_toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/drawio_toolbar_button.vue new file mode 100644 index 00000000000..a66becb5c92 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/markdown/drawio_toolbar_button.vue @@ -0,0 +1,48 @@ +<script> +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; +import { launchDrawioEditor } from '~/drawio/drawio_editor'; +import { create } from '~/drawio/markdown_field_editor_facade'; + +export default { + components: { + GlButton, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + uploadsPath: { + type: String, + required: true, + }, + markdownPreviewPath: { + type: String, + required: true, + }, + }, + methods: { + getTextArea() { + return document.querySelector('.js-gfm-input'); + }, + launchDrawioEditor() { + launchDrawioEditor({ + editorFacade: create({ + uploadsPath: this.uploadsPath, + textArea: this.getTextArea(), + markdownPreviewPath: this.markdownPreviewPath, + }), + }); + }, + }, +}; +</script> +<template> + <gl-button + v-gl-tooltip + :title="__('Insert or edit diagram')" + :aria-label="__('Insert or edit diagram')" + category="tertiary" + icon="diagram" + @click="launchDrawioEditor" + /> +</template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/editor_mode_dropdown.vue b/app/assets/javascripts/vue_shared/components/markdown/editor_mode_dropdown.vue index 6702a81e747..9ebf782a1d9 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/editor_mode_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/editor_mode_dropdown.vue @@ -23,7 +23,7 @@ export default { return this.value === 'markdown'; }, text() { - return this.markdownEditorSelected ? __('View rich text') : __('View markdown'); + return this.markdownEditorSelected ? __('Viewing markdown') : __('Viewing rich text'); }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 6f4cddbdfa2..9623c51d51c 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -2,7 +2,7 @@ import { GlIcon } from '@gitlab/ui'; import $ from 'jquery'; import { debounce, unescape } from 'lodash'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import GLForm from '~/gl_form'; import SafeHtml from '~/vue_shared/directives/safe_html'; import axios from '~/lib/utils/axios_utils'; @@ -132,6 +132,11 @@ export default { required: false, default: false, }, + drawioEnabled: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -355,6 +360,10 @@ export default { :enable-preview="enablePreview" :show-suggest-popover="showSuggestPopover" :suggestion-start-index="suggestionsStartIndex" + :uploads-path="uploadsPath" + :markdown-preview-path="markdownPreviewPath" + :drawio-enabled="drawioEnabled" + data-testid="markdownHeader" :restricted-tool-bar-items="restrictedToolBarItems" @preview-markdown="showPreviewTab" @write-markdown="showWriteTab" diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index e83441e59a2..eeeb0fce55d 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -10,11 +10,14 @@ import { INDENT_LINE, OUTDENT_LINE, } from '~/behaviors/shortcuts/keybindings'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { getModifierKey } from '~/constants'; import { getSelectedFragment } from '~/lib/utils/common_utils'; import { s__, __ } from '~/locale'; import { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm'; import ToolbarButton from './toolbar_button.vue'; +import DrawioToolbarButton from './drawio_toolbar_button.vue'; +import SavedRepliesDropdown from './saved_replies_dropdown.vue'; export default { components: { @@ -23,10 +26,18 @@ export default { GlButton, GlTabs, GlTab, + DrawioToolbarButton, + SavedRepliesDropdown, }, directives: { GlTooltip: GlTooltipDirective, }, + mixins: [glFeatureFlagsMixin()], + inject: { + newSavedRepliesPath: { + default: null, + }, + }, props: { previewMarkdown: { type: Boolean, @@ -62,6 +73,21 @@ export default { required: false, default: () => [], }, + uploadsPath: { + type: String, + required: false, + default: '', + }, + markdownPreviewPath: { + type: String, + required: false, + default: '', + }, + drawioEnabled: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -369,6 +395,15 @@ export default { icon="paperclip" @click="handleAttachFile" /> + <drawio-toolbar-button + v-if="drawioEnabled" + :uploads-path="uploadsPath" + :markdown-preview-path="markdownPreviewPath" + /> + <saved-replies-dropdown + v-if="newSavedRepliesPath && glFeatures.savedReplies" + :new-saved-replies-path="newSavedRepliesPath" + /> <toolbar-button v-if="!restrictedToolBarItems.includes('full-screen')" class="js-zen-enter" diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue index 7e6b0e4a63b..93583907a11 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue @@ -1,6 +1,8 @@ <script> +import Autosize from 'autosize'; import axios from '~/lib/utils/axios_utils'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; +import { updateDraft, clearDraft, getDraft } from '~/lib/utils/autosave'; import { EDITING_MODE_MARKDOWN_FIELD, EDITING_MODE_CONTENT_EDITOR } from '../../constants'; import MarkdownField from './field.vue'; @@ -22,15 +24,6 @@ export default { type: String, required: true, }, - markdownDocsPath: { - type: String, - required: true, - }, - quickActionsDocsPath: { - type: String, - required: false, - default: '', - }, uploadsPath: { type: String, required: false, @@ -41,21 +34,6 @@ export default { required: false, default: true, }, - enablePreview: { - type: Boolean, - required: false, - default: true, - }, - autocompleteDataSources: { - type: Object, - required: false, - default: () => ({}), - }, - enableAutocomplete: { - type: Boolean, - required: false, - default: true, - }, formFieldProps: { type: Object, required: true, @@ -71,7 +49,22 @@ export default { required: false, default: false, }, - useBottomToolbar: { + autosaveKey: { + type: String, + required: false, + default: null, + }, + quickActionsDocsPath: { + type: String, + required: false, + default: '', + }, + drawioEnabled: { + type: Boolean, + required: false, + default: false, + }, + disabled: { type: Boolean, required: false, default: false, @@ -79,6 +72,7 @@ export default { }, data() { return { + markdown: this.value || (this.autosaveKey ? getDraft(this.autosaveKey) : '') || '', editingMode: EDITING_MODE_MARKDOWN_FIELD, autofocused: false, }; @@ -92,15 +86,32 @@ export default { return this.autofocus && !this.autofocused ? 'end' : false; }, }, + watch: { + value(val) { + this.markdown = val; + + this.saveDraft(); + this.autosizeTextarea(); + }, + }, mounted() { this.autofocusTextarea(); + + this.saveDraft(); }, methods: { updateMarkdownFromContentEditor({ markdown }) { + this.markdown = markdown; this.$emit('input', markdown); + + this.saveDraft(); }, updateMarkdownFromMarkdownField({ target }) { + this.markdown = target.value; this.$emit('input', target.value); + + this.saveDraft(); + this.autosizeTextarea(); }, renderMarkdown(markdown) { return axios.post(this.renderMarkdownPath, { text: markdown }).then(({ data }) => data.body); @@ -126,6 +137,23 @@ export default { setEditorAsAutofocused() { this.autofocused = true; }, + saveDraft() { + if (!this.autosaveKey) return; + if (this.markdown) updateDraft(this.autosaveKey, this.markdown); + else clearDraft(this.autosaveKey); + }, + togglePreview(value) { + if (this.editingMode === EDITING_MODE_MARKDOWN_FIELD) { + this.$refs.markdownField.previewMarkdown = value; + } + }, + autosizeTextarea() { + if (this.editingMode === EDITING_MODE_MARKDOWN_FIELD) { + this.$nextTick(() => { + Autosize.update(this.$refs.textarea); + }); + } + }, }, }; </script> @@ -138,16 +166,16 @@ export default { /> <markdown-field v-if="!isContentEditorActive" + ref="markdownField" + v-bind="$attrs" + data-testid="markdown-field" :markdown-preview-path="renderMarkdownPath" can-attach-file - :enable-autocomplete="enableAutocomplete" - :textarea-value="value" - :markdown-docs-path="markdownDocsPath" - :quick-actions-docs-path="quickActionsDocsPath" - :autocomplete-data-sources="autocompleteDataSources" + :textarea-value="markdown" :uploads-path="uploadsPath" - :enable-preview="enablePreview" - show-content-editor-switcher + :quick-actions-docs-path="quickActionsDocsPath" + :show-content-editor-switcher="enableContentEditor" + :drawio-enabled="drawioEnabled" class="bordered-box" @enableContentEditor="onEditingModeChange('contentEditor')" > @@ -155,11 +183,12 @@ export default { <textarea v-bind="formFieldProps" ref="textarea" - :value="value" - class="note-textarea js-gfm-input js-autosize markdown-area" + :value="markdown" + class="note-textarea js-gfm-input markdown-area" dir="auto" :data-supports-quick-actions="supportsQuickActions" - data-qa-selector="markdown_editor_form_field" + :data-qa-selector="formFieldProps['data-qa-selector'] || 'markdown_editor_form_field'" + :disabled="disabled" @input="updateMarkdownFromMarkdownField" @keydown="$emit('keydown', $event)" > @@ -168,11 +197,15 @@ export default { </markdown-field> <div v-else> <content-editor + ref="contentEditor" :render-markdown="renderMarkdown" :uploads-path="uploadsPath" - :markdown="value" + :markdown="markdown" + :quick-actions-docs-path="quickActionsDocsPath" :autofocus="contentEditorAutofocused" - :use-bottom-toolbar="useBottomToolbar" + :placeholder="formFieldProps.placeholder" + :drawio-enabled="drawioEnabled" + :editable="!disabled" @initialized="setEditorAsAutofocused" @change="updateMarkdownFromContentEditor" @keydown="$emit('keydown', $event)" @@ -180,7 +213,7 @@ export default { /> <input v-bind="formFieldProps" - :value="value" + :value="markdown" data-qa-selector="markdown_editor_form_field" type="hidden" /> diff --git a/app/assets/javascripts/vue_shared/components/markdown/saved_replies.query.graphql b/app/assets/javascripts/vue_shared/components/markdown/saved_replies.query.graphql new file mode 100644 index 00000000000..9b9d4c89254 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/markdown/saved_replies.query.graphql @@ -0,0 +1,12 @@ +query getSavedReplies { + currentUser { + id + savedReplies { + nodes { + id + name + content + } + } + } +} diff --git a/app/assets/javascripts/vue_shared/components/markdown/saved_replies_dropdown.vue b/app/assets/javascripts/vue_shared/components/markdown/saved_replies_dropdown.vue new file mode 100644 index 00000000000..989b14f8711 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/markdown/saved_replies_dropdown.vue @@ -0,0 +1,120 @@ +<script> +import { GlCollapsibleListbox, GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui'; +import fuzzaldrinPlus from 'fuzzaldrin-plus'; +import savedRepliesQuery from './saved_replies.query.graphql'; + +export default { + apollo: { + savedReplies: { + query: savedRepliesQuery, + update: (r) => r.currentUser?.savedReplies?.nodes, + skip() { + return !this.shouldFetchSavedReplies; + }, + }, + }, + components: { + GlCollapsibleListbox, + GlIcon, + GlButton, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + newSavedRepliesPath: { + type: String, + required: true, + }, + }, + data() { + return { + shouldFetchSavedReplies: false, + savedReplies: [], + savedRepliesSearch: '', + loadingSavedReplies: false, + }; + }, + computed: { + filteredSavedReplies() { + const savedReplies = this.savedRepliesSearch + ? fuzzaldrinPlus.filter(this.savedReplies, this.savedRepliesSearch, { key: ['name'] }) + : this.savedReplies; + + return savedReplies.map((r) => ({ value: r.id, text: r.name, content: r.content })); + }, + }, + methods: { + fetchSavedReplies() { + this.shouldFetchSavedReplies = true; + }, + setSavedRepliesSearch(search) { + this.savedRepliesSearch = search; + }, + }, +}; +</script> + +<template> + <gl-collapsible-listbox + :header-text="__('Insert saved reply')" + :items="filteredSavedReplies" + placement="right" + searchable + class="saved-replies-dropdown" + :searching="$apollo.queries.savedReplies.loading" + @shown="fetchSavedReplies" + @search="setSavedRepliesSearch" + > + <template #toggle> + <gl-button + v-gl-tooltip + :title="__('Insert saved reply')" + :aria-label="__('Insert saved reply')" + category="tertiary" + class="gl-px-3!" + data-testid="saved-replies-dropdown-toggle" + > + <gl-icon name="symlink" class="gl-mr-0!" /> + <gl-icon name="chevron-down" /> + </gl-button> + </template> + <template #list-item="{ item }"> + <div + class="gl-display-flex js-saved-reply-content" + :data-md-tag="item.content" + data-md-cursor-offset="0" + data-md-prepend="true" + data-testid="saved-reply-dropdown-item" + > + <div class="gl-text-truncate"> + <strong>{{ item.text }}</strong + ><span class="gl-ml-2">{{ item.content }}</span> + </div> + </div> + </template> + <template #footer> + <div + class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-100 gl-display-flex gl-justify-content-center gl-p-3" + > + <gl-button + :href="newSavedRepliesPath" + category="tertiary" + block + class="gl-justify-content-start! gl-mt-0! gl-mb-0! gl-px-3!" + >{{ __('Add a new saved reply') }}</gl-button + > + </div> + </template> + </gl-collapsible-listbox> +</template> + +<style> +.saved-replies-dropdown .gl-new-dropdown-panel { + width: 350px; +} + +.saved-replies-dropdown .gl-new-dropdown-item-check-icon { + display: none; +} +</style> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index c307601e670..49eb11f8081 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -1,7 +1,7 @@ <script> import Vue from 'vue'; import SafeHtml from '~/vue_shared/directives/safe_html'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import SuggestionDiff from './suggestion_diff.vue'; diff --git a/app/assets/javascripts/vue_shared/components/metric_images/store/actions.js b/app/assets/javascripts/vue_shared/components/metric_images/store/actions.js index 1c4e8d332a9..6f91463365b 100644 --- a/app/assets/javascripts/vue_shared/components/metric_images/store/actions.js +++ b/app/assets/javascripts/vue_shared/components/metric_images/store/actions.js @@ -1,4 +1,4 @@ -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { s__ } from '~/locale'; import * as types from './mutation_types'; diff --git a/app/assets/javascripts/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue b/app/assets/javascripts/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue index b079181bd10..e09a6e2e811 100644 --- a/app/assets/javascripts/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue @@ -6,7 +6,7 @@ import { GlLoadingIcon, GlSearchBoxByType, } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; import { __, sprintf } from '~/locale'; import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; diff --git a/app/assets/javascripts/vue_shared/components/pagination/constants.js b/app/assets/javascripts/vue_shared/components/pagination/constants.js index 748ad178c70..f8a6d37dea1 100644 --- a/app/assets/javascripts/vue_shared/components/pagination/constants.js +++ b/app/assets/javascripts/vue_shared/components/pagination/constants.js @@ -1,8 +1,5 @@ import { s__ } from '~/locale'; -export const PAGINATION_UI_BUTTON_LIMIT = 4; -export const UI_LIMIT = 6; -export const SPREAD = '...'; export const PREV = s__('Pagination|Prev'); export const NEXT = s__('Pagination|Next'); export const FIRST = s__('Pagination|« First'); diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue index 16bc8070dc1..bdc8ffee90a 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue @@ -52,7 +52,7 @@ export default { <div class="gl-display-flex gl-align-items-center gl-flex-wrap project-namespace-name-container" > - <gl-icon v-if="selected" class="js-selected-icon" name="mobile-issue-close" /> + <gl-icon v-if="selected" data-testid="selected-icon" name="mobile-issue-close" /> <project-avatar :project-id="project.id" :project-avatar-url="projectAvatarUrl" @@ -61,16 +61,18 @@ export default { /> <div v-if="truncatedNamespace" + data-testid="project-namespace" :title="projectNameWithNamespace" - class="text-secondary text-truncate js-project-namespace" + class="text-secondary text-truncate" > {{ truncatedNamespace }} <span v-if="truncatedNamespace" class="text-secondary">/ </span> </div> <div v-safe-html="highlightedProjectName" + data-testid="project-name" :title="project.name" - class="js-project-name text-truncate" + class="text-truncate" ></div> </div> </gl-button> diff --git a/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue b/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue deleted file mode 100644 index 02cb7785ef4..00000000000 --- a/app/assets/javascripts/vue_shared/components/resizable_chart/resizable_chart_container.vue +++ /dev/null @@ -1,40 +0,0 @@ -<script> -import $ from 'jquery'; -import { debounceByAnimationFrame } from '~/lib/utils/common_utils'; - -export default { - data() { - return { - width: 0, - height: 0, - }; - }, - beforeDestroy() { - this.contentResizeHandler.off('content.resize', this.debouncedResize); - window.removeEventListener('resize', this.debouncedResize); - }, - created() { - this.debouncedResize = debounceByAnimationFrame(this.onResize); - - // Handle when we explicictly trigger a custom resize event - this.contentResizeHandler = $(document).on('content.resize', this.debouncedResize); - - // Handle window resize - window.addEventListener('resize', this.debouncedResize); - }, - methods: { - onResize() { - // Slot dimensions - const { clientWidth, clientHeight } = this.$refs.chartWrapper; - this.width = clientWidth; - this.height = clientHeight; - }, - }, -}; -</script> - -<template> - <div ref="chartWrapper"> - <slot :width="width" :height="height"> </slot> - </div> -</template> diff --git a/app/assets/javascripts/vue_shared/components/source_editor.vue b/app/assets/javascripts/vue_shared/components/source_editor.vue index 1925c5d4064..7b7d3d48d9e 100644 --- a/app/assets/javascripts/vue_shared/components/source_editor.vue +++ b/app/assets/javascripts/vue_shared/components/source_editor.vue @@ -2,6 +2,7 @@ import { debounce, isEmpty } from 'lodash'; import { CONTENT_UPDATE_DEBOUNCE, EDITOR_READY_EVENT } from '~/editor/constants'; import Editor from '~/editor/source_editor'; +import { markRaw } from '~/lib/utils/vue3compat/mark_raw'; function initSourceEditor({ el, ...args }) { const editor = new Editor({ @@ -10,10 +11,12 @@ function initSourceEditor({ el, ...args }) { }, }); - return editor.createInstance({ - el, - ...args, - }); + return markRaw( + editor.createInstance({ + el, + ...args, + }), + ); } export default { diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js index 15335ea6edc..514b626ed95 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js @@ -141,8 +141,6 @@ export const BIDI_CHARS_CLASS_LIST = 'unicode-bidi has-tooltip'; export const BIDI_CHAR_TOOLTIP = 'Potentially unwanted character detected: Unicode BiDi Control'; -export const HLJS_ON_AFTER_HIGHLIGHT = 'after:highlight'; - // We fallback to highlighting these languages with Rouge, see the following issue for more detail: // https://gitlab.com/gitlab-org/gitlab/-/issues/384375#note_1212752013 export const LEGACY_FALLBACKS = ['python']; diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue index 09414e679bb..bda88a48e48 100644 --- a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue +++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue @@ -21,6 +21,11 @@ export default { required: false, default: 'top', }, + boundary: { + type: String, + required: false, + default: '', + }, truncateTarget: { type: [String, Function], required: false, @@ -44,6 +49,8 @@ export default { title: this.title, placement: this.placement, disabled: this.tooltipDisabled, + // Only set the tooltip boundary if it's truthy + ...(this.boundary && { boundary: this.boundary }), }; }, }, diff --git a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue index a001b6bdf24..23fbf211d54 100644 --- a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue +++ b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue @@ -149,7 +149,7 @@ export default { > <slot> <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-px-5 gl-py-4 gl-mb-0" type="button" @click="openFileUpload" > diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index 1a81da3eb0d..ab308d11a79 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -105,7 +105,7 @@ export default { v-gl-tooltip :title="tooltipText" :tooltip-placement="tooltipPlacement" - class="gl-ml-3" + class="gl-ml-1" data-testid="user-avatar-link-username" > {{ username }} diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue index d06bc7b8f98..dd9d2ce66cd 100644 --- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue +++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue @@ -10,7 +10,7 @@ import { } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { glEmojiTag } from '~/emoji'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { followUser, unfollowUser } from '~/rest_api'; import { isUserBusy } from '~/set_status_modal/utils'; import Tracking from '~/tracking'; diff --git a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue index edcfabe7da3..abd3575d020 100644 --- a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue +++ b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue @@ -11,7 +11,7 @@ import { } from '@gitlab/ui'; import { __ } from '~/locale'; import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue'; -import { IssuableType, TYPE_ISSUE } from '~/issues/constants'; +import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { participantsQueries, userSearchQueries } from '~/sidebar/constants'; import { TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants'; @@ -149,7 +149,7 @@ export default { }, computed: { isMergeRequest() { - return this.issuableType === IssuableType.MergeRequest; + return this.issuableType === TYPE_MERGE_REQUEST; }, searchUsersVariables() { const variables = { |