Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
321d5557
Commit
321d5557
authored
Apr 16, 2022
by
Denys Mishunov
Committed by
David O'Regan
Apr 16, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Source Editor: The toolbar Vue components
parent
12ba23ee
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
453 additions
and
0 deletions
+453
-0
app/assets/javascripts/editor/components/source_editor_toolbar.vue
...s/javascripts/editor/components/source_editor_toolbar.vue
+70
-0
app/assets/javascripts/editor/components/source_editor_toolbar_button.vue
...cripts/editor/components/source_editor_toolbar_button.vue
+89
-0
app/assets/javascripts/editor/constants.js
app/assets/javascripts/editor/constants.js
+3
-0
app/assets/javascripts/editor/graphql/get_item.query.graphql
app/assets/javascripts/editor/graphql/get_item.query.graphql
+9
-0
app/assets/javascripts/editor/graphql/get_items.query.graphql
...assets/javascripts/editor/graphql/get_items.query.graphql
+5
-0
app/assets/javascripts/editor/graphql/update_item.mutation.graphql
...s/javascripts/editor/graphql/update_item.mutation.graphql
+3
-0
spec/frontend/editor/components/helpers.js
spec/frontend/editor/components/helpers.js
+12
-0
spec/frontend/editor/components/source_editor_toolbar_button_spec.js
...nd/editor/components/source_editor_toolbar_button_spec.js
+146
-0
spec/frontend/editor/components/source_editor_toolbar_spec.js
.../frontend/editor/components/source_editor_toolbar_spec.js
+116
-0
No files found.
app/assets/javascripts/editor/components/source_editor_toolbar.vue
0 → 100644
View file @
321d5557
<
script
>
import
{
isEmpty
}
from
'
lodash
'
;
import
{
GlButtonGroup
}
from
'
@gitlab/ui
'
;
import
getToolbarItemsQuery
from
'
~/editor/graphql/get_items.query.graphql
'
;
import
{
EDITOR_TOOLBAR_LEFT_GROUP
,
EDITOR_TOOLBAR_RIGHT_GROUP
}
from
'
~/editor/constants
'
;
import
SourceEditorToolbarButton
from
'
./source_editor_toolbar_button.vue
'
;
export
default
{
name
:
'
SourceEditorToolbar
'
,
components
:
{
SourceEditorToolbarButton
,
GlButtonGroup
,
},
data
()
{
return
{
items
:
[],
};
},
apollo
:
{
items
:
{
query
:
getToolbarItemsQuery
,
update
(
data
)
{
return
this
.
setDefaultGroup
(
data
?.
items
?.
nodes
);
},
},
},
computed
:
{
isVisible
()
{
return
this
.
items
.
length
;
},
},
methods
:
{
setDefaultGroup
(
nodes
=
[])
{
return
nodes
.
map
((
item
)
=>
{
return
{
...
item
,
group
:
(
this
.
$options
.
groups
.
includes
(
item
.
group
)
&&
item
.
group
)
||
EDITOR_TOOLBAR_RIGHT_GROUP
,
};
});
},
getGroupItems
(
group
)
{
return
this
.
items
.
filter
((
item
)
=>
item
.
group
===
group
);
},
hasGroupItems
(
group
)
{
return
!
isEmpty
(
this
.
getGroupItems
(
group
));
},
},
groups
:
[
EDITOR_TOOLBAR_LEFT_GROUP
,
EDITOR_TOOLBAR_RIGHT_GROUP
],
};
</
script
>
<
template
>
<section
v-if=
"isVisible"
id=
"se-toolbar"
class=
"gl-py-3 gl-px-5 gl-bg-white gl-border-t gl-border-b gl-display-flex gl-justify-content-space-between gl-align-items-center"
>
<template
v-for=
"group in $options.groups"
>
<gl-button-group
v-if=
"hasGroupItems(group)"
:key=
"group"
>
<template
v-for=
"item in getGroupItems(group)"
>
<source-editor-toolbar-button
:key=
"item.id"
:button=
"item"
@
click=
"$emit('click', item)"
/>
</
template
>
</gl-button-group>
</template>
</section>
</template>
app/assets/javascripts/editor/components/source_editor_toolbar_button.vue
0 → 100644
View file @
321d5557
<
script
>
import
{
GlButton
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
updateToolbarItemMutation
from
'
~/editor/graphql/update_item.mutation.graphql
'
;
import
getToolbarItemQuery
from
'
~/editor/graphql/get_item.query.graphql
'
;
export
default
{
name
:
'
SourceEditorToolbarButton
'
,
components
:
{
GlButton
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
props
:
{
button
:
{
type
:
Object
,
required
:
false
,
default
()
{
return
{};
},
},
},
data
()
{
return
{
buttonItem
:
this
.
button
,
};
},
apollo
:
{
buttonItem
:
{
query
:
getToolbarItemQuery
,
variables
()
{
return
{
id
:
this
.
button
.
id
,
};
},
update
({
item
})
{
return
item
;
},
skip
()
{
return
!
this
.
button
.
id
;
},
},
},
computed
:
{
icon
()
{
return
this
.
buttonItem
.
selected
?
this
.
buttonItem
.
selectedIcon
||
this
.
buttonItem
.
icon
:
this
.
buttonItem
.
icon
;
},
label
()
{
return
this
.
buttonItem
.
selected
?
this
.
buttonItem
.
selectedLabel
||
this
.
buttonItem
.
label
:
this
.
buttonItem
.
label
;
},
},
methods
:
{
clickHandler
()
{
if
(
this
.
buttonItem
.
onClick
)
{
this
.
buttonItem
.
onClick
();
}
this
.
$apollo
.
mutate
({
mutation
:
updateToolbarItemMutation
,
variables
:
{
id
:
this
.
buttonItem
.
id
,
propsToUpdate
:
{
selected
:
!
this
.
buttonItem
.
selected
,
},
},
});
this
.
$emit
(
'
click
'
);
},
},
};
</
script
>
<
template
>
<div>
<gl-button
v-gl-tooltip
.
hover
:category=
"buttonItem.category"
:variant=
"buttonItem.variant"
type=
"button"
:selected=
"buttonItem.selected"
:icon=
"icon"
:title=
"label"
:aria-label=
"label"
@
click=
"clickHandler"
/>
</div>
</
template
>
app/assets/javascripts/editor/constants.js
View file @
321d5557
...
...
@@ -12,6 +12,9 @@ export const EDITOR_TYPE_DIFF = 'vs.editor.IDiffEditor';
export
const
EDITOR_CODE_INSTANCE_FN
=
'
createInstance
'
;
export
const
EDITOR_DIFF_INSTANCE_FN
=
'
createDiffInstance
'
;
export
const
EDITOR_TOOLBAR_LEFT_GROUP
=
'
left
'
;
export
const
EDITOR_TOOLBAR_RIGHT_GROUP
=
'
right
'
;
export
const
SOURCE_EDITOR_INSTANCE_ERROR_NO_EL
=
s__
(
'
SourceEditor|"el" parameter is required for createInstance()
'
,
);
...
...
app/assets/javascripts/editor/graphql/get_item.query.graphql
0 → 100644
View file @
321d5557
query
ToolbarItem
(
$id
:
String
!)
{
item
(
id
:
$id
)
@client
{
id
label
icon
selected
group
}
}
app/assets/javascripts/editor/graphql/get_items.query.graphql
0 → 100644
View file @
321d5557
query
ToolbarItems
{
items
@client
{
nodes
}
}
app/assets/javascripts/editor/graphql/update_item.mutation.graphql
0 → 100644
View file @
321d5557
mutation
updateItem
(
$id
:
String
!,
$propsToUpdate
:
Item
!)
{
updateToolbarItem
(
id
:
$id
,
propsToUpdate
:
$propsToUpdate
)
@client
}
spec/frontend/editor/components/helpers.js
0 → 100644
View file @
321d5557
import
{
EDITOR_TOOLBAR_RIGHT_GROUP
}
from
'
~/editor/constants
'
;
export
const
buildButton
=
(
id
=
'
foo-bar-btn
'
,
options
=
{})
=>
{
return
{
__typename
:
'
Item
'
,
id
,
label
:
options
.
label
||
'
Foo Bar Button
'
,
icon
:
options
.
icon
||
'
foo-bar
'
,
selected
:
options
.
selected
||
false
,
group
:
options
.
group
||
EDITOR_TOOLBAR_RIGHT_GROUP
,
};
};
spec/frontend/editor/components/source_editor_toolbar_button_spec.js
0 → 100644
View file @
321d5557
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
SourceEditorToolbarButton
from
'
~/editor/components/source_editor_toolbar_button.vue
'
;
import
getToolbarItemQuery
from
'
~/editor/graphql/get_item.query.graphql
'
;
import
updateToolbarItemMutation
from
'
~/editor/graphql/update_item.mutation.graphql
'
;
import
{
buildButton
}
from
'
./helpers
'
;
Vue
.
use
(
VueApollo
);
describe
(
'
Source Editor Toolbar button
'
,
()
=>
{
let
wrapper
;
let
mockApollo
;
const
defaultBtn
=
buildButton
();
const
findButton
=
()
=>
wrapper
.
findComponent
(
GlButton
);
const
createComponentWithApollo
=
({
propsData
}
=
{})
=>
{
mockApollo
=
createMockApollo
();
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
getToolbarItemQuery
,
variables
:
{
id
:
defaultBtn
.
id
},
data
:
{
item
:
{
...
defaultBtn
,
},
},
});
wrapper
=
shallowMount
(
SourceEditorToolbarButton
,
{
propsData
,
apolloProvider
:
mockApollo
,
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
mockApollo
=
null
;
});
describe
(
'
default
'
,
()
=>
{
const
defaultProps
=
{
category
:
'
primary
'
,
variant
:
'
default
'
,
};
const
customProps
=
{
category
:
'
secondary
'
,
variant
:
'
info
'
,
};
it
(
'
renders a default button without props
'
,
async
()
=>
{
createComponentWithApollo
();
const
btn
=
findButton
();
expect
(
btn
.
exists
()).
toBe
(
true
);
expect
(
btn
.
props
()).
toMatchObject
(
defaultProps
);
});
it
(
'
renders a button based on the props passed
'
,
async
()
=>
{
createComponentWithApollo
({
propsData
:
{
button
:
customProps
,
},
});
const
btn
=
findButton
();
expect
(
btn
.
props
()).
toMatchObject
(
customProps
);
});
});
describe
(
'
button updates
'
,
()
=>
{
it
(
'
it properly updates button on Apollo cache update
'
,
async
()
=>
{
const
{
id
}
=
defaultBtn
;
createComponentWithApollo
({
propsData
:
{
button
:
{
id
,
},
},
});
expect
(
findButton
().
props
(
'
selected
'
)).
toBe
(
false
);
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
getToolbarItemQuery
,
variables
:
{
id
},
data
:
{
item
:
{
...
defaultBtn
,
selected
:
true
,
},
},
});
jest
.
runOnlyPendingTimers
();
await
nextTick
();
expect
(
findButton
().
props
(
'
selected
'
)).
toBe
(
true
);
});
});
describe
(
'
click handler
'
,
()
=>
{
it
(
'
fires the click handler on the button when available
'
,
()
=>
{
const
spy
=
jest
.
fn
();
createComponentWithApollo
({
propsData
:
{
button
:
{
onClick
:
spy
,
},
},
});
expect
(
spy
).
not
.
toHaveBeenCalled
();
findButton
().
vm
.
$emit
(
'
click
'
);
expect
(
spy
).
toHaveBeenCalled
();
});
it
(
'
emits the "click" event
'
,
()
=>
{
createComponentWithApollo
();
jest
.
spyOn
(
wrapper
.
vm
,
'
$emit
'
);
expect
(
wrapper
.
vm
.
$emit
).
not
.
toHaveBeenCalled
();
findButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
$emit
).
toHaveBeenCalledWith
(
'
click
'
);
});
it
(
'
triggers the mutation exposing the changed "selected" prop
'
,
()
=>
{
const
{
id
}
=
defaultBtn
;
createComponentWithApollo
({
propsData
:
{
button
:
{
id
,
},
},
});
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
);
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
not
.
toHaveBeenCalled
();
findButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
({
mutation
:
updateToolbarItemMutation
,
variables
:
{
id
,
propsToUpdate
:
{
selected
:
true
,
},
},
});
});
});
});
spec/frontend/editor/components/source_editor_toolbar_spec.js
0 → 100644
View file @
321d5557
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
{
GlButtonGroup
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
SourceEditorToolbar
from
'
~/editor/components/source_editor_toolbar.vue
'
;
import
SourceEditorToolbarButton
from
'
~/editor/components/source_editor_toolbar_button.vue
'
;
import
{
EDITOR_TOOLBAR_LEFT_GROUP
,
EDITOR_TOOLBAR_RIGHT_GROUP
}
from
'
~/editor/constants
'
;
import
getToolbarItemsQuery
from
'
~/editor/graphql/get_items.query.graphql
'
;
import
{
buildButton
}
from
'
./helpers
'
;
Vue
.
use
(
VueApollo
);
describe
(
'
Source Editor Toolbar
'
,
()
=>
{
let
wrapper
;
let
mockApollo
;
const
findButtons
=
()
=>
wrapper
.
findAllComponents
(
SourceEditorToolbarButton
);
const
createApolloMockWithCache
=
(
items
=
[])
=>
{
mockApollo
=
createMockApollo
();
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
getToolbarItemsQuery
,
data
:
{
items
:
{
nodes
:
items
,
},
},
});
};
const
createComponentWithApollo
=
(
items
=
[])
=>
{
createApolloMockWithCache
(
items
);
wrapper
=
shallowMount
(
SourceEditorToolbar
,
{
apolloProvider
:
mockApollo
,
stubs
:
{
GlButtonGroup
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
mockApollo
=
null
;
});
describe
(
'
groups
'
,
()
=>
{
it
.
each
`
group | expectedGroup
${
EDITOR_TOOLBAR_LEFT_GROUP
}
|
${
EDITOR_TOOLBAR_LEFT_GROUP
}
${
EDITOR_TOOLBAR_RIGHT_GROUP
}
|
${
EDITOR_TOOLBAR_RIGHT_GROUP
}
${
undefined
}
|
${
EDITOR_TOOLBAR_RIGHT_GROUP
}
${
'
non-existing
'
}
|
${
EDITOR_TOOLBAR_RIGHT_GROUP
}
`
(
'
puts item with group="$group" into $expectedGroup group
'
,
({
group
,
expectedGroup
})
=>
{
const
item
=
buildButton
(
'
first
'
,
{
group
,
});
createComponentWithApollo
([
item
]);
expect
(
findButtons
()).
toHaveLength
(
1
);
[
EDITOR_TOOLBAR_RIGHT_GROUP
,
EDITOR_TOOLBAR_LEFT_GROUP
].
forEach
((
g
)
=>
{
if
(
g
===
expectedGroup
)
{
expect
(
wrapper
.
vm
.
getGroupItems
(
g
)).
toEqual
([
expect
.
objectContaining
({
id
:
'
first
'
})]);
}
else
{
expect
(
wrapper
.
vm
.
getGroupItems
(
g
)).
toHaveLength
(
0
);
}
});
});
});
describe
(
'
buttons update
'
,
()
=>
{
it
(
'
it properly updates buttons on Apollo cache update
'
,
async
()
=>
{
const
item
=
buildButton
(
'
first
'
,
{
group
:
EDITOR_TOOLBAR_RIGHT_GROUP
,
});
createComponentWithApollo
();
expect
(
findButtons
()).
toHaveLength
(
0
);
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
getToolbarItemsQuery
,
data
:
{
items
:
{
nodes
:
[
item
],
},
},
});
jest
.
runOnlyPendingTimers
();
await
nextTick
();
expect
(
findButtons
()).
toHaveLength
(
1
);
});
});
describe
(
'
click handler
'
,
()
=>
{
it
(
'
emits the "click" event when a button is clicked
'
,
()
=>
{
const
item1
=
buildButton
(
'
first
'
,
{
group
:
EDITOR_TOOLBAR_LEFT_GROUP
,
});
const
item2
=
buildButton
(
'
second
'
,
{
group
:
EDITOR_TOOLBAR_RIGHT_GROUP
,
});
createComponentWithApollo
([
item1
,
item2
]);
jest
.
spyOn
(
wrapper
.
vm
,
'
$emit
'
);
expect
(
wrapper
.
vm
.
$emit
).
not
.
toHaveBeenCalled
();
findButtons
().
at
(
0
).
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
$emit
).
toHaveBeenCalledWith
(
'
click
'
,
item1
);
findButtons
().
at
(
1
).
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
$emit
).
toHaveBeenCalledWith
(
'
click
'
,
item2
);
expect
(
wrapper
.
vm
.
$emit
.
mock
.
calls
).
toHaveLength
(
2
);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment