1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-12-12 09:04:14 +02:00

Growth Spike - implement imports to help redirect (#2194)

* implement imports to help redirect

* fix constants
This commit is contained in:
Scott Bishel 2022-02-07 10:33:54 -07:00 committed by GitHub
parent a57196c93f
commit 9c473a6a7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 790 additions and 45 deletions

View File

@ -1,5 +1,283 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should match snapshot 1`] = `
<div>
<div
class="GlobalHeaderSettingsMenu"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="GlobalHeaderComponent__button menu-entry"
>
<i
class="CompassIcon icon-settings-outline SettingsIcon"
/>
</div>
<div
class="Menu noselect left"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
class="MenuOption SubMenuOption menu-option open-left"
id="import"
>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import
</div>
<div
class="SubMenu Menu noselect left-bottom"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import archive
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Trello"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Trello
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Asana"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Asana
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Notion"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Notion
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Jira"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Jira
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Todoist"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Todoist
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
<div
aria-label="Export archive"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Export archive
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption SubMenuOption menu-option open-left"
id="lang"
>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
</div>
<div
aria-label="Random icons"
class="MenuOption SwitchOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Random icons
</div>
<div
class="Switch on"
>
<div
class="octo-switch-inner"
/>
</div>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should match snapshot 1`] = `
<div>
<div
@ -27,21 +305,26 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
class="MenuOption SubMenuOption menu-option open-left"
id="import"
>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import archive
Import
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Export archive"
@ -515,21 +798,26 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu settings menu open should m
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
class="MenuOption SubMenuOption menu-option open-left"
id="import"
>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import archive
Import
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Export archive"

View File

@ -9,10 +9,17 @@ import {render} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import configureStore from 'redux-mock-store'
import {mocked} from 'ts-jest/utils'
import {wrapIntl} from '../../testUtils'
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient'
import GlobalHeaderSettingsMenu from './globalHeaderSettingsMenu'
jest.mock('../../telemetry/telemetryClient')
const mockedTelemetry = mocked(TelemetryClient, true)
describe('components/sidebar/GlobalHeaderSettingsMenu', () => {
const mockStore = configureStore([])
let store = mockStore({})
@ -54,4 +61,21 @@ describe('components/sidebar/GlobalHeaderSettingsMenu', () => {
userEvent.click(container.querySelector('#lang') as Element)
expect(container).toMatchSnapshot()
})
test('imports menu open should match snapshot', () => {
window.open = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<GlobalHeaderSettingsMenu/>
</ReduxProvider>,
)
const {container} = render(component)
userEvent.click(container.querySelector('.menu-entry') as Element)
userEvent.click(container.querySelector('#import') as Element)
expect(container).toMatchSnapshot()
userEvent.click(container.querySelector('[aria-label="Asana"]') as Element)
expect(mockedTelemetry.trackEvent).toBeCalledWith(TelemetryCategory, TelemetryActions.ImportAsana)
})
})

View File

@ -13,6 +13,7 @@ import CheckIcon from '../../widgets/icons/check'
import SettingsIcon from '../../widgets/icons/settings'
import {Constants} from '../../constants'
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient'
import './globalHeaderSettingsMenu.scss'
@ -33,15 +34,40 @@ const GlobalHeaderSettingsMenu = React.memo(() => {
<SettingsIcon/>
</div>
<Menu position='left'>
<Menu.Text
<Menu.SubMenu
id='import'
name={intl.formatMessage({id: 'Sidebar.import', defaultMessage: 'Import'})}
position='left-bottom'
>
<Menu.Text
id='import_archive'
name={intl.formatMessage({id: 'Sidebar.import-archive', defaultMessage: 'Import archive'})}
onClick={async () => Archiver.importFullArchive()}
onClick={async () => {
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ImportArchive)
Archiver.importFullArchive()
}}
/>
{
Constants.imports.map((i) => (
<Menu.Text
key={`${i.id}-import`}
id={`${i.id}-import`}
name={i.displayName}
onClick={() => {
TelemetryClient.trackEvent(TelemetryCategory, i.telemetryName)
window.open(i.href)
}}
/>
))
}
</Menu.SubMenu>
<Menu.Text
id='export'
name={intl.formatMessage({id: 'Sidebar.export-archive', defaultMessage: 'Export archive'})}
onClick={async () => Archiver.exportFullArchive()}
onClick={async () => {
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ExportArchive)
Archiver.exportFullArchive()
}}
/>
<Menu.SubMenu
id='lang'

View File

@ -1,5 +1,303 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/sidebar/SidebarSettingsMenu imports menu open should match snapshot 1`] = `
<div>
<div
class="SidebarSettingsMenu"
>
<div
aria-label="menuwrapper"
class="MenuWrapper"
role="button"
>
<div
class="menu-entry"
>
Settings
</div>
<div
class="Menu noselect top"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
class="MenuOption SubMenuOption menu-option"
id="import"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import
</div>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
<div
class="SubMenu Menu noselect top"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Import archive
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Trello"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Trello
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Asana"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Asana
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Notion"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Notion
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Jira"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Jira
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Todoist"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Todoist
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
<div
aria-label="Export archive"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Export archive
</div>
<div
class="noicon"
/>
</div>
<div
class="MenuOption SubMenuOption menu-option"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
</div>
<div
class="MenuOption SubMenuOption menu-option"
id="theme"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set theme
</div>
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
</div>
<div
aria-label="Random icons"
class="MenuOption SwitchOption menu-option"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Random icons
</div>
<div
class="Switch on"
>
<div
class="octo-switch-inner"
/>
</div>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Cancel
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/sidebar/SidebarSettingsMenu languages menu open should match snapshot 1`] = `
<div>
<div
@ -25,9 +323,8 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
class="MenuOption SubMenuOption menu-option"
id="import"
>
<div
class="noicon"
@ -35,11 +332,17 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
<div
class="menu-name"
>
Import archive
Import
</div>
<div
class="noicon"
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
</div>
<div
aria-label="Export archive"
@ -531,9 +834,8 @@ exports[`components/sidebar/SidebarSettingsMenu settings menu open should match
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
class="MenuOption SubMenuOption menu-option"
id="import"
>
<div
class="noicon"
@ -541,11 +843,17 @@ exports[`components/sidebar/SidebarSettingsMenu settings menu open should match
<div
class="menu-name"
>
Import archive
Import
</div>
<div
class="noicon"
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
</div>
<div
aria-label="Export archive"
@ -686,9 +994,8 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
class="menu-options"
>
<div
aria-label="Import archive"
class="MenuOption TextOption menu-option"
role="button"
class="MenuOption SubMenuOption menu-option"
id="import"
>
<div
class="noicon"
@ -696,11 +1003,17 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
<div
class="menu-name"
>
Import archive
Import
</div>
<div
class="noicon"
<svg
class="SubmenuTriangleIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polygon
points="50,35 75,50 50,65"
/>
</svg>
</div>
<div
aria-label="Export archive"

View File

@ -9,12 +9,19 @@ import {render} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import configureStore from 'redux-mock-store'
import {mocked} from 'ts-jest/utils'
import {wrapIntl} from '../../testUtils'
import {defaultThemeName} from '../../theme'
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient'
import SidebarSettingsMenu from './sidebarSettingsMenu'
jest.mock('../../telemetry/telemetryClient')
const mockedTelemetry = mocked(TelemetryClient, true)
describe('components/sidebar/SidebarSettingsMenu', () => {
const mockStore = configureStore([])
let store = mockStore({})
@ -69,4 +76,21 @@ describe('components/sidebar/SidebarSettingsMenu', () => {
userEvent.click(container.querySelector('#lang') as Element)
expect(container).toMatchSnapshot()
})
test('imports menu open should match snapshot', () => {
window.open = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<SidebarSettingsMenu activeTheme={defaultThemeName}/>
</ReduxProvider>,
)
const {container} = render(component)
userEvent.click(container.querySelector('.menu-entry') as Element)
userEvent.click(container.querySelector('#import') as Element)
expect(container).toMatchSnapshot()
userEvent.click(container.querySelector('[aria-label="Asana"]') as Element)
expect(mockedTelemetry.trackEvent).toBeCalledWith(TelemetryCategory, TelemetryActions.ImportAsana)
})
})

View File

@ -24,6 +24,8 @@ import './sidebarSettingsMenu.scss'
import CheckIcon from '../../widgets/icons/check'
import {Constants} from '../../constants'
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient'
type Props = {
activeTheme: string
}
@ -81,15 +83,40 @@ const SidebarSettingsMenu = React.memo((props: Props) => {
/>
</div>
<Menu position='top'>
<Menu.Text
<Menu.SubMenu
id='import'
name={intl.formatMessage({id: 'Sidebar.import', defaultMessage: 'Import'})}
position='top'
>
<Menu.Text
id='import_archive'
name={intl.formatMessage({id: 'Sidebar.import-archive', defaultMessage: 'Import archive'})}
onClick={async () => Archiver.importFullArchive()}
onClick={async () => {
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ImportArchive)
Archiver.importFullArchive()
}}
/>
{
Constants.imports.map((i) => (
<Menu.Text
key={`${i.id}-import`}
id={`${i.id}-import`}
name={i.displayName}
onClick={() => {
TelemetryClient.trackEvent(TelemetryCategory, i.telemetryName)
window.open(i.href)
}}
/>
))
}
</Menu.SubMenu>
<Menu.Text
id='export'
name={intl.formatMessage({id: 'Sidebar.export-archive', defaultMessage: 'Export archive'})}
onClick={async () => Archiver.exportFullArchive()}
onClick={async () => {
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ExportArchive)
Archiver.exportFullArchive()
}}
/>
<Menu.SubMenu
id='lang'

View File

@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {TelemetryActions} from './telemetry/telemetryClient'
class Constants {
static readonly menuColors: {[key: string]: string} = {
propColorDefault: 'Default',
@ -23,6 +25,40 @@ class Constants {
static readonly versionString = '0.15.0'
static readonly versionDisplayString = 'Mar 2022'
static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/data-and-archives.html'
static readonly imports = [
{
id: 'trello',
displayName: 'Trello',
telemetryName: TelemetryActions.ImportTrello,
href: Constants.archiveHelpPage + '#import-from-trello',
},
{
id: 'asana',
displayName: 'Asana',
telemetryName: TelemetryActions.ImportAsana,
href: Constants.archiveHelpPage + '#import-from-asana',
},
{
id: 'notion',
displayName: 'Notion',
telemetryName: TelemetryActions.ImportNotion,
href: Constants.archiveHelpPage + '#import-from-notion',
},
{
id: 'jira',
displayName: 'Jira',
telemetryName: TelemetryActions.ImportJira,
href: Constants.archiveHelpPage + '#import-from-jira',
},
{
id: 'todoist',
displayName: 'Todoist',
telemetryName: TelemetryActions.ImportTodoist,
href: Constants.archiveHelpPage + '#import-from-todoist',
},
]
static readonly languages = [
{
code: 'en',

View File

@ -30,6 +30,13 @@ export const TelemetryActions = {
DeleteCard: 'deleteCard',
AddTemplateFromCard: 'addTemplateFromCard',
ViewSharedBoard: 'viewSharedBoard',
ImportArchive: 'settings_importArchive',
ImportTrello: 'settings_importTrello',
ImportAsana: 'settings_importAsana',
ImportNotion: 'settings_importNotion',
ImportJira: 'settings_importJira',
ImportTodoist: 'settings_importTodoist',
ExportArchive: 'settings_exportArchive',
}
interface IEventProps {