Skip to content

Commit

Permalink
Merge pull request #4203 from Sage/FE-2823-link-preview
Browse files Browse the repository at this point in the history
fix(toolbar): add missing focus trigger when right key press and last button focused FE-2823
  • Loading branch information
edleeks87 authored Jul 16, 2021
2 parents a6b9e26 + 868a37e commit 2ec8fca
Show file tree
Hide file tree
Showing 33 changed files with 1,166 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ Feature: Accessibility tests - Design System folder

@accessibility
Scenario: Design System Note component
When I open "Design System Note" component page "inline controls" in no iframe
When I open "Design System Note" component page "with inline controls" in no iframe
Then "Note inline controls" component has no accessibility violations

@accessibility
Expand Down
39 changes: 39 additions & 0 deletions cypress/features/regression/designSystem/linkPreview.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Feature: Design System Link Preview component
I want to test Design System Link Preview component

@positive
Scenario: Verify hover color of Link Preview component
Given I open "Design System Link Preview" component page "default story" in no iframe
When I hover mouse onto Link Preview component
Then Link Preview text element has correct background-color "rgb(204, 214, 219)"

@positive
Scenario: Verify border outline color and width of Link Preview on focus
Given I open "Design System Link Preview" component page "default story" in no iframe
When I focus Link Preview component
Then Link Preview has the border outline color "rgb(255, 181, 0)" and width "2px"

@positive
Scenario: Verify border outline color and width of close icon on focus
Given I open "Design System Link Preview" component page "with close icon" in no iframe
When I focus Link Preview close icon
Then Link Preview close icon has the border outline color "rgb(255, 181, 0)" and width "3px"

@positive
Scenario: Check the delete event using the mouse
Given I open "Design System Link Preview Test" component page "default"
And clear all actions in Actions Tab
When I click Link Preview close icon in Iframe
Then "close icon clicked: \"https://www.sage.com\"" action is called in Actions Tab for Link Preview

@positive
Scenario Outline: Check the delete event using <key> key
Given I open "Design System Link Preview Test" component page "default"
And clear all actions in Actions Tab
And I focus Link Preview close icon in Iframe
When I click onto Link Preview close icon using "<key>" key
Then "close icon clicked: \"https://www.sage.com\"" action is called in Actions Tab for Link Preview
Examples:
| key |
| Enter |
| Space |
7 changes: 7 additions & 0 deletions cypress/locators/link-preview/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DLS_ROOT } from "../locators";
import { PILL_CLOSE_ICON } from "../pill/locators";

// component preview locators
export const linkPreviewText = () => cy.get(DLS_ROOT).find("a");
export const linkPreviewCloseIcon = () => cy.get(PILL_CLOSE_ICON);
export const linkPreviewCloseIconIframe = () => cy.iFrame(PILL_CLOSE_ICON);
1 change: 1 addition & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "cypress-axe";
import "cypress-real-events/support";

require("cypress-plugin-tab");

Expand Down
62 changes: 62 additions & 0 deletions cypress/support/step-definitions/link-preview-steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { eventInAction } from "../../locators";
import {
linkPreviewText,
linkPreviewCloseIcon,
linkPreviewCloseIconIframe,
} from "../../locators/link-preview";
import { keyCode } from "../helper";

When("I hover mouse onto Link Preview component", () => {
linkPreviewText().realHover();
});

Then("Link Preview text element has correct background-color {string}", () => {
linkPreviewText().should(
"have.css",
"background-color",
"rgb(204, 214, 219)"
);
});

When("I focus Link Preview component", () => {
linkPreviewText().focus();
});

Then(
"Link Preview has the border outline color {string} and width {string}",
(color, width) => {
linkPreviewText()
.should("have.css", "outline-color", color)
.and("have.css", "outline-width", width);
}
);

When("I focus Link Preview close icon", () => {
linkPreviewCloseIcon().parent().focus();
});

Then(
"Link Preview close icon has the border outline color {string} and width {string}",
(color, width) => {
linkPreviewCloseIcon()
.parent()
.should("have.css", "outline-color", color)
.and("have.css", "outline-width", width);
}
);

When("I click Link Preview close icon in Iframe", () => {
linkPreviewCloseIconIframe().click();
});

When("I focus Link Preview close icon in Iframe", () => {
linkPreviewCloseIconIframe().parent().focus();
});

When("I click onto Link Preview close icon using {string} key", (key) => {
linkPreviewCloseIconIframe().trigger("keydown", keyCode(key));
});

Then("{string} action is called in Actions Tab for Link Preview", (event) => {
eventInAction(event);
});
2 changes: 1 addition & 1 deletion cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"baseUrl": "../node_modules",
"outDir": "dist",
"types": [
"cypress"
"cypress", "cypress-real-events"
]
},
"include": [
Expand Down
4 changes: 4 additions & 0 deletions jest.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@
"transform": {
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
"^.+\\.svg$": "<rootDir>/svgTransform.js"
},
"moduleNameMapper": {
"\\.(png)$": "<rootDir>/__mocks__/imageMock.js"
}

}
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
"bowser": "~1.5.0",
"classnames": "~2.2.6",
"crypto-js": "~3.3.0",
"cypress-real-events": "^1.5.0",
"escape-string-regexp": "^4.0.0",
"immutable": "~3.8.2",
"invariant": "^2.2.4",
Expand Down
55 changes: 55 additions & 0 deletions src/components/link-preview/__internal__/placeholder.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from "react";
import styled from "styled-components";
import { toColor } from "../../../style/utils/color";
import { baseTheme } from "../../../style/themes";

const StyledPlaceHolder = styled.div`
overflow: hidden;
position: relative;
height: 152px;
min-width: 152px;
background-color: ${({ theme }) => theme.editorLinkPreview.background};
`;

const Circle = styled.div`
height: 22px;
width: 22px;
border-radius: 50%;
background-color: ${({ theme }) => theme.editorLinkPreview.hoverBackground};
position: absolute;
left: 22px;
top: 30px;
`;

const Square = styled.div`
height: 200px;
width: 200px;
transform: rotate(45deg);
background-color: ${({ color, theme }) => toColor(theme, color)};
position: absolute;
border-radius: 2%;
top: ${({ top }) => top};
left: ${({ left }) => left};
`;

StyledPlaceHolder.defaultProps = {
theme: baseTheme,
};

Circle.defaultProps = {
theme: baseTheme,
};

Square.defaultProps = {
theme: baseTheme,
};

const Placeholder = () => (
<StyledPlaceHolder data-component="link preview image placeholder">
<Circle />
<Square color="slateTint90" top="120px" left="-64px" />
<Square color="slateTint75" top="96px" left="16px" />
</StyledPlaceHolder>
);

export default Placeholder;
2 changes: 2 additions & 0 deletions src/components/link-preview/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from "./link-preview";
export * from "./link-preview";
1 change: 1 addition & 0 deletions src/components/link-preview/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./link-preview.component";
107 changes: 107 additions & 0 deletions src/components/link-preview/link-preview.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from "react";
import PropTypes from "prop-types";
import {
StyledLinkPreview,
StyledPreviewWrapper,
StyledCloseIconWrapper,
StyledTitle,
StyledDescription,
StyledUrl,
} from "./link-preview.style";
import Image from "../image";
import Preview from "../preview";
import IconButton from "../icon-button";
import Icon from "../icon";
import Placeholder from "./__internal__/placeholder.component";

const SCHEME_SEPARATOR = "://";

const LinkPreview = ({
as,
description,
image,
isLoading,
onClose,
title,
url,
...rest
}) => {
const loadingState = isLoading || !url;
const canRenderAsLink = !loadingState && as !== "div";

const imageProps = () => {
return {
src: image?.url,
alt: image?.alt || "Link preview image",
height: "152px",
};
};

const displayUrl = () => {
if (url?.includes(SCHEME_SEPARATOR)) {
const startIndex =
url.indexOf(SCHEME_SEPARATOR) + SCHEME_SEPARATOR.length;
return url.substring(startIndex);
}

return url;
};

return (
<StyledLinkPreview
as={loadingState ? "div" : as}
tabIndex={loadingState || as === "div" ? -1 : 0}
href={canRenderAsLink ? url : undefined}
target={canRenderAsLink ? "_blank" : undefined}
rel={canRenderAsLink ? "noopener noreferrer" : undefined}
{...rest}
>
{imageProps().src ? <Image {...imageProps()} /> : <Placeholder />}
<StyledPreviewWrapper isLoading={loadingState}>
<Preview loading={loadingState} lines={4}>
<StyledTitle>{title}</StyledTitle>
<StyledDescription>
<div>{description}</div>
</StyledDescription>
<StyledUrl>{displayUrl()}</StyledUrl>
</Preview>
</StyledPreviewWrapper>
{onClose && as === "div" && (
<StyledCloseIconWrapper>
<IconButton
aria-label="link preview close button"
onAction={() => onClose(url)}
>
<Icon type="close" />
</IconButton>
</StyledCloseIconWrapper>
)}
</StyledLinkPreview>
);
};

LinkPreview.propTypes = {
/** Used to set the root element to either am anchor link or div container */
as: PropTypes.oneOf(["a", "div"]),
/** The description to be displayed */
description: PropTypes.string,
/** The config for the image to be displayed */
image: PropTypes.shape({
/** The url string to be passed to image src */
url: PropTypes.string.isRequired,
/** The string to be passed to image alt */
alt: PropTypes.string,
}),
/** Flag to trigger the loading animation */
isLoading: PropTypes.bool,
/** The callback to handle the deleting of a Preview, to hide the close button do not set this prop */
onClose: PropTypes.func,
/** The title to be displayed */
title: PropTypes.string,
/** The url string to be displayed and to serve as the link's src */
url: PropTypes.string,
};

LinkPreview.displayName = "LinkPreview";

export default LinkPreview;
24 changes: 24 additions & 0 deletions src/components/link-preview/link-preview.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
interface ImageShape {
/** The url string to be passed to image src */
url: string;
/** The string to be passed to image alt */
alt?: string;
}

export interface LinkPreviewProps {
description?: string;
/** The config for the image to be displayed */
image?: ImageShape;
/** Flag to trigger the loading animation */
isLoading?: boolean;
/** The callback to handle the deleting of a Preview, to hide the close button do not set this prop */
onClose?: (url: string) => void;
/** The title to be displayed */
title?: string;
/** The url string to be displayed and to serve as the link's src */
url?: string;
}

declare function LinkPreview(props: LinkPreviewProps): JSX.Element;

export default LinkPreview;
Loading

0 comments on commit 2ec8fca

Please sign in to comment.