about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2022-10-09 15:55:32 +0200
committerClaire <claire.github-309c@sitedethib.com>2022-10-28 19:24:02 +0200
commitc36f28ba77d784cff6f5dba109e1286b13204221 (patch)
tree6549f7972cc6aa74dae9d0ca8d566f5aff6006ed
parentdea951cce881a4d7379013d7cb5b8d012b5aa59d (diff)
[Glitch] Fix intermediary responsive layout, accessibility on navigation in web UI
Port 07653246223251052f5150e1e74139bf8ff41ec4 to glitch-soc

Co-authored-by: Yamagishi Kazutoshi <ykzts@desire.sh>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
-rw-r--r--app/javascript/flavours/glitch/components/avatar.js2
-rw-r--r--app/javascript/flavours/glitch/components/logo.js3
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column_link.js24
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js (renamed from app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js)24
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/header.js3
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/navigation_panel.js56
-rw-r--r--app/javascript/flavours/glitch/styles/components/columns.scss3
-rw-r--r--app/javascript/flavours/glitch/styles/components/single_column.scss31
-rw-r--r--app/javascript/flavours/glitch/styles/variables.scss2
9 files changed, 104 insertions, 44 deletions
diff --git a/app/javascript/flavours/glitch/components/avatar.js b/app/javascript/flavours/glitch/components/avatar.js
index ce91d401d..1da4a861c 100644
--- a/app/javascript/flavours/glitch/components/avatar.js
+++ b/app/javascript/flavours/glitch/components/avatar.js
@@ -70,6 +70,8 @@ export default class Avatar extends React.PureComponent {
         onMouseLeave={this.handleMouseLeave}
         style={style}
         data-avatar-of={account && `@${account.get('acct')}`}
+        role='img'
+        aria-label={account.get('acct')}
       />
     );
   }
diff --git a/app/javascript/flavours/glitch/components/logo.js b/app/javascript/flavours/glitch/components/logo.js
index 3570b3644..ee5c22496 100644
--- a/app/javascript/flavours/glitch/components/logo.js
+++ b/app/javascript/flavours/glitch/components/logo.js
@@ -1,7 +1,8 @@
 import React from 'react';
 
 const Logo = () => (
-  <svg viewBox='0 0 261 66' className='logo'>
+  <svg viewBox='0 0 261 66' className='logo' role='img'>
+    <title>Mastodon</title>
     <use xlinkHref='#logo-symbol-wordmark' />
   </svg>
 );
diff --git a/app/javascript/flavours/glitch/features/ui/components/column_link.js b/app/javascript/flavours/glitch/features/ui/components/column_link.js
index d04b869b6..1c475d087 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column_link.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column_link.js
@@ -1,26 +1,29 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { Link } from 'react-router-dom';
+import { NavLink } from 'react-router-dom';
 import Icon from 'flavours/glitch/components/icon';
+import classNames from 'classnames';
 
-const ColumnLink = ({ icon, text, to, onClick, href, method, badge }) => {
+const ColumnLink = ({ icon, text, to, onClick, href, method, badge, transparent, ...other }) => {
+  const className = classNames('column-link', { 'column-link--transparent': transparent });
   const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
+  const iconElement = typeof icon === 'string' ? <Icon id={icon} fixedWidth className='column-link__icon' /> : icon;
 
   if (href) {
     return (
-      <a href={href} className='column-link' data-method={method}>
-        <Icon id={icon} fixedWidth className='column-link__icon' />
+      <a href={href} className={className} data-method={method} title={text} {...other}>
+        {iconElement}
         {text}
         {badgeElement}
       </a>
     );
   } else if (to) {
     return (
-      <Link to={to} className='column-link'>
-        <Icon id={icon} fixedWidth className='column-link__icon' />
+      <NavLink to={to} className={className} title={text} {...other}>
+        {iconElement}
         {text}
         {badgeElement}
-      </Link>
+      </NavLink>
     );
   } else {
     const handleOnClick = (e) => {
@@ -29,8 +32,8 @@ const ColumnLink = ({ icon, text, to, onClick, href, method, badge }) => {
       return onClick(e);
     }
     return (
-      <a href='#' onClick={onClick && handleOnClick} className='column-link' tabIndex='0'>
-        <Icon id={icon} fixedWidth className='column-link__icon' />
+      <a href='#' onClick={onClick && handleOnClick} className={className} title={text} {...other} tabIndex='0'>
+        {iconElement}
         {text}
         {badgeElement}
       </a>
@@ -39,13 +42,14 @@ const ColumnLink = ({ icon, text, to, onClick, href, method, badge }) => {
 };
 
 ColumnLink.propTypes = {
-  icon: PropTypes.string.isRequired,
+  icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
   text: PropTypes.string.isRequired,
   to: PropTypes.string,
   onClick: PropTypes.func,
   href: PropTypes.string,
   method: PropTypes.string,
   badge: PropTypes.node,
+  transparent: PropTypes.bool,
 };
 
 export default ColumnLink;
diff --git a/app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js b/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js
index c30427896..301392a52 100644
--- a/app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js
+++ b/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js
@@ -2,22 +2,27 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
 import { connect } from 'react-redux';
-import { NavLink, withRouter } from 'react-router-dom';
+import ColumnLink from 'flavours/glitch/features/ui/components/column_link';
 import IconWithBadge from 'flavours/glitch/components/icon_with_badge';
 import { List as ImmutableList } from 'immutable';
-import { FormattedMessage } from 'react-intl';
+import { injectIntl, defineMessages } from 'react-intl';
+
+const messages = defineMessages({
+  text: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
+});
 
 const mapStateToProps = state => ({
   count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
 });
 
-export default @withRouter
+export default @injectIntl
 @connect(mapStateToProps)
-class FollowRequestsNavLink extends React.Component {
+class FollowRequestsColumnLink extends React.Component {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
     count: PropTypes.number.isRequired,
+    intl: PropTypes.object.isRequired,
   };
 
   componentDidMount () {
@@ -27,13 +32,20 @@ class FollowRequestsNavLink extends React.Component {
   }
 
   render () {
-    const { count } = this.props;
+    const { count, intl } = this.props;
 
     if (count === 0) {
       return null;
     }
 
-    return <NavLink className='column-link column-link--transparent' to='/follow_requests'><IconWithBadge className='column-link__icon' id='user-plus' count={count} /><FormattedMessage id='navigation_bar.follow_requests' defaultMessage='Follow requests' /></NavLink>;
+    return (
+      <ColumnLink
+        transparent
+        to='/follow_requests'
+        icon={<IconWithBadge className='column-link__icon' id='user-plus' count={count} />}
+        text={intl.formatMessage(messages.text)}
+      />
+    );
   }
 
 }
diff --git a/app/javascript/flavours/glitch/features/ui/components/header.js b/app/javascript/flavours/glitch/features/ui/components/header.js
index 041bdff05..5fdef0af4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/header.js
+++ b/app/javascript/flavours/glitch/features/ui/components/header.js
@@ -11,8 +11,7 @@ import { connect } from 'react-redux';
 const Account = connect(state => ({
   account: state.getIn(['accounts', me]),
 }))(({ account }) => (
-  <Permalink href={account.get('url')} to={`/@${account.get('acct')}`}>
-    <span style={{ display: 'none' }}>{account.get('acct')}</span>
+  <Permalink href={account.get('url')} to={`/@${account.get('acct')}`} title={account.get('acct')}>
     <Avatar account={account} size={35} />
   </Permalink>
 ));
diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js
index 754c651c2..0955b3cf8 100644
--- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js
+++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js
@@ -1,17 +1,34 @@
-import PropTypes from 'prop-types';
 import React from 'react';
-import { FormattedMessage } from 'react-intl';
-import { Link, NavLink } from 'react-router-dom';
-import Icon from 'flavours/glitch/components/icon';
+import PropTypes from 'prop-types';
+import { defineMessages, injectIntl } from 'react-intl';
+import { Link } from 'react-router-dom';
 import TrendsContainer from 'flavours/glitch/features/getting_started/containers/trends_container';
 import { showTrends, timelinePreview } from 'flavours/glitch/initial_state';
-import FollowRequestsNavLink from './follow_requests_nav_link';
+import FollowRequestsColumnLink from './follow_requests_column_link';
 import ListPanel from './list_panel';
 import NotificationsCounterIcon from './notifications_counter_icon';
 import SignInBanner from './sign_in_banner';
 import { preferencesLink, relationshipsLink } from 'flavours/glitch/utils/backend_links';
+import ColumnLink from 'flavours/glitch/features/ui/components/column_link';
+
+const messages = defineMessages({
+  home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
+  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
+  explore: { id: 'explore.title', defaultMessage: 'Explore' },
+  local: { id: 'tabs_bar.local_timeline', defaultMessage: 'Local' },
+  federated: { id: 'tabs_bar.federated_timeline', defaultMessage: 'Federated' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
+  bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
+  lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
+  followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
+  about: { id: 'navigation_bar.about', defaultMessage: 'About' },
+  app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
+});
 
-export default class NavigationPanel extends React.Component {
+export default @injectIntl
+class NavigationPanel extends React.Component {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
@@ -23,24 +40,24 @@ export default class NavigationPanel extends React.Component {
   };
 
   render() {
+    const { intl, onOpenSettings } = this.props;
     const { signedIn } = this.context.identity;
-    const { onOpenSettings } = this.props;
 
     return (
       <div className='navigation-panel'>
         {signedIn && (
           <React.Fragment>
-            <NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
-            <NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
-            <FollowRequestsNavLink />
+            <ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
+            <ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
+            <FollowRequestsColumnLink />
           </React.Fragment>
         )}
 
-        <NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink>
+        <ColumnLink transparent to='/explore' icon='hashtag' text={intl.formatMessage(messages.explore)} />
         {signedIn || timelinePreview && (
           <>
-            <NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
-            <NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
+            <ColumnLink transparent to='/public/local' icon='users' text={intl.formatMessage(messages.local)} />
+            <ColumnLink transparent exact to='/public' icon='globe' text={intl.formatMessage(messages.federated)} />
           </>
         )}
 
@@ -53,17 +70,18 @@ export default class NavigationPanel extends React.Component {
 
         {signedIn && (
           <React.Fragment>
-            <NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='envelope' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
-            <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
-            <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
+            <ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
+            <ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
+            <ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
+            <ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
 
             <ListPanel />
 
             <hr />
 
-            {!!preferencesLink && <a className='column-link column-link--transparent' href={preferencesLink} target='_blank'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>}
-            <a className='column-link column-link--transparent' href='#' onClick={onOpenSettings}><Icon className='column-link__icon' id='cogs' fixedWidth /><FormattedMessage id='navigation_bar.app_settings' defaultMessage='App settings' /></a>
-            {!!relationshipsLink && <a className='column-link column-link--transparent' href={relationshipsLink} target='_blank'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>}
+            {!!preferencesLink && <ColumnLink transparent href={preferencesLink} icon='cog' text={intl.formatMessage(messages.preferences)} />}
+            <ColumnLink transparent href='#' onClick={onOpenSettings} icon='cogs' text={intl.formatMessage(messages.app_settings)} />
+            {!!relationshipsLink && <ColumnLink transparent href={relationshipsLink} icon='users' text={intl.formatMessage(messages.followsAndFollowers)} />}
           </React.Fragment>
         )}
 
diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss
index 6f878f791..3233506cc 100644
--- a/app/javascript/flavours/glitch/styles/components/columns.scss
+++ b/app/javascript/flavours/glitch/styles/components/columns.scss
@@ -214,6 +214,9 @@ $ui-header-height: 55px;
   font-size: 16px;
   padding: 15px;
   text-decoration: none;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 
   &:hover,
   &:focus,
diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss
index 63bebc514..4a987a131 100644
--- a/app/javascript/flavours/glitch/styles/components/single_column.scss
+++ b/app/javascript/flavours/glitch/styles/components/single_column.scss
@@ -275,12 +275,14 @@
 }
 
 @media screen and (max-width: $no-gap-breakpoint - 1px) {
+  $sidebar-width: 285px;
+
   .with-fab .scrollable .item-list:last-child {
     padding-bottom: 5.25rem;
   }
 
   .columns-area__panels__main {
-    width: calc(100% - 55px);
+    width: calc(100% - $sidebar-width);
   }
 
   .columns-area__panels {
@@ -288,10 +290,10 @@
   }
 
   .columns-area__panels__pane--navigational {
-    min-width: 55px;
+    min-width: $sidebar-width;
 
     .columns-area__panels__pane__inner {
-      width: 55px;
+      width: $sidebar-width;
     }
 
     .navigation-panel {
@@ -301,7 +303,6 @@
       height: 100vh;
     }
 
-    .column-link span,
     .navigation-panel__sign-in-banner,
     .navigation-panel__logo,
     .getting-started__trends {
@@ -326,11 +327,31 @@
   }
 }
 
+@media screen and (max-width: $no-gap-breakpoint - 285px - 1px) {
+  $sidebar-width: 55px;
+
+  .columns-area__panels__main {
+    width: calc(100% - $sidebar-width);
+  }
+
+  .columns-area__panels__pane--navigational {
+    min-width: $sidebar-width;
+
+    .columns-area__panels__pane__inner {
+      width: $sidebar-width;
+    }
+
+    .column-link span {
+      display: none;
+    }
+  }
+}
+
 .explore__search-header {
   display: none;
 }
 
-@media screen and (max-width: $no-gap-breakpoint + 285px - 1px) {
+@media screen and (max-width: $no-gap-breakpoint - 1px) {
   .columns-area__panels__pane--compositional {
     display: none;
   }
diff --git a/app/javascript/flavours/glitch/styles/variables.scss b/app/javascript/flavours/glitch/styles/variables.scss
index 4b4f5ffbe..b865b5a2d 100644
--- a/app/javascript/flavours/glitch/styles/variables.scss
+++ b/app/javascript/flavours/glitch/styles/variables.scss
@@ -51,7 +51,7 @@ $media-modal-media-max-width: 100%;
 // put margins on top and bottom of image to avoid the screen covered by image.
 $media-modal-media-max-height: 80%;
 
-$no-gap-breakpoint: 890px;
+$no-gap-breakpoint: 1175px;
 
 $font-sans-serif: 'mastodon-font-sans-serif' !default;
 $font-display: 'mastodon-font-display' !default;