feat(editor): Implement new banners framework (#6603)

*  Implemented new grid row - banners
*  Fixing node creator and executions sidebar position after layout update
* 💄 Added configurable round corners to the Callout component
*  Fixing mouse position detection and main tab bar position
*  Implemented basic banner component structure
*  Implemented banner state and dismiss logic
*  Fixing grid layout. Updating banners height state dynamically
*  Fix zoom to fit position, mouse position in demo mode and callout vertical alignment
*  Implementing proper trial banners logic
* 💄 Only showing execution usage data once the sidebar is fully expanded
*  Implemented permanent/temporary dismiss logic for v1 flag
*  Minor refactoring of banner logic
*  Updating permanent dismiss logic to work with all banners
* 👕 Fixing linting errors
* ✔️ Updating Callout component test snapshots
* 💄 Tweaking zoom to fit position
* ✔️ Updating testing endpoints to use new store data
*  Added banners unit tests
* ✔️ Fixing failing banner tests
*  Added more banner tests
*  Updating banners dimensions on resize, removing leftover code
* ✔️ Removing store import from API file
* 👕 Fixing lint errors
*  Updating migration files
*  Using query parameters in migrations
* 👌 Addressing design review feedback
*  Updating upgrade plan button click
*  Updating the migrations syntax
* 👌 Updating permanent banner dismiss endpoint and back-end logic
* 👌 Refactoring trial banner component and ui store
* 👌 Addressing more points from code review
* 👌 Moving DOM logic from the store
* ✔️ Updated callout component snapshots
* 👌 Updating mysql migration file
* ✔️ Updating e2e test canvas coordinates after setting it's position to absolute
* 👌 Addressing back-end review feedback
* 👌 Improving typing around banners
* 👕 Fixing lint errors
This commit is contained in:
Milorad FIlipović
2023-07-14 15:36:17 +02:00
committed by GitHub
parent ff0759530d
commit 4240e76253
47 changed files with 637 additions and 221 deletions

View File

@@ -0,0 +1,151 @@
import { PiniaVuePlugin } from 'pinia';
import { render, within } from '@testing-library/vue';
import { merge } from 'lodash-es';
import userEvent from '@testing-library/user-event';
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { STORES } from '@/constants';
import { createTestingPinia } from '@pinia/testing';
import BannerStack from '@/components/banners/BannerStack.vue';
import { useUIStore } from '@/stores/ui.store';
import { useUsersStore } from '@/stores/users.store';
let uiStore: ReturnType<typeof useUIStore>;
let usersStore: ReturnType<typeof useUsersStore>;
const DEFAULT_SETUP = {
pinia: createTestingPinia({
initialState: {
[STORES.SETTINGS]: {
settings: merge({}, SETTINGS_STORE_DEFAULT_STATE.settings),
},
[STORES.UI]: {
banners: {
V1: { dismissed: false },
TRIAL: { dismissed: false },
TRIAL_OVER: { dismissed: false },
},
},
[STORES.USERS]: {
currentUserId: 'aaa-bbb',
users: {
'aaa-bbb': {
id: 'aaa-bbb',
globalRole: {
id: '1',
name: 'owner',
scope: 'global',
},
},
'bbb-bbb': {
id: 'bbb-bbb',
globalRoleId: 2,
globalRole: {
id: '2',
name: 'member',
scope: 'global',
},
},
},
},
},
}),
};
const renderComponent = (renderOptions: Parameters<typeof render>[1] = {}) =>
render(BannerStack, merge(DEFAULT_SETUP, renderOptions), (vue) => {
vue.use(PiniaVuePlugin);
});
describe('BannerStack', () => {
beforeEach(() => {
uiStore = useUIStore();
usersStore = useUsersStore();
});
afterEach(() => {
vi.clearAllMocks();
});
it('should render default configuration', async () => {
const { getByTestId } = renderComponent();
const bannerStack = getByTestId('banner-stack');
expect(bannerStack).toBeInTheDocument();
expect(within(bannerStack).getByTestId('banners-TRIAL')).toBeInTheDocument();
expect(within(bannerStack).getByTestId('banners-TRIAL_OVER')).toBeInTheDocument();
expect(within(bannerStack).getByTestId('banners-V1')).toBeInTheDocument();
});
it('should not render dismissed banners', async () => {
const { getByTestId } = renderComponent({
pinia: createTestingPinia({
initialState: merge(
{
[STORES.UI]: {
banners: {
V1: { dismissed: true },
TRIAL: { dismissed: true },
},
},
},
DEFAULT_SETUP.pinia,
),
}),
});
const bannerStack = getByTestId('banner-stack');
expect(bannerStack).toBeInTheDocument();
expect(within(bannerStack).queryByTestId('banners-V1')).not.toBeInTheDocument();
expect(within(bannerStack).queryByTestId('banners-TRIAL')).not.toBeInTheDocument();
expect(within(bannerStack).getByTestId('banners-TRIAL_OVER')).toBeInTheDocument();
});
it('should dismiss banner on click', async () => {
const { getByTestId } = renderComponent();
const dismissBannerSpy = vi
.spyOn(useUIStore(), 'dismissBanner')
.mockImplementation(async (banner, mode) => {});
const closeTrialBannerButton = getByTestId('banner-TRIAL_OVER-close');
expect(closeTrialBannerButton).toBeInTheDocument();
await userEvent.click(closeTrialBannerButton);
expect(dismissBannerSpy).toHaveBeenCalledWith('TRIAL_OVER');
});
it('should permanently dismiss banner on click', async () => {
const { getByTestId } = renderComponent({
pinia: createTestingPinia({
initialState: merge(DEFAULT_SETUP.pinia, {
[STORES.UI]: {
banners: {
V1: { dismissed: false },
},
},
}),
}),
});
const dismissBannerSpy = vi
.spyOn(useUIStore(), 'dismissBanner')
.mockImplementation(async (banner, mode) => {});
const permanentlyDismissBannerLink = getByTestId('banner-confirm-v1');
expect(permanentlyDismissBannerLink).toBeInTheDocument();
await userEvent.click(permanentlyDismissBannerLink);
expect(dismissBannerSpy).toHaveBeenCalledWith('V1', 'permanent');
});
it('should not render permanent dismiss link if user is not owner', async () => {
const { queryByTestId } = renderComponent({
pinia: createTestingPinia({
initialState: merge(DEFAULT_SETUP.pinia, {
[STORES.USERS]: {
currentUserId: 'bbb-bbb',
},
}),
}),
});
expect(queryByTestId('banner-confirm-v1')).not.toBeInTheDocument();
});
});

View File

@@ -8,11 +8,10 @@ import { SOURCE_CONTROL_PULL_MODAL_KEY, STORES } from '@/constants';
import { i18nInstance } from '@/plugins/i18n';
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import MainSidebarSourceControl from '@/components/MainSidebarSourceControl.vue';
import { useUsersStore, useSourceControlStore, useUIStore } from '@/stores';
import { useSourceControlStore, useUIStore } from '@/stores';
let pinia: ReturnType<typeof createTestingPinia>;
let sourceControlStore: ReturnType<typeof useSourceControlStore>;
let usersStore: ReturnType<typeof useUsersStore>;
let uiStore: ReturnType<typeof useUIStore>;
const renderComponent = (renderOptions: Parameters<typeof render>[1] = {}) => {
@@ -44,7 +43,6 @@ describe('MainSidebarSourceControl', () => {
sourceControlStore = useSourceControlStore();
uiStore = useUIStore();
usersStore = useUsersStore();
});
it('should render nothing', async () => {