Skip to content
Snippets Groups Projects
Unverified Commit e06cb80e authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett Committed by Luke Bennett
Browse files

Simplify admin instance licenses page

Displays all licenses on the admin license
page.
Allows for downloading and delete previous
licenses.
parent 9b58a431
No related branches found
No related tags found
No related merge requests found
Showing
with 1101 additions and 98 deletions
export default () => ({
licenses: [],
deleteQueue: [],
isLoadingLicenses: false,
licensesPath: '',
deleteLicensePath: '',
newLicensePath: '',
downloadLicensePath: '',
currentActiveUserCount: null,
});
import mountInstanceLicenseApp from 'ee/licenses';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('instance-license-mount-element');
mountInstanceLicenseApp(mountElement);
});
Loading
Loading
@@ -5,3 +5,64 @@
color: $gl-gray-light;
}
}
.license-card-body {
overflow-x: scroll;
@include media-breakpoint-up(lg) {
overflow-x: hidden;
}
}
.license-table {
min-width: max-content;
.license-row:first-child .license-cell {
border-top: 0;
}
.license-cell:last-child {
border-right: 0;
}
}
.license-cell {
border-color: $gray-200;
border-style: solid;
border-width: 1px 1px 0 0;
flex-basis: 0;
.title {
color: $gray-700;
line-height: $gl-line-height;
}
.value {
color: $gray-900;
&.number {
font-size: 1.25rem;
}
}
}
.license-header-cell {
flex-basis: initial;
width: $license-header-cell-width;
.title {
color: $gray-900;
}
}
.skeleton-license-card {
.skeleton-bar {
max-height: $gl-line-height;
.skeleton-line-1,
.skeleton-line-1::after {
height: 100%;
width: 100%;
}
}
}
Loading
Loading
@@ -110,5 +110,11 @@ module LicenseHelper
!Gitlab::CurrentSettings.should_check_namespace_plan? && show_promotions? && show_callout?('promote_advanced_search_dismissed') && !License.feature_available?(:elastic_search)
end
 
def license_app_data
{ data: { current_active_user_count: current_active_user_count,
licenses_path: api_v4_licenses_path, delete_license_path: api_v4_license_path(id: ':id'),
new_license_path: new_admin_license_path, download_license_path: download_admin_license_path } }
end
extend self
end
- page_title "License"
%h3.page-title
Your License
- if current_license.trial?
= render "upload_buy_license"
- else
= link_to 'Upload New License', new_admin_license_path, class: "btn btn-success float-right"
 
%hr
- if Feature.enabled?(:licenses_app)
#instance-license-mount-element{ license_app_data }
- else
%h3.page-title
Your License
- if current_license.trial?
= render "upload_buy_license"
- else
= link_to 'Upload New License', new_admin_license_path, class: "btn btn-success float-right"
 
.row
.col-md-6
.card
.card-header
Licensed to
%ul.content-list
- @license.licensee.each do |label, value|
%li
%span.light #{label}:
%strong= value
%hr
 
.card.js-license-info-panel
.card-header
Details
%ul.content-list
%li
%span.light Plan:
%strong= @license.plan.capitalize
%li
%span.light Uploaded:
%strong= time_ago_with_tooltip @license.created_at
%li
%span.light Started:
%strong= time_ago_with_tooltip @license.starts_at
%li
%span.light
- if @license.expired?
Expired:
- else
Expires:
- if @license.will_expire? && @license.active?
- if @license.trial?
%strong.has-tooltip{ title: @license.expires_at.to_formatted_s(:long), data: { placement: 'top' } }
Free trial will expire in #{pluralize(@license.remaining_days, 'day')}
.row
.col-md-6
.card
.card-header
Licensed to
%ul.content-list
- @license.licensee.each do |label, value|
%li
%span.light #{label}:
%strong= value
.card.js-license-info-panel
.card-header
Details
%ul.content-list
%li
%span.light Plan:
%strong= @license.plan.capitalize
%li
%span.light Uploaded:
%strong= time_ago_with_tooltip @license.created_at
%li
%span.light Started:
%strong= time_ago_with_tooltip @license.starts_at
%li
%span.light
- if @license.expired?
Expired:
- else
Expires:
- if @license.will_expire? && @license.active?
- if @license.trial?
%strong.has-tooltip{ title: @license.expires_at.to_formatted_s(:long), data: { placement: 'top' } }
Free trial will expire in #{pluralize(@license.remaining_days, 'day')}
- else
%strong= time_ago_with_tooltip(@license.expires_at)
- else
%strong= time_ago_with_tooltip(@license.expires_at)
- else
%strong Never
%strong Never
 
- if @license.expired?
%span.badge.badge-danger.float-right
%strong Expired
- if @license.expired?
%span.badge.badge-danger.float-right
%strong Expired
 
.col-md-6
.card.border-info
.card-header.bg-info.text-white
Download license
.card-body
%p Your license will be included in your GitLab backup and will survive upgrades, so in normal usage you should never need to re-upload your <code>.gitlab-license</code> file.
%p Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again.
%br
= link_to 'Download license', download_admin_license_path, class: "btn btn-info"
.col-md-6
.card.border-info
.card-header.bg-info.text-white
Download license
.card-body
%p Your license will be included in your GitLab backup and will survive upgrades, so in normal usage you should never need to re-upload your <code>.gitlab-license</code> file.
%p Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again.
%br
= link_to 'Download license', download_admin_license_path, class: "btn btn-info"
 
 
.card.border-danger
.card-header.bg-danger.text-white
Remove license
.card-body
%p If you remove this license, GitLab will fall back on the previous license, if any.
%p If there is no previous license or if the previous license has expired, some GitLab functionality will be blocked until a new, valid license is uploaded.
%br
= link_to 'Remove license', admin_license_path, data: { confirm: "Are you sure you want to remove the license?" }, method: :delete, class: "btn btn-remove qa-remove-license-link"
.card.border-danger
.card-header.bg-danger.text-white
Remove license
.card-body
%p If you remove this license, GitLab will fall back on the previous license, if any.
%p If there is no previous license or if the previous license has expired, some GitLab functionality will be blocked until a new, valid license is uploaded.
%br
= link_to 'Remove license', admin_license_path, data: { confirm: "Are you sure you want to remove the license?" }, method: :delete, class: "btn btn-remove qa-remove-license-link"
 
= render "breakdown", license: @license
= render "breakdown", license: @license
 
- if @previous_licenses.any?
%h4 License History
- if @previous_licenses.any?
%h4 License History
 
.card#license_history
%table.table
%thead.card-header
%tr
- @license.licensee.keys.each do |label|
%th= label
%th Plan
%th Uploaded at
%th Started at
%th Expired at
%th Active users
%tbody
- @previous_licenses.each do |license|
.card#license_history
%table.table
%thead.card-header
%tr
- @license.licensee.keys.each do |label|
%td= license.licensee[label]
%td
%span
= license.plan.capitalize
%td
%span
= license.created_at
%td
%span
= license.starts_at
%td
%span
= license.expires_at || "Never"
%td
%span
- if license.restricted?(:active_user_count)
#{license.restrictions[:active_user_count]} users
- else
Unlimited
%th= label
%th Plan
%th Uploaded at
%th Started at
%th Expired at
%th Active users
%tbody
- @previous_licenses.each do |license|
%tr
- @license.licensee.keys.each do |label|
%td= license.licensee[label]
%td
%span
= license.plan.capitalize
%td
%span
= license.created_at
%td
%span
= license.starts_at
%td
%span
= license.expires_at || "Never"
%td
%span
- if license.restricted?(:active_user_count)
#{license.restrictions[:active_user_count]} users
- else
Unlimited
---
title: Simplify admin instance licenses page
merge_request: 9785
author:
type: other
Loading
Loading
@@ -4,6 +4,7 @@ describe "Admin uploads license" do
set(:admin) { create(:admin) }
 
before do
stub_feature_flags(licenses_app: false)
sign_in(admin)
end
 
Loading
Loading
Loading
Loading
@@ -4,6 +4,7 @@ describe "Admin views license" do
set(:admin) { create(:admin) }
 
before do
stub_feature_flags(licenses_app: false)
sign_in(admin)
end
 
Loading
Loading
# frozen_string_literal: true
require "spec_helper"
describe "Licenses app", :js do
let(:admin) { create(:admin) }
let!(:licenses) do
[
create(:license, data: build(:gitlab_license, restrictions: { active_user_count: 2000 }).export),
create(:license, data: build(:gitlab_license, expires_at: Date.today - 10, restrictions: { active_user_count: 2000, plan: 'ultimate' }).export)
]
end
def visit_page
visit(admin_license_path)
find('.js-license-table', match: :first)
end
def assert_usage_row(row, license)
header, seats_in_license, seats_in_use, historical_max, overage = row.find_all('.license-cell').to_a
expect(header).to have_content 'Usage'
expect(seats_in_license).to have_content 'Seats in license'
expect(seats_in_license).to have_content license.restrictions[:active_user_count]
expect(seats_in_use).to have_content 'Seats currently in use'
expect(seats_in_use).to have_content User.active.count
expect(historical_max).to have_content 'Max seats used'
expect(historical_max).to have_content license.historical_max
expect(overage).to have_content 'Users outside of license'
expect(overage).to have_content license.overage
end
def assert_validity_row(row, license)
header, starts_at, expires_at, created_at = row.find_all('.license-cell').to_a
expect(header).to have_content 'Validity'
expect(starts_at).to have_content 'Start date'
expect(starts_at).to have_content license.starts_at.strftime('%B %-d, %Y')
expect(expires_at).to have_content 'End date'
expect(expires_at).to have_content license.expires_at.strftime('%B %-d, %Y')
if license.expired?
expect(expires_at).to have_content 'Expired'
else
expect(expires_at).not_to have_content 'Expired'
end
expect(created_at).to have_content 'Uploaded on'
expect(created_at).to have_content license.created_at.strftime('%B %-d, %Y')
end
def assert_registration_row(row, license)
header, name, email, company = row.find_all('.license-cell').to_a
expect(header).to have_content 'Registration'
expect(name).to have_content 'Licensed to'
expect(name).to have_content license.licensee['Name'] || 'Unknown'
expect(email).to have_content 'Email address'
expect(email).to have_content license.licensee['Email'] || 'Unknown'
expect(company).to have_content 'Company'
expect(company).to have_content license.licensee['Company'] || 'Unknown'
end
def assert_license_card(card, license)
top_row, middle_row, bottom_row = card.find_all('.license-row').to_a
assert_usage_row(top_row, license)
assert_validity_row(middle_row, license)
assert_registration_row(bottom_row, license)
end
before do
stub_feature_flags(licenses_app: true)
sign_in(admin)
end
it 'renders a list of licenses' do
visit_page
licenses.each_with_index do |license, index|
assert_license_card(find_all('.license-table')[index], licenses.reverse[index])
end
end
it 'deletes a license' do
visit_page
license_card = find('.license-card', match: :first)
current_id = License.current.id
license_card.find('.js-manage-license').click
page.accept_alert 'Are you sure you want to permanently delete this license?' do
license_card.find('.js-delete-license').click
end
expect(license_card).not_to have_selector('.license-card-loading')
expect(License.find_by(id: current_id)).to be_nil
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InstanceCardsList renders a list of license cards 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<glbutton-stub
class="my-3 js-add-license"
href="/newLicensePath"
variant="success"
>
Add license
</glbutton-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<licensecard-stub
iscurrentlicense="true"
license="[object Object]"
/>
</li>
<li>
<licensecard-stub
license="[object Object]"
/>
</li>
</ul>
</div>
`;
exports[`InstanceCardsList renders a message when there are no licenses 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<glbutton-stub
class="my-3 js-add-license"
href="/newLicensePath"
variant="success"
>
Add license
</glbutton-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<strong>
No licenses found.
</strong>
</li>
</ul>
</div>
`;
exports[`InstanceCardsList renders a skeleton loading card if loading licenses 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<glbutton-stub
class="my-3 js-add-license"
href="/newLicensePath"
variant="success"
>
Add license
</glbutton-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<skeletonlicensecard-stub />
</li>
</ul>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LicenseCardBody renders a license card body 1`] = `
<div
class="card-body license-card-body p-0"
>
<div
class="license-table js-license-table"
>
<div
class="license-row d-flex"
>
<headercell-stub
icon="monitor"
title="Usage"
/>
<cell-stub
isflexible="true"
title="Seats in license"
value="10"
/>
<infocell-stub
popovercontent="Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
title="Seats currently in use"
value="10"
/>
<infocell-stub
popovercontent="This is the maximum number of users that have existed at the same time since the license started. This is the minimum number of seats you will need to buy when you renew your license."
title="Max seats used"
value="20"
/>
<infocell-stub
popovercontent="GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
title="Users outside of license"
value="5"
/>
</div>
<div
class="license-row d-flex"
>
<headercell-stub
icon="calendar"
title="Validity"
/>
<datecell-stub
datenow="2017/10/10"
title="Start date"
value="2013/10/10"
/>
<datecell-stub
datenow="2017/10/10"
isexpirable="true"
title="End date"
value="2015/10/10"
/>
<datecell-stub
datenow="2017/10/10"
title="Uploaded on"
/>
</div>
<div
class="license-row d-flex"
>
<headercell-stub
icon="user"
title="Registration"
/>
<cell-stub
isflexible="true"
title="Licensed to"
value="Jon Dough"
/>
<cell-stub
isflexible="true"
title="Email address"
value="email@address.tanuki"
/>
<cell-stub
isflexible="true"
title="Company"
value="TanukiVille"
/>
</div>
</div>
</div>
`;
exports[`LicenseCardBody renders a loading state if isRemoving 1`] = `
<div
class="card-body license-card-body p-0"
>
<div
class="p-5 d-flex justify-content-center align-items-center license-card-loading"
>
<icon-stub
cssclasses=""
name="spinner"
size="16"
/>
<span
class="ml-2"
>
Removing license…
</span>
</div>
</div>
`;
exports[`LicenseCardBody renders fallback licensee values 1`] = `
<div
class="card-body license-card-body p-0"
licensee="[object Object]"
>
<div
class="license-table js-license-table"
>
<div
class="license-row d-flex"
>
<headercell-stub
icon="monitor"
title="Usage"
/>
<cell-stub
isflexible="true"
title="Seats in license"
value="10"
/>
<infocell-stub
popovercontent="Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
title="Seats currently in use"
value="10"
/>
<infocell-stub
popovercontent="This is the maximum number of users that have existed at the same time since the license started. This is the minimum number of seats you will need to buy when you renew your license."
title="Max seats used"
value="20"
/>
<infocell-stub
popovercontent="GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
title="Users outside of license"
value="5"
/>
</div>
<div
class="license-row d-flex"
>
<headercell-stub
icon="calendar"
title="Validity"
/>
<datecell-stub
datenow="2017/10/10"
title="Start date"
value="2013/10/10"
/>
<datecell-stub
datenow="2017/10/10"
isexpirable="true"
title="End date"
value="2015/10/10"
/>
<datecell-stub
datenow="2017/10/10"
title="Uploaded on"
/>
</div>
<div
class="license-row d-flex"
>
<headercell-stub
icon="user"
title="Registration"
/>
<cell-stub
isflexible="true"
title="Licensed to"
value="Jon Dough"
/>
<cell-stub
isflexible="true"
title="Email address"
value="email@address.tanuki"
/>
<cell-stub
isflexible="true"
title="Company"
value="TanukiVille"
/>
</div>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LicenseCard renders license card with a delete button and license body 1`] = `
<div
class="card license-card mb-5"
>
<div
class="card-header"
>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
GitLab Enterprise Edition Super duper
</h4>
<gldropdown-stub
class="js-manage-license"
right=""
text="Manage"
>
<!---->
<gldropdownitem-stub
class="js-delete-license text-danger"
>
Delete license
</gldropdownitem-stub>
</gldropdown-stub>
</div>
</div>
<licensecardbody-stub
currentactiveusercount="10"
license="[object Object]"
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SkeletonLicenseCard renders a skeleton license card 1`] = `
<div
class="card license-card skeleton-license-card"
>
<div
class="card-header d-flex justify-content-between align-items-center py-3"
>
<glskeletonloading-stub
class="w-75 skeleton-bar"
lines="1"
/>
</div>
<div
class="card-body p-0"
>
<div
class="license-table"
>
<div
class="license-row d-flex"
>
<skeletonheadercell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
</div>
<div
class="license-row d-flex"
>
<skeletonheadercell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
</div>
<div
class="license-row d-flex"
>
<skeletonheadercell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
<skeletoncell-stub />
</div>
</div>
</div>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import LicenseCardBody from 'ee/licenses/components/cards/license_card_body.vue';
describe('LicenseCardBody', () => {
let wrapper;
const defaultProps = {
license: {
userLimit: 10,
historicalMax: 20,
overage: 5,
startsAt: '2013/10/10',
expiresAt: '2015/10/10',
licensee: {
Name: 'Jon Dough',
Email: 'email@address.tanuki',
Company: 'TanukiVille',
},
},
isRemoving: false,
currentActiveUserCount: 10,
};
function createComponent(props) {
const propsData = Object.assign({}, defaultProps, props);
wrapper = shallowMount(LicenseCardBody, {
propsData,
});
}
beforeEach(() => {
jest.spyOn(global.Date.prototype, 'toString').mockReturnValue('2017/10/10');
});
afterEach(() => {
if (wrapper) wrapper.destroy();
global.Date.prototype.toString.mockRestore();
});
it('renders a license card body', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('renders a loading state if isRemoving', () => {
createComponent({ isRemoving: true });
expect(wrapper.element).toMatchSnapshot();
});
it('renders fallback licensee values', () => {
createComponent({ licensee: {} });
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { LicenseCard } from 'ee/licenses/components/cards';
describe('LicenseCard', () => {
let wrapper;
let actions;
const defaultProps = {
license: {
id: 1,
plan: 'super duper',
},
isCurrentLicense: false,
};
const defaultState = {
currentActiveUserCount: 10,
deleteQueue: [],
downloadLicensePath: '/downloadLicensePath',
};
const localVue = createLocalVue();
localVue.use(Vuex);
function createStore(newState) {
const state = Object.assign({}, defaultState, newState);
actions = { fetchDeleteLicense: jest.fn() };
return new Vuex.Store({ state, actions });
}
function createComponent(state, props) {
const propsData = Object.assign({}, defaultProps, props);
wrapper = shallowMount(LicenseCard, {
store: createStore(state),
propsData,
localVue,
});
}
afterEach(() => {
if (wrapper) wrapper.destroy();
});
it('renders license card with a delete button and license body', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount } from '@vue/test-utils';
import { SkeletonLicenseCard } from 'ee/licenses/components/cards';
describe('SkeletonLicenseCard', () => {
let wrapper;
function createComponent() {
wrapper = shallowMount(SkeletonLicenseCard);
}
afterEach(() => {
if (wrapper) wrapper.destroy();
});
it('renders a skeleton license card', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Cell renders a number value and title through props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2 number"
>
<span>
100
</span>
</div>
</div>
`;
exports[`Cell renders a string value and title through props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2"
>
<span>
value
</span>
</div>
</div>
`;
exports[`Cell renders an inflexible variant 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2"
>
<span>
value
</span>
</div>
</div>
`;
exports[`Cell renders value and title slots that override props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<h1>
tanuki
</h1>
</span>
<div
class="value mt-2"
>
<marquee>
party
</marquee>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DateCell renders a date value that represents a date in words and title through props 1`] = `
<cell-stub
isflexible="true"
title="title"
>
<div
class=""
>
March 6, 2018
<!---->
</div>
</cell-stub>
`;
exports[`DateCell renders a fallback value if isExpirable and no value 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<!---->
</cell-stub>
`;
exports[`DateCell renders a string value that represents a date in words and title through props 1`] = `
<cell-stub
isflexible="true"
title="title"
>
<div
class=""
>
October 24, 2018
<!---->
</div>
</cell-stub>
`;
exports[`DateCell renders an expired warning if isExpirable and date value is before now 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<div
class="text-danger"
>
October 24, 2018
<span>
- Expired
</span>
</div>
</cell-stub>
`;
exports[`DateCell renders date value with no warning if isExpirable and date value is after now 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<div
class=""
>
October 24, 2018
<!---->
</div>
</cell-stub>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`HeaderCell renders an inflexible cell with a title with an icon through props 1`] = `
<cell-stub
class="license-header-cell"
>
<template>
<icon-stub
class="icon"
cssclasses=""
name="retry"
size="16"
/>
<span
class="ml-2 font-weight-bold"
>
title
</span>
</template>
</cell-stub>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InfoCell renders a number value 1`] = `
<cell-stub
class="license-info-cell"
isflexible="true"
value="100"
>
<template>
<span
class="mr-2 text"
>
title
</span>
<button
class="btn-link information-target"
type="button"
>
<icon-stub
cssclasses="icon d-block"
name="information"
size="16"
/>
</button>
<glpopover-stub
content="popoverContent"
placement="bottom"
triggers="hover"
/>
</template>
</cell-stub>
`;
exports[`InfoCell renders a title and string value with an info popover through props 1`] = `
<cell-stub
class="license-info-cell"
isflexible="true"
value="value"
>
<template>
<span
class="mr-2 text"
>
title
</span>
<button
class="btn-link information-target"
type="button"
>
<icon-stub
cssclasses="icon d-block"
name="information"
size="16"
/>
</button>
<glpopover-stub
content="popoverContent"
placement="bottom"
triggers="hover"
/>
</template>
</cell-stub>
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment