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
7355d8b5
Commit
7355d8b5
authored
Nov 25, 2021
by
Nicolò Maria Mezzopera
Committed by
Natalia Tepluhina
Nov 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a registry list component
parent
5bd757b7
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
326 additions
and
0 deletions
+326
-0
app/assets/javascripts/packages_and_registries/shared/components/registry_list.vue
...ckages_and_registries/shared/components/registry_list.vue
+124
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/packages_and_registries/shared/components/registry_list_spec.js
...es_and_registries/shared/components/registry_list_spec.js
+199
-0
No files found.
app/assets/javascripts/packages_and_registries/shared/components/registry_list.vue
0 → 100644
View file @
7355d8b5
<
script
>
import
{
GlButton
,
GlFormCheckbox
,
GlKeysetPagination
}
from
'
@gitlab/ui
'
;
import
{
filter
}
from
'
lodash
'
;
import
{
__
}
from
'
~/locale
'
;
export
default
{
name
:
'
RegistryList
'
,
components
:
{
GlButton
,
GlFormCheckbox
,
GlKeysetPagination
,
},
props
:
{
title
:
{
type
:
String
,
required
:
true
,
},
isLoading
:
{
type
:
Boolean
,
default
:
false
,
required
:
false
,
},
hiddenDelete
:
{
type
:
Boolean
,
default
:
false
,
required
:
false
,
},
pagination
:
{
type
:
Object
,
required
:
false
,
default
:
()
=>
({}),
},
items
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
idProperty
:
{
type
:
String
,
required
:
false
,
default
:
'
id
'
,
},
},
data
()
{
return
{
selectedReferences
:
{},
};
},
computed
:
{
showPagination
()
{
return
this
.
pagination
.
hasPreviousPage
||
this
.
pagination
.
hasNextPage
;
},
disableDeleteButton
()
{
return
this
.
isLoading
||
filter
(
this
.
selectedReferences
).
length
===
0
;
},
selectedItems
()
{
return
this
.
items
.
filter
(
this
.
isSelected
);
},
selectAll
:
{
get
()
{
return
this
.
items
.
every
(
this
.
isSelected
);
},
set
(
value
)
{
this
.
items
.
forEach
((
item
)
=>
{
const
id
=
item
[
this
.
idProperty
];
this
.
$set
(
this
.
selectedReferences
,
id
,
value
);
});
},
},
},
methods
:
{
selectItem
(
item
)
{
const
id
=
item
[
this
.
idProperty
];
this
.
$set
(
this
.
selectedReferences
,
id
,
!
this
.
selectedReferences
[
id
]);
},
isSelected
(
item
)
{
const
id
=
item
[
this
.
idProperty
];
return
this
.
selectedReferences
[
id
];
},
},
i18n
:
{
deleteSelected
:
__
(
'
Delete Selected
'
),
},
};
</
script
>
<
template
>
<div>
<div
class=
"gl-display-flex gl-justify-content-space-between gl-mb-3"
>
<gl-form-checkbox
v-if=
"!hiddenDelete"
v-model=
"selectAll"
class=
"gl-ml-2"
>
<span
class=
"gl-font-weight-bold"
>
{{
title
}}
</span>
</gl-form-checkbox>
<gl-button
v-if=
"!hiddenDelete"
:disabled=
"disableDeleteButton"
category=
"secondary"
variant=
"danger"
@
click=
"$emit('delete', selectedItems)"
>
{{
$options
.
i18n
.
deleteSelected
}}
</gl-button>
</div>
<div
v-for=
"(item, index) in items"
:key=
"index"
>
<slot
:select-item=
"selectItem"
:is-selected=
"isSelected"
:item=
"item"
:first=
"index === 0"
></slot>
</div>
<div
class=
"gl-display-flex gl-justify-content-center"
>
<gl-keyset-pagination
v-if=
"showPagination"
v-bind=
"pagination"
class=
"gl-mt-3"
@
prev=
"$emit('prev-page')"
@
next=
"$emit('next-page')"
/>
</div>
</div>
</
template
>
locale/gitlab.pot
View file @
7355d8b5
...
@@ -11174,6 +11174,9 @@ msgstr ""
...
@@ -11174,6 +11174,9 @@ msgstr ""
msgid "Delete Key"
msgid "Delete Key"
msgstr ""
msgstr ""
msgid "Delete Selected"
msgstr ""
msgid "Delete Value Stream"
msgid "Delete Value Stream"
msgstr ""
msgstr ""
...
...
spec/frontend/packages_and_registries/shared/components/registry_list_spec.js
0 → 100644
View file @
7355d8b5
import
{
GlButton
,
GlFormCheckbox
,
GlKeysetPagination
}
from
'
@gitlab/ui
'
;
import
{
nextTick
}
from
'
vue
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
component
from
'
~/packages_and_registries/shared/components/registry_list.vue
'
;
describe
(
'
Registry List
'
,
()
=>
{
let
wrapper
;
const
items
=
[{
id
:
'
a
'
},
{
id
:
'
b
'
}];
const
defaultPropsData
=
{
title
:
'
test_title
'
,
items
,
};
const
rowScopedSlot
=
`
<div data-testid="scoped-slot">
<button @click="props.selectItem(props.item)">Select</button>
<span>{{props.first}}</span>
<p>{{props.isSelected(props.item)}}</p>
</div>`
;
const
mountComponent
=
({
propsData
=
defaultPropsData
}
=
{})
=>
{
wrapper
=
shallowMountExtended
(
component
,
{
propsData
,
scopedSlots
:
{
default
:
rowScopedSlot
,
},
});
};
const
findSelectAll
=
()
=>
wrapper
.
findComponent
(
GlFormCheckbox
);
const
findDeleteSelected
=
()
=>
wrapper
.
findComponent
(
GlButton
);
const
findPagination
=
()
=>
wrapper
.
findComponent
(
GlKeysetPagination
);
const
findScopedSlots
=
()
=>
wrapper
.
findAllByTestId
(
'
scoped-slot
'
);
const
findScopedSlotSelectButton
=
(
index
)
=>
findScopedSlots
().
at
(
index
).
find
(
'
button
'
);
const
findScopedSlotFirstValue
=
(
index
)
=>
findScopedSlots
().
at
(
index
).
find
(
'
span
'
);
const
findScopedSlotIsSelectedValue
=
(
index
)
=>
findScopedSlots
().
at
(
index
).
find
(
'
p
'
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
header
'
,
()
=>
{
it
(
'
renders the title passed in the prop
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
text
()).
toContain
(
defaultPropsData
.
title
);
});
describe
(
'
select all checkbox
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
exists
'
,
()
=>
{
expect
(
findSelectAll
().
exists
()).
toBe
(
true
);
});
it
(
'
select and unselect all
'
,
async
()
=>
{
// no row is not selected
items
.
forEach
((
item
,
index
)
=>
{
expect
(
findScopedSlotIsSelectedValue
(
index
).
text
()).
toBe
(
''
);
});
// simulate selection
findSelectAll
().
vm
.
$emit
(
'
input
'
,
true
);
await
nextTick
();
// all rows selected
items
.
forEach
((
item
,
index
)
=>
{
expect
(
findScopedSlotIsSelectedValue
(
index
).
text
()).
toBe
(
'
true
'
);
});
// simulate de-selection
findSelectAll
().
vm
.
$emit
(
'
input
'
,
''
);
await
nextTick
();
// no row is not selected
items
.
forEach
((
item
,
index
)
=>
{
expect
(
findScopedSlotIsSelectedValue
(
index
).
text
()).
toBe
(
''
);
});
});
});
describe
(
'
delete button
'
,
()
=>
{
it
(
'
has the correct text
'
,
()
=>
{
mountComponent
();
expect
(
findDeleteSelected
().
text
()).
toBe
(
component
.
i18n
.
deleteSelected
);
});
it
(
'
is hidden when hiddenDelete is true
'
,
()
=>
{
mountComponent
({
propsData
:
{
...
defaultPropsData
,
hiddenDelete
:
true
}
});
expect
(
findDeleteSelected
().
exists
()).
toBe
(
false
);
});
it
(
'
is disabled when isLoading is true
'
,
()
=>
{
mountComponent
({
propsData
:
{
...
defaultPropsData
,
isLoading
:
true
}
});
expect
(
findDeleteSelected
().
props
(
'
disabled
'
)).
toBe
(
true
);
});
it
(
'
is disabled when no row is selected
'
,
async
()
=>
{
mountComponent
();
expect
(
findDeleteSelected
().
props
(
'
disabled
'
)).
toBe
(
true
);
await
findScopedSlotSelectButton
(
0
).
trigger
(
'
click
'
);
expect
(
findDeleteSelected
().
props
(
'
disabled
'
)).
toBe
(
false
);
});
it
(
'
on click emits the delete event with the selected rows
'
,
async
()
=>
{
mountComponent
();
await
findScopedSlotSelectButton
(
0
).
trigger
(
'
click
'
);
findDeleteSelected
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
delete
'
)).
toEqual
([[[
items
[
0
]]]]);
});
});
});
describe
(
'
main area
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
renders scopedSlots based on the items props
'
,
()
=>
{
expect
(
findScopedSlots
()).
toHaveLength
(
items
.
length
);
});
it
(
'
populates the scope of the slot correctly
'
,
async
()
=>
{
expect
(
findScopedSlots
().
at
(
0
).
exists
()).
toBe
(
true
);
// it's the first slot
expect
(
findScopedSlotFirstValue
(
0
).
text
()).
toBe
(
'
true
'
);
// item is not selected, falsy is translated to empty string
expect
(
findScopedSlotIsSelectedValue
(
0
).
text
()).
toBe
(
''
);
// find the button with the bound function
await
findScopedSlotSelectButton
(
0
).
trigger
(
'
click
'
);
// the item is selected
expect
(
findScopedSlotIsSelectedValue
(
0
).
text
()).
toBe
(
'
true
'
);
});
});
describe
(
'
footer
'
,
()
=>
{
let
pagination
;
beforeEach
(()
=>
{
pagination
=
{
hasPreviousPage
:
false
,
hasNextPage
:
true
};
});
it
(
'
has a pagination
'
,
()
=>
{
mountComponent
({
propsData
:
{
...
defaultPropsData
,
pagination
},
});
expect
(
findPagination
().
props
()).
toMatchObject
(
pagination
);
});
it
.
each
`
hasPreviousPage | hasNextPage | visible
${
true
}
|
${
true
}
|
${
true
}
${
true
}
|
${
false
}
|
${
true
}
${
false
}
|
${
true
}
|
${
true
}
${
false
}
|
${
false
}
|
${
false
}
`
(
'
when hasPreviousPage is $hasPreviousPage and hasNextPage is $hasNextPage is $visible that the pagination is shown
'
,
({
hasPreviousPage
,
hasNextPage
,
visible
})
=>
{
pagination
=
{
hasPreviousPage
,
hasNextPage
};
mountComponent
({
propsData
:
{
...
defaultPropsData
,
pagination
},
});
expect
(
findPagination
().
exists
()).
toBe
(
visible
);
},
);
it
(
'
pagination emits the correct events
'
,
()
=>
{
mountComponent
({
propsData
:
{
...
defaultPropsData
,
pagination
},
});
findPagination
().
vm
.
$emit
(
'
prev
'
);
expect
(
wrapper
.
emitted
(
'
prev-page
'
)).
toEqual
([[]]);
findPagination
().
vm
.
$emit
(
'
next
'
);
expect
(
wrapper
.
emitted
(
'
next-page
'
)).
toEqual
([[]]);
});
});
});
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