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>2021-12-20 16:37:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/work_items
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/work_items')
-rw-r--r--app/assets/javascripts/work_items/components/item_title.vue71
-rw-r--r--app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql18
-rw-r--r--app/assets/javascripts/work_items/graphql/fragmentTypes.json2
-rw-r--r--app/assets/javascripts/work_items/graphql/provider.js20
-rw-r--r--app/assets/javascripts/work_items/graphql/resolvers.js58
-rw-r--r--app/assets/javascripts/work_items/graphql/typedefs.graphql52
-rw-r--r--app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql18
-rw-r--r--app/assets/javascripts/work_items/graphql/widget.fragment.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item.query.graphql2
-rw-r--r--app/assets/javascripts/work_items/pages/create_work_item.vue71
-rw-r--r--app/assets/javascripts/work_items/pages/work_item_root.vue38
-rw-r--r--app/assets/javascripts/work_items/router/routes.js7
12 files changed, 324 insertions, 35 deletions
diff --git a/app/assets/javascripts/work_items/components/item_title.vue b/app/assets/javascripts/work_items/components/item_title.vue
new file mode 100644
index 00000000000..5e9e50a94f0
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/item_title.vue
@@ -0,0 +1,71 @@
+<script>
+import { escape } from 'lodash';
+import { __ } from '~/locale';
+
+export default {
+ props: {
+ initialTitle: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ placeholder: {
+ type: String,
+ required: false,
+ default: __('Add a title...'),
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ title: this.initialTitle,
+ };
+ },
+ methods: {
+ getSanitizedTitle(inputEl) {
+ const { innerText } = inputEl;
+ return escape(innerText);
+ },
+ handleBlur({ target }) {
+ this.$emit('title-changed', this.getSanitizedTitle(target));
+ },
+ handleInput({ target }) {
+ this.$emit('title-input', this.getSanitizedTitle(target));
+ },
+ handleSubmit() {
+ this.$refs.titleEl.blur();
+ },
+ },
+};
+</script>
+
+<template>
+ <h2
+ class="gl-font-weight-normal gl-sm-font-weight-bold gl-my-5 gl-display-inline-block"
+ :class="{ 'gl-cursor-not-allowed': disabled }"
+ data-testid="title"
+ aria-labelledby="item-title"
+ >
+ <span
+ id="item-title"
+ ref="titleEl"
+ role="textbox"
+ :aria-label="__('Title')"
+ :data-placeholder="placeholder"
+ :contenteditable="!disabled"
+ class="gl-pseudo-placeholder"
+ @blur="handleBlur"
+ @keyup="handleInput"
+ @keydown.enter.exact="handleSubmit"
+ @keydown.ctrl.u.prevent
+ @keydown.meta.u.prevent
+ @keydown.ctrl.b.prevent
+ @keydown.meta.b.prevent
+ >{{ title }}</span
+ >
+ </h2>
+</template>
diff --git a/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql
new file mode 100644
index 00000000000..2f302dae7d7
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql
@@ -0,0 +1,18 @@
+#import './widget.fragment.graphql'
+
+mutation createWorkItem($input: LocalCreateWorkItemInput) {
+ localCreateWorkItem(input: $input) @client {
+ workItem {
+ id
+ type
+ widgets {
+ nodes {
+ ...WidgetBase
+ ... on LocalTitleWidget {
+ contentText
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/fragmentTypes.json b/app/assets/javascripts/work_items/graphql/fragmentTypes.json
index c048ac34ac0..3b837e84ee9 100644
--- a/app/assets/javascripts/work_items/graphql/fragmentTypes.json
+++ b/app/assets/javascripts/work_items/graphql/fragmentTypes.json
@@ -1 +1 @@
-{"__schema":{"types":[{"kind":"INTERFACE","name":"WorkItemWidget","possibleTypes":[{"name":"TitleWidget"}]}]}}
+{"__schema":{"types":[{"kind":"INTERFACE","name":"LocalWorkItemWidget","possibleTypes":[{"name":"LocalTitleWidget"}]}]}}
diff --git a/app/assets/javascripts/work_items/graphql/provider.js b/app/assets/javascripts/work_items/graphql/provider.js
index 083735336ce..fb536a425c0 100644
--- a/app/assets/javascripts/work_items/graphql/provider.js
+++ b/app/assets/javascripts/work_items/graphql/provider.js
@@ -4,6 +4,7 @@ import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql';
import workItemQuery from './work_item.query.graphql';
import introspectionQueryResultData from './fragmentTypes.json';
+import { resolvers } from './resolvers';
import typeDefs from './typedefs.graphql';
const fragmentMatcher = new IntrospectionFragmentMatcher({
@@ -13,15 +14,12 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
export function createApolloProvider() {
Vue.use(VueApollo);
- const defaultClient = createDefaultClient(
- {},
- {
- cacheConfig: {
- fragmentMatcher,
- },
- typeDefs,
+ const defaultClient = createDefaultClient(resolvers, {
+ cacheConfig: {
+ fragmentMatcher,
},
- );
+ typeDefs,
+ });
defaultClient.cache.writeQuery({
query: workItemQuery,
@@ -30,14 +28,14 @@ export function createApolloProvider() {
},
data: {
workItem: {
- __typename: 'WorkItem',
+ __typename: 'LocalWorkItem',
id: '1',
type: 'FEATURE',
widgets: {
- __typename: 'WorkItemWidgetConnection',
+ __typename: 'LocalWorkItemWidgetConnection',
nodes: [
{
- __typename: 'TitleWidget',
+ __typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
// eslint-disable-next-line @gitlab/require-i18n-strings
diff --git a/app/assets/javascripts/work_items/graphql/resolvers.js b/app/assets/javascripts/work_items/graphql/resolvers.js
index e69de29bb2d..63d5234d083 100644
--- a/app/assets/javascripts/work_items/graphql/resolvers.js
+++ b/app/assets/javascripts/work_items/graphql/resolvers.js
@@ -0,0 +1,58 @@
+import { uuids } from '~/lib/utils/uuids';
+import workItemQuery from './work_item.query.graphql';
+
+export const resolvers = {
+ Mutation: {
+ localCreateWorkItem(_, { input }, { cache }) {
+ const id = uuids()[0];
+ const workItem = {
+ __typename: 'LocalWorkItem',
+ type: 'FEATURE',
+ id,
+ widgets: {
+ __typename: 'LocalWorkItemWidgetConnection',
+ nodes: [
+ {
+ __typename: 'LocalTitleWidget',
+ type: 'TITLE',
+ enabled: true,
+ contentText: input.title,
+ },
+ ],
+ },
+ };
+
+ cache.writeQuery({ query: workItemQuery, variables: { id }, data: { workItem } });
+
+ return {
+ __typename: 'LocalCreateWorkItemPayload',
+ workItem,
+ };
+ },
+
+ localUpdateWorkItem(_, { input }, { cache }) {
+ const workItemTitle = {
+ __typename: 'LocalTitleWidget',
+ type: 'TITLE',
+ enabled: true,
+ contentText: input.title,
+ };
+ const workItem = {
+ __typename: 'LocalWorkItem',
+ type: 'FEATURE',
+ id: input.id,
+ widgets: {
+ __typename: 'LocalWorkItemWidgetConnection',
+ nodes: [workItemTitle],
+ },
+ };
+
+ cache.writeQuery({ query: workItemQuery, variables: { id: input.id }, data: { workItem } });
+
+ return {
+ __typename: 'LocalUpdateWorkItemPayload',
+ workItem,
+ };
+ },
+ },
+};
diff --git a/app/assets/javascripts/work_items/graphql/typedefs.graphql b/app/assets/javascripts/work_items/graphql/typedefs.graphql
index 4a6e4aeed60..177eea00322 100644
--- a/app/assets/javascripts/work_items/graphql/typedefs.graphql
+++ b/app/assets/javascripts/work_items/graphql/typedefs.graphql
@@ -1,38 +1,60 @@
-enum WorkItemType {
+enum LocalWorkItemType {
FEATURE
}
-enum WidgetType {
+enum LocalWidgetType {
TITLE
}
-interface WorkItemWidget {
- type: WidgetType!
+interface LocalWorkItemWidget {
+ type: LocalWidgetType!
}
# Replicating Relay connection type for client schema
-type WorkItemWidgetEdge {
+type LocalWorkItemWidgetEdge {
cursor: String!
- node: WorkItemWidget
+ node: LocalWorkItemWidget
}
-type WorkItemWidgetConnection {
- edges: [WorkItemWidgetEdge]
- nodes: [WorkItemWidget]
+type LocalWorkItemWidgetConnection {
+ edges: [LocalWorkItemWidgetEdge]
+ nodes: [LocalWorkItemWidget]
pageInfo: PageInfo!
}
-type TitleWidget implements WorkItemWidget {
- type: WidgetType!
+type LocalTitleWidget implements LocalWorkItemWidget {
+ type: LocalWidgetType!
contentText: String!
}
-type WorkItem {
+type LocalWorkItem {
id: ID!
- type: WorkItemType!
- widgets: [WorkItemWidgetConnection]
+ type: LocalWorkItemType!
+ widgets: [LocalWorkItemWidgetConnection]
+}
+
+input LocalCreateWorkItemInput {
+ title: String!
+}
+
+input LocalUpdateWorkItemInput {
+ id: ID!
+ title: String
+}
+
+type LocalCreateWorkItemPayload {
+ workItem: LocalWorkItem!
+}
+
+type LocalUpdateWorkItemPayload {
+ workItem: LocalWorkItem!
}
extend type Query {
- workItem(id: ID!): WorkItem!
+ workItem(id: ID!): LocalWorkItem!
+}
+
+extend type Mutation {
+ localCreateWorkItem(input: LocalCreateWorkItemInput!): LocalCreateWorkItemPayload!
+ localUpdateWorkItem(input: LocalUpdateWorkItemInput!): LocalUpdateWorkItemPayload!
}
diff --git a/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql
new file mode 100644
index 00000000000..f0563f099b2
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql
@@ -0,0 +1,18 @@
+#import './widget.fragment.graphql'
+
+mutation updateWorkItem($input: LocalUpdateWorkItemInput) {
+ localUpdateWorkItem(input: $input) @client {
+ workItem {
+ id
+ type
+ widgets {
+ nodes {
+ ...WidgetBase
+ ... on LocalTitleWidget {
+ contentText
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/widget.fragment.graphql b/app/assets/javascripts/work_items/graphql/widget.fragment.graphql
index d7608c26052..154367dc0d8 100644
--- a/app/assets/javascripts/work_items/graphql/widget.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/widget.fragment.graphql
@@ -1,3 +1,3 @@
-fragment WidgetBase on WorkItemWidget {
+fragment WidgetBase on LocalWorkItemWidget {
type
}
diff --git a/app/assets/javascripts/work_items/graphql/work_item.query.graphql b/app/assets/javascripts/work_items/graphql/work_item.query.graphql
index 549e4f8c65a..9f173f7c302 100644
--- a/app/assets/javascripts/work_items/graphql/work_item.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item.query.graphql
@@ -7,7 +7,7 @@ query WorkItem($id: ID!) {
widgets {
nodes {
...WidgetBase
- ... on TitleWidget {
+ ... on LocalTitleWidget {
contentText
}
}
diff --git a/app/assets/javascripts/work_items/pages/create_work_item.vue b/app/assets/javascripts/work_items/pages/create_work_item.vue
new file mode 100644
index 00000000000..12bad5606d4
--- /dev/null
+++ b/app/assets/javascripts/work_items/pages/create_work_item.vue
@@ -0,0 +1,71 @@
+<script>
+import { GlButton, GlAlert } from '@gitlab/ui';
+import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql';
+
+import ItemTitle from '../components/item_title.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlAlert,
+ ItemTitle,
+ },
+ data() {
+ return {
+ title: '',
+ error: false,
+ };
+ },
+ methods: {
+ async createWorkItem() {
+ try {
+ const response = await this.$apollo.mutate({
+ mutation: createWorkItemMutation,
+ variables: {
+ input: {
+ title: this.title,
+ },
+ },
+ });
+
+ const {
+ data: {
+ localCreateWorkItem: {
+ workItem: { id },
+ },
+ },
+ } = response;
+ this.$router.push({ name: 'workItem', params: { id } });
+ } catch {
+ this.error = true;
+ }
+ },
+ handleTitleInput(title) {
+ this.title = title;
+ },
+ },
+};
+</script>
+
+<template>
+ <form @submit.prevent="createWorkItem">
+ <gl-alert v-if="error" variant="danger" @dismiss="error = false">{{
+ __('Something went wrong when creating a work item. Please try again')
+ }}</gl-alert>
+ <item-title data-testid="title-input" @title-input="handleTitleInput" />
+ <div class="gl-bg-gray-10 gl-py-5 gl-px-6">
+ <gl-button
+ variant="confirm"
+ :disabled="title.length === 0"
+ class="gl-mr-3"
+ data-testid="create-button"
+ type="submit"
+ >
+ {{ __('Create') }}
+ </gl-button>
+ <gl-button type="button" data-testid="cancel-button" @click="$router.go(-1)">
+ {{ __('Cancel') }}
+ </gl-button>
+ </div>
+ </form>
+</template>
diff --git a/app/assets/javascripts/work_items/pages/work_item_root.vue b/app/assets/javascripts/work_items/pages/work_item_root.vue
index 493ee0aba01..479274baf3a 100644
--- a/app/assets/javascripts/work_items/pages/work_item_root.vue
+++ b/app/assets/javascripts/work_items/pages/work_item_root.vue
@@ -1,8 +1,16 @@
<script>
+import { GlAlert } from '@gitlab/ui';
import workItemQuery from '../graphql/work_item.query.graphql';
+import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import { widgetTypes } from '../constants';
+import ItemTitle from '../components/item_title.vue';
+
export default {
+ components: {
+ ItemTitle,
+ GlAlert,
+ },
props: {
id: {
type: String,
@@ -12,6 +20,7 @@ export default {
data() {
return {
workItem: null,
+ error: false,
};
},
apollo: {
@@ -29,20 +38,39 @@ export default {
return this.workItem?.widgets?.nodes?.find((widget) => widget.type === widgetTypes.title);
},
},
+ methods: {
+ async updateWorkItem(title) {
+ try {
+ await this.$apollo.mutate({
+ mutation: updateWorkItemMutation,
+ variables: {
+ input: {
+ id: this.id,
+ title,
+ },
+ },
+ });
+ } catch {
+ this.error = true;
+ }
+ },
+ },
};
</script>
<template>
<section>
+ <gl-alert v-if="error" variant="danger" @dismiss="error = false">{{
+ __('Something went wrong while updating work item. Please try again')
+ }}</gl-alert>
<!-- Title widget placeholder -->
<div>
- <h2
+ <item-title
v-if="titleWidgetData"
- class="gl-font-weight-normal gl-sm-font-weight-bold gl-my-5"
+ :initial-title="titleWidgetData.contentText"
data-testid="title"
- >
- {{ titleWidgetData.contentText }}
- </h2>
+ @title-changed="updateWorkItem"
+ />
</div>
</section>
</template>
diff --git a/app/assets/javascripts/work_items/router/routes.js b/app/assets/javascripts/work_items/router/routes.js
index a3cf44ad4ca..95772bbd026 100644
--- a/app/assets/javascripts/work_items/router/routes.js
+++ b/app/assets/javascripts/work_items/router/routes.js
@@ -1,7 +1,12 @@
export const routes = [
{
+ path: '/new',
+ name: 'createWorkItem',
+ component: () => import('../pages/create_work_item.vue'),
+ },
+ {
path: '/:id',
- name: 'work_item',
+ name: 'workItem',
component: () => import('../pages/work_item_root.vue'),
props: true,
},