Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split top tabs from formViewTabs file and remove x button #4450

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 8 additions & 4 deletions jsapp/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import bem from 'js/bem';
import mixins from 'js/mixins';
import MainHeader from 'js/components/header/mainHeader';
import Drawer from 'js/components/drawer';
import FormViewTabs from 'js/components/formViewTabs';
import FormViewSideTabs from 'js/components/formViewSideTabs';
import ProjectTopTabs from 'js/project/projectTopTabs.component';
import PermValidator from 'js/components/permissions/permValidator';
import {assign} from 'utils';
import BigModal from 'js/components/bigModal/bigModal';
Expand Down Expand Up @@ -86,11 +87,14 @@ class App extends React.Component {
</React.Fragment>
}

<bem.PageWrapper__content className='mdl-layout__content' m={pageWrapperContentModifiers}>
<bem.PageWrapper__content
className='mdl-layout__content'
m={pageWrapperContentModifiers}
>
{ !this.isFormBuilder() &&
<React.Fragment>
<FormViewTabs type={'top'} show={this.isFormSingle()} />
<FormViewTabs type={'side'} show={this.isFormSingle()} />
{this.isFormSingle() && <ProjectTopTabs/>}
<FormViewSideTabs show={this.isFormSingle()} />
</React.Fragment>
}
<Outlet />
Expand Down
4 changes: 2 additions & 2 deletions jsapp/js/assetStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class AssetStore extends Reflux.Store {
this.trigger(this.data);
}

/** Returns asset data (if exists). */
getAsset(assetUid: string) {
/** Returns asset object (if exists). */
getAsset(assetUid: string): AssetResponse | undefined {
return this.data[assetUid];
}

Expand Down
1 change: 0 additions & 1 deletion jsapp/js/bemComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ bem.FormView__name = makeBem(bem.FormView, 'name');
bem.FormView__description = makeBem(bem.FormView, 'description');
bem.FormView__subs = makeBem(bem.FormView, 'subs');
// end used in header.es6
bem.FormView__toptabs = makeBem(bem.FormView, 'toptabs');
bem.FormView__sidetabs = makeBem(bem.FormView, 'sidetabs');

bem.FormView__label = makeBem(bem.FormView, 'label');
Expand Down
2 changes: 1 addition & 1 deletion jsapp/js/components/formSummary.es6
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import DocumentTitle from 'react-document-title';
import Icon from 'js/components/common/icon';
import moment from 'moment';
import Chart from 'chart.js';
import {getFormDataTabs} from './formViewTabs';
import {getFormDataTabs} from './formViewSideTabs';
import {
formatDate,
stringToColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import bem from 'js/bem';
import sessionStore from 'js/stores/session';
import assetStore from 'js/assetStore';
import {Link, NavLink} from 'react-router-dom';
import {NavLink} from 'react-router-dom';
import mixins from '../mixins';
import {PERMISSIONS_CODENAMES} from 'js/constants';
import {ROUTES} from 'js/router/routerConstants';
import {withRouter} from 'js/router/legacy';
import {assign} from 'utils';
import {userCan, userCanPartially} from 'js/components/permissions/utils';
import {userCan} from 'js/components/permissions/utils';

export function getFormDataTabs(assetUid) {
return [
Expand Down Expand Up @@ -44,7 +43,7 @@ export function getFormDataTabs(assetUid) {
];
}

class FormViewTabs extends Reflux.Component {
class FormViewSideTabs extends Reflux.Component {
constructor(props) {
super(props);
this.state = {};
Expand Down Expand Up @@ -75,87 +74,6 @@ class FormViewTabs extends Reflux.Component {
}
}

isDataTabEnabled() {
return (
this.state.asset.deployment__identifier != undefined &&
this.state.asset.has_deployment &&
this.state.asset.deployment__submission_count > 0 &&
(
userCan('view_submissions', this.state.asset) ||
userCanPartially('view_submissions', this.state.asset)
)
);
}

renderTopTabs() {
if (this.state.asset === undefined) {
return false;
}

let dataTabClassNames = 'form-view__tab';
if (!this.isDataTabEnabled()) {
dataTabClassNames += ' form-view__tab--disabled';
}

let summaryTabClassNames = 'form-view__tab';
if (!sessionStore.isLoggedIn) {
summaryTabClassNames += ' form-view__tab--disabled';
}

let settingsTabClassNames = 'form-view__tab';
if (
!(
sessionStore.isLoggedIn && (
userCan('change_asset', this.state.asset) ||
userCan('change_metadata_asset', this.state.asset)
)
)
) {
settingsTabClassNames += ' form-view__tab--disabled';
}

return (
<bem.FormView__toptabs>
<NavLink
to={ROUTES.FORM_SUMMARY.replace(':uid', this.state.asset.uid)}
className={summaryTabClassNames}
>
{t('Summary')}
</NavLink>

<NavLink
to={ROUTES.FORM_LANDING.replace(':uid', this.state.asset.uid)}
className='form-view__tab'
>
{t('Form')}
</NavLink>

<NavLink
to={ROUTES.FORM_DATA.replace(':uid', this.state.asset.uid)}
className={dataTabClassNames}
>
{t('Data')}
</NavLink>

<NavLink
to={ROUTES.FORM_SETTINGS.replace(':uid', this.state.asset.uid)}
className={settingsTabClassNames}
>
{t('Settings')}
</NavLink>

{sessionStore.isLoggedIn && (
<Link
to={ROUTES.FORMS}
className='form-view__link form-view__link--close'
>
<i className='k-icon k-icon-close' />
</Link>
)}
</bem.FormView__toptabs>
);
}

renderFormSideTabs() {
var sideTabs = [];

Expand Down Expand Up @@ -276,24 +194,15 @@ class FormViewTabs extends Reflux.Component {
if (!this.props.show) {
return false;
}
if (this.props.type === 'top') {
return (
this.renderTopTabs()
);
}
if (this.props.type === 'side') {
return (
this.renderFormSideTabs()
);
}
return this.renderFormSideTabs();
}
}

reactMixin(FormViewTabs.prototype, Reflux.ListenerMixin);
reactMixin(FormViewTabs.prototype, mixins.contextRouter);
reactMixin(FormViewSideTabs.prototype, Reflux.ListenerMixin);
reactMixin(FormViewSideTabs.prototype, mixins.contextRouter);

FormViewTabs.contextTypes = {
FormViewSideTabs.contextTypes = {
router: PropTypes.object,
};

export default withRouter(FormViewTabs);
export default withRouter(FormViewSideTabs);
99 changes: 99 additions & 0 deletions jsapp/js/project/projectTopTabs.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import classnames from 'classnames';
import React, {useEffect, useState} from 'react';
import {useNavigate} from 'react-router-dom';
import {ROUTES} from 'js/router/routerConstants';
import {userCan, userCanPartially} from 'js/components/permissions/utils';
import {
getRouteAssetUid,
isAnyFormDataRoute,
isFormLandingRoute,
isAnyFormSettingsRoute,
isFormSummaryRoute,
} from 'js/router/routerUtils';
import assetStore from 'js/assetStore';
import sessionStore from 'js/stores/session';
import type {AssetResponse} from 'js/dataInterface';
import styles from './projectTopTabs.module.scss';

export default function ProjectTopTabs() {
// First check if uid is available
const assetUid = getRouteAssetUid();
if (assetUid === null) {
return null;
}

// Setup navigation
const navigate = useNavigate();

const [asset, setAsset] = useState<AssetResponse | undefined>(undefined);

useEffect(() => {
assetStore.whenLoaded(assetUid, setAsset);
}, []);

const isDataTabEnabled =
asset?.deployment__identifier != undefined &&
asset?.has_deployment &&
asset?.deployment__submission_count > 0 &&
(userCan('view_submissions', asset) ||
userCanPartially('view_submissions', asset));

const isSettingsTabEnabled =
sessionStore.isLoggedIn &&
(userCan('change_asset', asset) || userCan('change_metadata_asset', asset));

return (
<nav className={styles.root}>
<ul className={styles.tabs}>
<li
onClick={() =>
navigate(ROUTES.FORM_SUMMARY.replace(':uid', assetUid))
}
className={classnames({
[styles.tab]: true,
[styles.disabled]: !sessionStore.isLoggedIn,
[styles.active]: isFormSummaryRoute(assetUid),
})}
>
{t('Summary')}
</li>

<li
onClick={() =>
navigate(ROUTES.FORM_LANDING.replace(':uid', assetUid))
}
className={classnames({
[styles.tab]: true,
[styles.active]: isFormLandingRoute(assetUid),
})}
>
{t('Form')}
</li>

<li
onClick={() => navigate(ROUTES.FORM_DATA.replace(':uid', assetUid))}
className={classnames({
[styles.tab]: true,
[styles.disabled]: !isDataTabEnabled,
[styles.active]: isAnyFormDataRoute(assetUid),
})}
>
{t('Data')}
</li>

<li
onClick={() =>
navigate(ROUTES.FORM_SETTINGS.replace(':uid', assetUid))
}
className={classnames({
[styles.tab]: true,
[styles.disabled]: !isSettingsTabEnabled,
[styles.active]: isAnyFormSettingsRoute(assetUid),
})}
>
{t('Settings')}
</li>
</ul>
</nav>
);
}
53 changes: 53 additions & 0 deletions jsapp/js/project/projectTopTabs.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@use 'scss/sizes';
@use '~kobo-common/src/styles/colors';

@media print {
.root {
display: none;
}
}

.root {
width: 100%;
}

.tabs {
background: colors.$kobo-white;
border-bottom: sizes.$x1 solid colors.$kobo-gray-92;
text-align: center;
height: sizes.$x48;
position: relative;
display: flex;
flex-direction: row;
justify-content: center;
align-items: stretch;
}

.tab {
background: transparent;
border: none;
border-bottom: sizes.$x2 solid transparent;
text-transform: uppercase;
line-height: sizes.$x48;
margin: 0 sizes.$x30;
font-size: sizes.$x15;
cursor: pointer;
position: relative;
color: colors.$kobo-gray-40;
font-weight: normal;

&:hover,
&.active {
color: colors.$kobo-gray-24;
}

&.active {
font-weight: 700;
border-bottom: sizes.$x4 solid colors.$kobo-teal;
}

&.disabled {
pointer-events: none;
opacity: 0.5;
}
}
12 changes: 12 additions & 0 deletions jsapp/js/router/routerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,16 @@ export function isFormLandingRoute(uid: string): boolean {
return getCurrentPath() === ROUTES.FORM_LANDING.replace(':uid', uid);
}

/** Note that this is `false` for sub-routes of `FORM_DATA`. */
export function isFormDataRoute(uid: string): boolean {
return getCurrentPath() === ROUTES.FORM_DATA.replace(':uid', uid);
}

/** If on `forms/<uid>/data/…` route */
export function isAnyFormDataRoute(uid: string) {
return getCurrentPath().startsWith(ROUTES.FORM_DATA.replace(':uid', uid));
}

export function isFormReportRoute(uid: string): boolean {
return getCurrentPath() === ROUTES.FORM_REPORT.replace(':uid', uid);
}
Expand All @@ -139,10 +145,16 @@ export function isFormMapByRoute(uid: string, viewby: string): boolean {
return getCurrentPath() === ROUTES.FORM_MAP_BY.replace(':uid', uid).replace(':viewby', viewby);
}

/** Note that this is `false` for sub-routes of `FORM_SETTINGS`. */
export function isFormSettingsRoute(uid: string): boolean {
return getCurrentPath() === ROUTES.FORM_SETTINGS.replace(':uid', uid);
}

/** If on `forms/<uid>/settings/…` route */
export function isAnyFormSettingsRoute(uid: string) {
return getCurrentPath().startsWith(ROUTES.FORM_SETTINGS.replace(':uid', uid));
}

export function isFormMediaRoute(uid: string): boolean {
return getCurrentPath() === ROUTES.FORM_MEDIA.replace(':uid', uid);
}
Expand Down