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

[Link] Warn users when links open in a new window #1247

Merged
merged 1 commit into from
Apr 9, 2019
Merged

Conversation

ry5n
Copy link
Contributor

@ry5n ry5n commented Mar 25, 2019

WHY are these changes introduced?

Fixes Shopify/polaris-ux#146

WHAT is this pull request doing?

This PR takes the position that we should automatically do the right thing for accessibility whenever possible. It automatically adds screen reader text and an icon when the external prop is true.

Unfortunately, implementation is complicated by CSS things, historical things, and international things (see comments below).

I’m looking for comments on this approach before going any further.

How to 🎩

  • The icon will be blank when tophatting—we’re waiting on the correct icon to be available (should be in the next release of Polaris Icons).
  • Change the viewport width to make sure the icon wraps with the last word in the link, never on its own.

🖥 Local development instructions
🗒 General tophatting guidelines
📄 Changelog guidelines

Copy-paste this code in playground/Playground.tsx:
import * as React from 'react';
import {Page, Link, TextContainer} from '../src';

interface State {}

export default class Playground extends React.Component<{}, State> {
  render() {
    return (
      <Page title="Playground">
        <TextContainer>
          <p>
            This is some sample text to make the paragraph longer, in order to
            test text wrapping. Links to external sites, like the{' '}
            <Link url="https://help.shopify.com/manual" external>
              Shopify Help Center
            </Link>
            , have a trailing icon and open in a new browser tab.
          </p>
          <p>
            これはテキストの折り返しをテストするために段落を長くするためのサンプルテキストです。{' '}
            <Link url="https://help.shopify.com/manual" external>
              Andersonヘルプセンター
            </Link>
            などの外部サイトへのリンクには末尾のアイコンがあり、新しいブラウザタブで開きます。
          </p>
        </TextContainer>
      </Page>
    );
  }
}

🎩 checklist

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 March 25, 2019 23:41 Inactive
const className = classNames(styles.Link, monochrome && styles.monochrome);
let childrenMarkup = children;

if (external && typeof children === 'string') {

This comment was marked as outdated.

<span className={styles.IconLockup}>
{lastWord}
<Icon accessibilityLabel={iconLabel} source={ExternalSmallMinor} />
</span>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to center the icon with the text, and to prevent the icon from wrapping by itself to the next line, the best solution I found is to wrap the last word and the icon in an inline-flex container.

This way of preventing unwanted wrapping won’t work with languages like Japanese because those languages don’t use spaces between words. I’m not sure what to do about this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For languages that does not use space between words, you can grab the last character + punctuation as a word. Since each character is consider a word, a single character on the second line is perfectly fine. But punctuation should never be wrap onto the next line by itself.


你好
嗎。


你好嗎

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @michelleyschen! Would we want an icon at the end of a text link to behave the same way as punctuation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I would consider icon a punctuation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I have a new implementation that doesn’t manipulate the children string. Should work for all languages now 😄

@ry5n ry5n self-assigned this Mar 25, 2019
@yourpalsonja
Copy link
Contributor

What if we ignore the content of Link and just rely on the external prop to add a class that:

  1. Adds a(n) :before/:after element that contains the icon (since we know it won't change) and is positioned absolutely, so that it won't wrap to the next line but also
  2. Adds padding or a margin to the end of the element so that the entire link will wrap to the next line if things get too jammed up.
  3. This would work for both RTL languages, and languages like Japanese.

Caveats: Could be weird if someone decides to use a :pseudo element for something else, but I don't think that can happen by default based on my reading of the code.

@ry5n
Copy link
Contributor Author

ry5n commented Mar 27, 2019

@yourpalsonja Thanks for looking at this. That’s an interesting idea! I tried it a couple ways, and I think something like it could work.

Polaris React uses inline SVGs, relying on them for being able to recolor icons for interaction effects like hover. I am not sure, but I don’t think inline SVGs play well with CSS generated content. BUT, if we made the link inline-flex, we would achieve the same overall effect (without needing absolute positioning).

The downside is that as you pointed out, the whole link wraps as a unit, which will cause some disruption to the flow of the text on narrower columns when links are long. Example:

Screen Shot 2019-03-27 at 4 15 22 PM

Both approaches have tradeoffs. Are there other approaches?

@dleroux
Copy link
Contributor

dleroux commented Mar 28, 2019

I tried inserting a &nbsp; at the end of the string and then concatenating the Icon hoping it wouldn't wrap but it did. Guessing because the Icon is html and not just text.

Have we considered something like a Tooltip? In the end visually impaired users are warned of the new window via the rel attribute. A simple tooltip would keep the ui clean and meet the requirement of warning sighted users. It's one of the examples provided by the w3c: https://www.w3.org/TR/WCAG20-TECHS/G201.html

1 caveat with our Tooltip implementation is that the link would not wrap.

@dpersing
Copy link
Contributor

dpersing commented Mar 28, 2019

Have we considered something like a Tooltip?

@dleroux We currently have a few logged a11y issues around tooltips (https://github.com/Shopify/polaris-ux/issues/154, #793), so I'd like to avoid adding more complexity for the link.

The rel attribute isn't actually conveyed to screen reader users via the accessibility API, which is why we've had to implement workarounds like this. There's also concern for sighted keyboard-only users, who will want to be able to fully control their experience, as well as low vision users relying on tools like magnification, and users with cognitive issues, who may not be expecting or notice a new tab/window.

Adds a(n) :before/:after element that contains the icon (since we know it won't change) and is positioned absolutely, so that it won't wrap to the next line but also

@yourpalsonja If we add the icon as a pseudo-element, we'll need a way to provide the text equivalent for the icon as part of the link content. It doesn't appear that accessibilityLabel can be used on the Link component, so I recommended including the icon inline. If we could add that prop, that would be helpful for AT users in general, provided we add some guidance around its use like we have on the Button component.

@yourpalsonja
Copy link
Contributor

@ry5n I was thinking the link wrapping in this case would be a good thing, so the text doesn't look too crowded with the link beside it? I could be wrong though.

@dpersing would we need to provide accessible text for the icon, or could we just add text that is read by the screen reader in addition to the icon? (like, "Visit our docs" [icon]), and a screenreader would read "Visit our docs, external link".

@ry5n
Copy link
Contributor Author

ry5n commented Mar 29, 2019

Here’s a side-by-side of how the 2 wrapping options behave:

Link itself can wrap Link wraps as a whole
Screen Shot 2019-03-28 at 6 24 14 PM Screen Shot 2019-03-28 at 6 24 39 PM

Here’s what the the current PR does. Screen readers will see “(opens a new window)” appended to the link content. (This follows Devon’s recommendation in the original issue):

Screen Shot 2019-03-28 at 6 15 32 PM

@dpersing
Copy link
Contributor

would we need to provide accessible text for the icon, or could we just add text that is read by the screen reader in addition to the icon? (like, "Visit our docs" [icon]), and a screenreader would read "Visit our docs, external link".

@yourpalsonja I think it's 6 of 1 and half a dozen of the other!

I also want to clarify that it's less that it's an external link and more that it's a link that opens a new tab/window. The issue isn't with the content, but with the browser behavior, if that makes sense. @ry5n's "(opens a new window)" text does the trick!

@ry5n
Copy link
Contributor Author

ry5n commented Mar 29, 2019

OK, I think I found another way. This is close to what Dan was thinking. I’ve tested this with different font sizes, and in Chrome, Firefox, Safari and Edge (CodeSandbox doesn’t work in IE11, so I couldn’t test there). I’ve also tested it with our different system fonts (San Francisco, Roboto, and Segoe UI).

https://codesandbox.io/s/9wymvnyrw

Here’s how this works:

  1. It uses a non-breaking space between the Link text and the icon (not sure why this works when it didn’t for Dan).
  2. It wraps the Icon component in a span set to inline-flex so that the icon can sit inline with the text.
  3. A zero-width space character is included in the span along with the Icon. Because the space is an actual character with font metrics (while the icon isn’t), this ensures that the span has the same height as the rest of the text. Because it has the right height, flexbox centers the icon correctly.
  4. The span is pulled backwards with negative margin to negate the width of the non-breaking space. I wanted to use a zero-width non-breaking space, but browsers don’t actually enforce the no-break behavior for this character.

@dpersing
Copy link
Contributor

Looks great when text is resized too, @ry5n ! ❤️

@ry5n
Copy link
Contributor Author

ry5n commented Mar 29, 2019

……except it actually doesn’t work in Polaris React, trying it for real. This is probably what Dan ran into. I’m stumped.

@dleroux
Copy link
Contributor

dleroux commented Mar 29, 2019

Looks like the span around the last word might be it. https://snook.ca/archives/html_and_css/icon-wrap-after-text

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 March 29, 2019 19:22 Inactive
@ry5n
Copy link
Contributor Author

ry5n commented Mar 29, 2019

Looks like the span around the last word might be it.

Yeah. That brings us back to the code in this PR. I pushed an update that fixes cases where there are no spaces. It seems to work OK with Japanese at least.

I also reached out to the international team to see if there are any more gotchas with this approach. Once I’m confident in internationalization things, I can write some tests and will ask for final code review. Thanks all!

@ry5n ry5n requested review from paulstairmand and removed request for yourpalsonja, dleroux, danrosenthal and dpersing March 29, 2019 19:53
Copy link
Contributor

@paulstairmand paulstairmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From an internationalization perspective, the obvious issue I see is with languages that have no spaces between words. But it seems you have a solution to that @ry5n

The only other thing is choosing an icon that is internationally understandable, especially in our major markets. Once we have a suggested icon we can run it past folks in our in-country teams to check this.

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 2, 2019 22:34 Inactive
@@ -1,8 +1,11 @@
import * as React from 'react';

import {classNames} from '@shopify/react-utilities';
import {ChevronLeftMinor} from '@shopify/polaris-icons';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be changed to ExternalSmallMinor once this icon is included in Polaris Icons. I believe this is the last blocker for this PR.

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 2, 2019 22:43 Inactive
@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 2, 2019 23:20 Inactive
@ry5n
Copy link
Contributor Author

ry5n commented Apr 2, 2019

OK folks, I think this is almost there. @dleroux I went back to your approach again, really dug into Snook’s article and codepen, and found a way to make it work with inline SVGs. I’m confident now this will work for multiple languages.

The only real thing still blocking this is the correct icon isn’t available in Polaris Icons yet.

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 3, 2019 15:50 Inactive
@dleroux
Copy link
Contributor

dleroux commented Apr 8, 2019

Nice work! 🎩 looks good.

Is the plan to roll this in now or wait until version 4?

@@ -18,6 +19,26 @@
}
}

// Prevents the icon from wrapping on its own to the next line
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these comments can go, it's pretty self explanatory. Could even call this ExternalIconLockUp if we want more clarity.

@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 8, 2019 17:42 Inactive
@BPScott BPScott temporarily deployed to polaris-react-pr-1247 April 8, 2019 19:44 Inactive
@ry5n ry5n changed the title [WIP][Link] Warn users when links open in a new window [Link] Warn users when links open in a new window Apr 8, 2019
@ry5n
Copy link
Contributor Author

ry5n commented Apr 8, 2019

The external link icon merged this morning, so this is ready for final review 😀

I’d like to get this into the current version—@dleroux do you think it’s a breaking change?

Screen Shot 2019-04-08 at 12 53 10 PM

@dleroux
Copy link
Contributor

dleroux commented Apr 8, 2019

I guess breaking would mean an api change. I just question the importance of a change when it could visually impact a design. Since we are checking for a ‘string’ the risk is minimal, so either way it should be ok.

Copy link
Contributor

@dleroux dleroux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants