Skip to content

Commit

Permalink
Merge pull request #4450 from kobotoolbox/4234-remove-x-button-from-p…
Browse files Browse the repository at this point in the history
…roject-route

split top tabs from `formViewTabs` file and remove x button
  • Loading branch information
magicznyleszek committed Jul 6, 2023
2 parents e7f54b7 + f013820 commit 75adb9d
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 157 deletions.
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

0 comments on commit 75adb9d

Please sign in to comment.