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
Hide 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 ""
msgid "Delete Key"
msgstr ""
msgid "Delete Selected"
msgstr ""
msgid "Delete Value Stream"
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