web/satellite: confirm saving API key modal implemented

WHAT:
modal with saving API key confirmation implemented for onboarding tour

WHY:
ensure user saved API key

Change-Id: I9bc000cf5a19a9d6fc9eb3a4bfb9156d6b4bc78f
This commit is contained in:
VitaliiShpital 2020-07-15 14:54:36 +03:00 committed by Vitalii Shpital
parent 00f9882ad5
commit c921710247
10 changed files with 200 additions and 3 deletions

View File

@ -70,6 +70,10 @@
:on-press="onDoneClick"
:is-disabled="isCreatingState"
/>
<SaveApiKeyModal
v-if="isSaveApiKeyModalShown"
@confirmSave="onConfirmClick"
/>
</div>
</template>
@ -78,11 +82,13 @@ import { Component, Vue } from 'vue-property-decorator';
import HeaderedInput from '@/components/common/HeaderedInput.vue';
import VButton from '@/components/common/VButton.vue';
import SaveApiKeyModal from '@/components/onboardingTour/steps/SaveApiKeyModal.vue';
import InfoImage from '@/../static/images/onboardingTour/info.svg';
import { API_KEYS_ACTIONS } from '@/store/modules/apiKeys';
import { ApiKey } from '@/types/apiKeys';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { AddingApiKeyState } from '@/utils/constants/onboardingTourEnums';
@ -95,6 +101,7 @@ const {
components: {
VButton,
HeaderedInput,
SaveApiKeyModal,
InfoImage,
},
})
@ -107,6 +114,13 @@ export default class CreateApiKeyStep extends Vue {
public errorMessage: string = '';
public isLoading: boolean = false;
/**
* Indicates if save API key modal is shown.
*/
public get isSaveApiKeyModalShown(): boolean {
return this.$store.state.appStateModule.appState.isSaveApiKeyModalShown;
}
/**
* Indicates if view is in creating state.
*/
@ -190,9 +204,17 @@ export default class CreateApiKeyStep extends Vue {
}
/**
* Sets tour state to last step.
* Toggles save API key modal visibility.
*/
public onDoneClick(): void {
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SAVE_API_KEY_MODAL);
}
/**
* Sets tour state to last step.
*/
public onConfirmClick(): void {
this.onDoneClick();
this.$emit('setUploadDataState');
}
}

View File

@ -0,0 +1,116 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="save-api-modal">
<div class="save-api-modal__container">
<OrangeExclamation/>
<h1 class="save-api-modal__container__title">Is Your API Key Saved?</h1>
<p class="save-api-modal__container__message">
API Keys are only displayed once when generated. If you havent saved your key, go back to copy and
paste the API key to your preferred method of storing secrets (i.e. TextEdit, Keybase, etc.)
</p>
<div class="save-api-modal__container__buttons-area">
<VButton
class="back-button"
width="186px"
height="45px"
label="Go Back"
:on-press="onBackClick"
:is-blue-white="true"
/>
<VButton
width="186px"
height="45px"
label="Yes, it's Saved!"
:on-press="onConfirmClick"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VButton from '@/components/common/VButton.vue';
import OrangeExclamation from '@/../static/images/onboardingTour/orange-exclamation.svg';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
@Component({
components: {
OrangeExclamation,
VButton,
},
})
export default class SaveApiKeyModal extends Vue {
/**
* Toggles modal visibility.
*/
public onBackClick(): void {
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SAVE_API_KEY_MODAL);
}
/**
* Proceeds to tour's last step.
*/
public onConfirmClick(): void {
this.$emit('confirmSave');
}
}
</script>
<style scoped lang="scss">
.save-api-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(9, 21, 35, 0.85);
display: flex;
align-items: center;
justify-content: center;
font-family: 'font_regular', sans-serif;
&__container {
background-color: #fff;
z-index: 1;
padding: 35px;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 6px;
max-width: 460px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 22px;
line-height: 27px;
color: #000;
margin: 10px 0;
}
&__message {
font-size: 16px;
line-height: 24px;
color: #000;
word-break: break-word;
text-align: center;
margin: 0 0 10px 0;
}
&__buttons-area {
display: flex;
align-items: center;
}
}
}
.back-button {
margin-right: 10px;
}
</style>

View File

@ -24,6 +24,7 @@ export const appStateModule = {
isChangePasswordPopupShown: false,
isPaymentSelectionShown: false,
isCreateProjectButtonShown: false,
isSaveApiKeyModalShown: false,
},
satelliteName: '',
},
@ -38,6 +39,11 @@ export const appStateModule = {
state.appState.isNewProjectPopupShown = !state.appState.isNewProjectPopupShown;
},
// Mutation changing save api key modal visibility
[APP_STATE_MUTATIONS.TOGGLE_SAVE_API_KEY_MODAL](state: any): void {
state.appState.isSaveApiKeyModalShown = !state.appState.isSaveApiKeyModalShown;
},
// Mutation changing project dropdown visibility
[APP_STATE_MUTATIONS.TOGGLE_PROJECT_DROPDOWN](state: any): void {
state.appState.isProjectsDropdownShown = !state.appState.isProjectsDropdownShown;
@ -120,6 +126,13 @@ export const appStateModule = {
commit(APP_STATE_MUTATIONS.TOGGLE_NEW_PROJECT_POPUP);
},
[APP_STATE_ACTIONS.TOGGLE_SAVE_API_KEY_MODAL]: function ({commit, state}: any): void {
if (!state.appState.isSaveApiKeyModalShown) {
commit(APP_STATE_MUTATIONS.CLOSE_ALL);
}
commit(APP_STATE_MUTATIONS.TOGGLE_SAVE_API_KEY_MODAL);
},
[APP_STATE_ACTIONS.TOGGLE_PROJECTS]: function ({commit, state}: any): void {
if (!state.appState.isProjectsDropdownShown) {
commit(APP_STATE_MUTATIONS.CLOSE_ALL);

View File

@ -14,6 +14,7 @@ export const APP_STATE_MUTATIONS = {
TOGGLE_NEW_PROJECT_POPUP: 'TOGGLE_NEW_PROJECT_POPUP',
TOGGLE_PROJECT_DROPDOWN: 'TOGGLE_PROJECT_DROPDOWN',
TOGGLE_ACCOUNT_DROPDOWN: 'TOGGLE_ACCOUNT_DROPDOWN',
TOGGLE_SAVE_API_KEY_MODAL: 'TOGGLE_SAVE_API_KEY_MODAL',
TOGGLE_DELETE_PROJECT_DROPDOWN: 'TOGGLE_DELETE_PROJECT_DROPDOWN',
TOGGLE_DELETE_ACCOUNT_DROPDOWN: 'TOGGLE_DELETE_ACCOUNT_DROPDOWN',
TOGGLE_SORT_PM_BY_DROPDOWN: 'TOGGLE_SORT_PM_BY_DROPDOWN',

View File

@ -4,6 +4,7 @@
export const APP_STATE_ACTIONS = {
TOGGLE_TEAM_MEMBERS: 'toggleAddTeamMembersPopup',
TOGGLE_NEW_PROJ : 'toggleNewProjectPopup',
TOGGLE_SAVE_API_KEY_MODAL: 'toggleSaveApiKeyModal',
TOGGLE_PROJECTS: 'toggleProjectsDropdown',
TOGGLE_ACCOUNT: 'toggleAccountDropdown',
TOGGLE_DEL_PROJ: 'toggleDeleteProjectPopup',

View File

@ -0,0 +1,3 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 24C1.15877e-06 10.7452 10.7452 -1.15877e-06 24 0C37.2548 2.09815e-06 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 -2.09815e-06 37.2548 0 24ZM26.6669 24.0003C26.6669 25.473 25.473 26.6669 24.0003 26.6669C22.5275 26.6669 21.3336 25.473 21.3336 24.0003L21.3336 13.3336C21.3336 11.8609 22.5275 10.6669 24.0003 10.6669C25.473 10.6669 26.6669 11.8609 26.6669 13.3336L26.6669 24.0003ZM24.0003 37.3335C25.473 37.3335 26.6669 36.1396 26.6669 34.6668C26.6669 33.194 25.473 32.0001 24.0003 32.0001C22.5275 32.0001 21.3336 33.194 21.3336 34.6668C21.3336 36.1396 22.5275 37.3335 24.0003 37.3335Z" fill="#F4B000"/>
</svg>

After

Width:  |  Height:  |  Size: 771 B

View File

@ -2,6 +2,7 @@
// See LICENSE for copying information.
import Vuex from 'vuex';
import sinon from 'sinon';
import CreateApiKeyStep from '@/components/onboardingTour/steps/CreateApiKeyStep.vue';
@ -15,6 +16,7 @@ import { createLocalVue, mount } from '@vue/test-utils';
import { ApiKeysMock } from '../../mock/api/apiKeys';
import { ProjectsApiMock } from '../../mock/api/projects';
import {appStateModule} from "@/store/modules/appState";
const localVue = createLocalVue();
const notificationPlugin = new NotificatorPlugin();
@ -31,7 +33,7 @@ localVue.use(Vuex);
localVue.use(notificationPlugin);
localVue.use(segmentioPlugin);
const store = new Vuex.Store({ modules: { projectsModule, apiKeysModule }});
const store = new Vuex.Store({ modules: { projectsModule, apiKeysModule, appStateModule }});
describe('CreateApiKeyStep.vue', () => {
it('renders correctly', (): void => {
@ -58,13 +60,17 @@ describe('CreateApiKeyStep.vue', () => {
});
it('done click works correctly correctly', async (): Promise<void> => {
const spy = sinon.spy();
const wrapper = mount(CreateApiKeyStep, {
store,
localVue,
methods: {
onDoneClick: spy,
},
});
await wrapper.find('.done-button').trigger('click');
expect(wrapper.emitted()).toHaveProperty('setUploadDataState');
expect(spy.callCount).toBe(1);
});
});

View File

@ -0,0 +1,14 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import SaveApiKeyModal from '@/components/onboardingTour/steps/SaveApiKeyModal.vue';
import { mount } from '@vue/test-utils';
describe('SaveApiKeyModal.vue', () => {
it('renders correctly', (): void => {
const wrapper = mount(SaveApiKeyModal);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -44,6 +44,7 @@ exports[`CreateApiKeyStep.vue create api key works correctly correctly 1`] = `
<!---->
</div>
<div class="done-button container" style="width: 156px; height: 48px;"><span class="label">Done</span></div>
<!---->
</div>
`;
@ -79,5 +80,6 @@ exports[`CreateApiKeyStep.vue renders correctly 1`] = `
<!---->
</div>
<div class="done-button container disabled" style="width: 156px; height: 48px;"><span class="label">Done</span></div>
<!---->
</div>
`;

View File

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SaveApiKeyModal.vue renders correctly 1`] = `
<div class="save-api-modal">
<div class="save-api-modal__container"><svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 24C1.15877e-06 10.7452 10.7452 -1.15877e-06 24 0C37.2548 2.09815e-06 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 -2.09815e-06 37.2548 0 24ZM26.6669 24.0003C26.6669 25.473 25.473 26.6669 24.0003 26.6669C22.5275 26.6669 21.3336 25.473 21.3336 24.0003L21.3336 13.3336C21.3336 11.8609 22.5275 10.6669 24.0003 10.6669C25.473 10.6669 26.6669 11.8609 26.6669 13.3336L26.6669 24.0003ZM24.0003 37.3335C25.473 37.3335 26.6669 36.1396 26.6669 34.6668C26.6669 33.194 25.473 32.0001 24.0003 32.0001C22.5275 32.0001 21.3336 33.194 21.3336 34.6668C21.3336 36.1396 22.5275 37.3335 24.0003 37.3335Z" fill="#F4B000"></path>
</svg>
<h1 class="save-api-modal__container__title">Is Your API Key Saved?</h1>
<p class="save-api-modal__container__message">
API Keys are only displayed once when generated. If you havent saved your key, go back to copy and
paste the API key to your preferred method of storing secrets (i.e. TextEdit, Keybase, etc.)
</p>
<div class="save-api-modal__container__buttons-area">
<div class="back-button container blue-white" style="width: 186px; height: 45px;"><span class="label">Go Back</span></div>
<div class="container" style="width: 186px; height: 45px;"><span class="label">Yes, it's Saved!</span></div>
</div>
</div>
</div>
`;