+```
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/index.js
new file mode 100644
index 000000000..a7f8ab037
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/index.js
@@ -0,0 +1,52 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { withElementWrapper } from "../withElementWrapper";
+import { useStateValue } from "../../../SharedContext";
+import { getTextColor, shapeConfig } from "../../../../utils/utils";
+import { Link } from "@quintype/components";
+import "./reference.m.css";
+
+const ReferenceBase = ({ element, config = {}, opts = {}, ...restProps }) => {
+ if (!element) return null;
+
+ const configData = useStateValue() || {};
+ const textInvertColor = getTextColor(configData.theme);
+
+ const eleArr = element["story-elements"] && element["story-elements"].map((ref) => ref.metadata);
+ const { showHeadline = true, headlineText } = opts;
+
+ return (
+
+ {eleArr &&
+ eleArr.map((metadata, index) => {
+ const { name = "", description = "", url } = metadata;
+ return (
+
+
+ {showHeadline && (
+
+ {headlineText || name}
+
+ )}
+
{description}
+
+
+ );
+ })}
+
+ );
+};
+
+ReferenceBase.propTypes = {
+ element: PropTypes.shape({
+ "story-elements": PropTypes.arrayOf(
+ PropTypes.shape({
+ metadata: PropTypes.shape({ name: PropTypes.string, description: PropTypes.string, url: PropTypes.string }),
+ })
+ ),
+ }),
+ opts: PropTypes.shape({ hideHeadline: PropTypes.bool }),
+ config: shapeConfig,
+};
+
+export const Reference = withElementWrapper(ReferenceBase);
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.m.css b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.m.css
new file mode 100644
index 000000000..57c1f1670
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.m.css
@@ -0,0 +1,43 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value tablet from viewports;
+
+.wrapper {
+ border-radius: var(--arrow-spacing-xxs);
+ padding: var(--arrow-spacing-m);
+ margin-bottom: var(--arrow-spacing-l);
+ @media (min-width: tablet) {
+ padding: var(--arrow-spacing-l);
+ }
+}
+
+.name {
+ font-size: var(--arrow-fs-m);
+ font-weight: var(--arrow-fw-bold);
+ line-height: var(--arrow-lh-3);
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-l);
+ }
+}
+
+.description {
+ font-size: var(--arrow-fs-s);
+ line-height: var(--arrow-lh-5);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.dark {
+ background-color: var(--arrow-c-mono7);
+ border: solid 1px var(--arrow-c-mono7);
+ color: var(--arrow-c-mono2);
+}
+
+.light {
+ background-color: var(--arrow-c-invert-mono7);
+ border: solid 1px var(--arrow-c-invert-mono7);
+ color: var(--arrow-c-invert-mono2);
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.test.js
new file mode 100644
index 000000000..ac1c72e7b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/reference.test.js
@@ -0,0 +1,28 @@
+import React from "react";
+import { mount, shallow } from "enzyme";
+import { Provider } from "react-redux";
+import { Reference } from ".";
+import { generateStoryElementData, generateStore } from "../../../Fixture";
+
+const element = generateStoryElementData("references");
+
+describe("Reference element", () => {
+ it("Should render default template of the reference element", () => {
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find(Reference)).toHaveLength(1);
+ });
+
+ it("Should render headline if it is enabled", () => {
+ const wrapper = shallow( );
+ expect(wrapper.find({ "data-test-id": "ref-headline" }).length).toEqual(0);
+ });
+
+ it("Should not render headline if it is disabled", () => {
+ const wrapper = shallow( );
+ expect(wrapper.find({ "data-test-id": "ref-headline" }).length).toEqual(0);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/stories.js
new file mode 100644
index 000000000..ae244da94
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Reference/stories.js
@@ -0,0 +1,18 @@
+import React from "react";
+import { Reference } from "./index";
+import { withStore } from "../../../../../storybook";
+import Readme from "./README.md";
+import { generateStoryElementData } from "../../../Fixture";
+import { boolean, text } from "@storybook/addon-knobs";
+
+const element = generateStoryElementData("references");
+
+withStore("Atoms/Story Elements/Reference", {}, Readme).add("Default", () => (
+
+));
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/README.md b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/README.md
new file mode 100644
index 000000000..13ed9be09
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/README.md
@@ -0,0 +1,10 @@
+# Wrapper Story Element
+
+Story element is a a wrapper around `qt/components/StoryElement` which can be used as the fallback element handler component.
+For example, we can use `qt/arrow/StoryElement` for elements like, Table, File, JSEmbed, Polltype, Soundcloud etc
+
+## Usage
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/index.js
new file mode 100644
index 000000000..8c2fa8272
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/index.js
@@ -0,0 +1,21 @@
+import React from "react";
+import { StoryElement as QTCStoryElement } from "@quintype/components";
+import PropTypes from "prop-types";
+import { withElementWrapper } from "../withElementWrapper";
+import { shapeStory, shapeConfig } from "../../../../utils/utils";
+
+const StoryElementBase = ({ element, story = {}, config = {}, ...restProps }) => {
+ return (
+
+
+
+ );
+};
+
+export const StoryElement = withElementWrapper(StoryElementBase);
+
+StoryElementBase.propTypes = {
+ element: PropTypes.object,
+ story: shapeStory,
+ config: shapeConfig,
+};
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/stories.js
new file mode 100644
index 000000000..a4d378f70
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/stories.js
@@ -0,0 +1,20 @@
+import React from "react";
+import { withStore } from "../../../../../storybook";
+import { StoryElement } from "./index";
+import Readme from "./README.md";
+import { generateStoryElementData } from "../../../Fixture";
+
+const elementJsembed = generateStoryElementData("jsembed");
+
+withStore(
+ "Atoms/Story Elements/StoryElement",
+
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+).add("should work with any jsembed ", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/storyelement.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/storyelement.test.js
new file mode 100644
index 000000000..89a21bd9d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/StoryElement/storyelement.test.js
@@ -0,0 +1,13 @@
+import React from "react";
+import { shallow } from "enzyme";
+import { StoryElement } from ".";
+import { generateStoryElementData } from "../../../Fixture";
+import { StoryElement as QTCStoryElement } from "@quintype/components";
+const element = generateStoryElementData("jsembed");
+
+describe("Story Element", () => {
+ it("should render with correct elements", () => {
+ const wrapper = shallow( ).dive();
+ expect(wrapper.find(QTCStoryElement).length).toEqual(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/README.md b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/README.md
new file mode 100644
index 000000000..c55e4d712
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/README.md
@@ -0,0 +1,56 @@
+# Summary
+
+Displays the Summary element of the story
+
+## Usage
+
+#### Summary story element with default template
+
+```jsx
+
+```
+
+#### Summary story element with header template
+
+```jsx
+
+```
+
+#### Summary story element with border template
+
+```jsx
+
+```
+
+#### Summary story element with header background color
+
+```jsx
+const css = { headerBgColor: "#ff214b" };
+
+ ;
+```
+
+#### Summary story element without headline.
+
+```jsx
+const opts = { hideHeadline: true };
+
+ ;
+```
+
+#### Summary story element with custom headline.
+
+```jsx
+const opts = { headline: true };
+ ;
+```
+
+#### Replace default Summary element template with custom template passed using render
+
+```jsx
+const customTemplate = ({ element }) => ;
+
+ ;
+```
+
+
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/index.js
new file mode 100644
index 000000000..10d629138
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/index.js
@@ -0,0 +1,69 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { withElementWrapper } from "../withElementWrapper";
+import { getTextColor, updateContentLinks, shapeStory, shapeConfig } from "../../../../utils/utils";
+
+import "./summary.m.css";
+import { useStateValue } from "../../../SharedContext";
+
+const SummaryBase = ({
+ element,
+ template = "",
+ opts = {},
+ css = {},
+ story = {},
+ config = {},
+ render,
+ ...restProps
+}) => {
+ const configData = useStateValue() || {};
+ const textInvertColor = getTextColor(configData.theme);
+ const content = element.text;
+ if (!content) return null;
+
+ const { headerBgColor } = css;
+ const { isExternalLink = true, headline = "Summary", hideHeadline = false } = opts;
+ const text = (isExternalLink && updateContentLinks(content)) || content;
+
+ const supportedTemplates = ["header", "border"];
+ const templateStyle = supportedTemplates.includes(template) ? `summary-${template}` : "summary";
+
+ const contentBorder = hideHeadline ? `content-border` : "";
+ const renderTemplate = template === "header" || template === "border";
+ const updateHeaderColor = renderTemplate ? `${headerBgColor}` : "";
+ const textColor = renderTemplate ? getTextColor(headerBgColor) : "";
+ const updateContentColor = template === "header" ? "" : textInvertColor;
+
+ return (
+
+ {!hideHeadline && (
+
+ )}
+
+
+ );
+};
+
+SummaryBase.propTypes = {
+ element: PropTypes.shape({ text: PropTypes.string }),
+ template: PropTypes.string,
+ opts: PropTypes.shape({ isExternalLink: PropTypes.bool, headline: PropTypes.string, hideHeadline: PropTypes.bool }),
+ story: shapeStory,
+ config: shapeConfig,
+ render: PropTypes.func,
+ css: PropTypes.shape({ headerBgColor: PropTypes.string }),
+};
+
+export const Summary = withElementWrapper(SummaryBase);
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/stories.js
new file mode 100644
index 000000000..da1d79319
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/stories.js
@@ -0,0 +1,33 @@
+import React from "react";
+import { withStore, optionalSelect } from "../../../../../storybook";
+import { generateStoryElementData } from "../../../Fixture";
+import Readme from "./README.md";
+import { Summary } from "./index";
+import { color, boolean, text } from "@storybook/addon-knobs";
+
+const element = generateStoryElementData("summary");
+
+const summaryTemplate = {
+ default: "",
+ header: "header",
+ border: "border",
+};
+
+withStore("Atoms/Story Elements/Summary", {}, Readme)
+ .add("Default", () => (
+
+ ))
+ .add("Custom", () => {
+ // eslint-disable-next-line react/prop-types
+ const customTemplate = ({ element }) => ;
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.m.css b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.m.css
new file mode 100644
index 000000000..e1cfee7a2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.m.css
@@ -0,0 +1,122 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+
+.headline {
+ font-size: var(--arrow-fs-s);
+ font-weight: var(--arrow-fw-bold);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.summary:global(.arrow-component) .content p {
+ font-style: italic;
+}
+
+.summary:global(.arrow-component) .content > p,
+.summary-header:global(.arrow-component) .content > p,
+.summary-border:global(.arrow-component) .content > p,
+.dark {
+ font-size: var(--arrow-fs-s);
+ color: var(--arrow-c-dark);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.summary:global(.arrow-component) .content.light > p,
+.summary-header:global(.arrow-component) .content.light > p,
+.summary-border:global(.arrow-component) .content.light > p {
+ color: var(--arrow-c-light);
+}
+
+.dark {
+ color: var(--arrow-c-mono1);
+}
+.light {
+ color: var(--arrow-c-invert-mono1);
+}
+
+/* default template */
+.summary .headline {
+ margin-bottom: var(--arrow-spacing-xs);
+ font-size: var(--arrow-fs-s);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-l);
+ }
+}
+
+/* headline template */
+.summary-header .headline {
+ padding: var(--arrow-spacing-xs) var(--arrow-spacing-s);
+ font-size: var(--arrow-fs-s);
+ background-color: var(--arrow-c-brand1);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ padding: var(--arrow-spacing-xs) var(--arrow-spacing-l);
+ }
+}
+.summary-header .content {
+ background-color: var(--arrow-c-invert-mono7);
+ padding: var(--arrow-spacing-s);
+ @media (min-width: mobile) {
+ padding: var(--arrow-spacing-l);
+ }
+}
+
+/* border template */
+.summary-border .heading-wrapper {
+ display: flex;
+ margin-bottom: -22px;
+}
+
+.summary-border .heading-wrapper::before {
+ content: " ";
+ position: relative;
+ width: var(--arrow-spacing-m);
+ border-top: 1px solid var(--arrow-c-dark);
+ align-self: center;
+}
+
+.summary-border .heading-wrapper.light::before {
+ border-top: 1px solid var(--arrow-c-light);
+}
+
+.summary-border .heading-wrapper::after {
+ content: " ";
+ position: relative;
+ flex: 1 1 auto;
+ border-top: 1px solid var(--arrow-c-dark);
+ align-self: center;
+ margin-right: 0;
+}
+
+.summary-border .heading-wrapper.light::after {
+ border-top: 1px solid var(--arrow-c-light);
+}
+
+.summary-border .content {
+ border: 1px solid var(--arrow-c-dark);
+ border-top: none;
+ padding: var(--arrow-spacing-l);
+}
+
+.summary-border .content.light {
+ border: 1px solid var(--arrow-c-light);
+}
+.summary-border .headline {
+ background-color: var(--arrow-c-brand1);
+ padding: var(--arrow-spacing-xs) var(--arrow-spacing-l);
+ line-height: var(--arrow-lh-4);
+ border-radius: 2px;
+ margin: 0 var(--arrow-spacing-xs);
+}
+
+.summary-border .content-border.dark {
+ border: 1px solid var(--arrow-c-mono5);
+}
+
+.summary-border .content-border.light {
+ border: 1px solid var(--arrow-c-invert-mono5);
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.test.js
new file mode 100644
index 000000000..0dfc24498
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Summary/summary.test.js
@@ -0,0 +1,50 @@
+import * as React from "react";
+import { shallow, mount } from "enzyme";
+import expect from "expect";
+import { generateStoryElementData } from "../../../Fixture";
+import { Summary } from "./index";
+
+const element = generateStoryElementData("summary");
+
+describe("Summary Story Element", () => {
+ it("Should render default template", () => {
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "summary" }).prop("className")).toMatch(/summary/);
+ });
+
+ it("Should render border left template", () => {
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "summary" }).prop("className")).toMatch(/summary-header/);
+ });
+
+ it("Should render border top template", () => {
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "summary" }).prop("className")).toMatch(/summary-border/);
+ });
+
+ it("Should render template without headline", () => {
+ const wrapper = shallow( );
+ expect(wrapper.find({ "data-test-id": "summary-headline" }).length).toEqual(0);
+ });
+
+ it("Should render template with custom headline", () => {
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "summary-headline" }).text()).toEqual("in short");
+ });
+
+ it("Should render template with header background color", () => {
+ const wrapper = mount( );
+ expect(wrapper.html()).toBe(
+ ``
+ );
+ });
+
+ it("Should render custom template", () => {
+ // eslint-disable-next-line react/prop-types
+ const customTemplate = ({ element }) => ;
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe(
+ "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries , but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
"
+ );
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Text/README.md b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/README.md
new file mode 100644
index 000000000..eed9bfefd
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/README.md
@@ -0,0 +1,32 @@
+# Text Story Element
+
+Displays the text story element of the story
+
+## Usage
+
+#### Default text story element
+```jsx
+
+```
+
+#### Text story element with external links opening in new tab
+```jsx
+const opts = { isExternalLink: true };
+
+
+```
+
+#### Text story element as promotional message
+```jsx
+const promotionalMessage = { ...element, metadata: { "promotional-message": true } };
+
+```
+
+#### Replace default text story element template with custom template passed using render
+```jsx
+const customTemplate = ({element}) => ;
+
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Text/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/index.js
new file mode 100644
index 000000000..ee17bcb09
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/index.js
@@ -0,0 +1,41 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { withElementWrapper } from "../withElementWrapper";
+import { updateContentLinks, shapeStory, shapeConfig, getTextColor } from "../../../../utils/utils";
+import "./text.m.css";
+import { useStateValue } from "../../../SharedContext";
+
+const TextBase = ({ element = {}, opts = {}, css = {}, story = {}, config = {}, ...restProps }) => {
+ const content = element.text;
+ if (!content) return null;
+ const configData = useStateValue() || {};
+ const textInvertColor = getTextColor(configData.theme);
+ const { textColor } = css;
+ const { isExternalLink = true } = opts;
+ const text = (isExternalLink && updateContentLinks(content)) || content;
+ const isPromotionalMessage = get(element, ["metadata", "promotional-message"], false);
+ const updatedStyle = isPromotionalMessage ? "promotionalMessage" : "textElement";
+
+ return (
+
+ );
+};
+
+TextBase.propTypes = {
+ element: PropTypes.shape({ text: PropTypes.string }),
+ opts: PropTypes.shape({ isExternalLink: PropTypes.bool }),
+ css: PropTypes.shape({ hyperlinkColor: PropTypes.string, textColor: PropTypes.string }),
+ story: shapeStory,
+ config: shapeConfig,
+};
+
+export const Text = withElementWrapper(TextBase);
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Text/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/stories.js
new file mode 100644
index 000000000..a90e83907
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/stories.js
@@ -0,0 +1,32 @@
+import React from "react";
+import { withStore } from "../../../../../storybook";
+import { Text } from "./index";
+import Readme from "./README.md";
+import { generateStoryElementData } from "../../../Fixture";
+import { boolean, color } from "@storybook/addon-knobs";
+
+const element = generateStoryElementData("text");
+
+withStore("Atoms/Story Elements/Text", {}, Readme)
+ .add("Default", () => (
+
+ ))
+ .add("Promotional Message", () => {
+ const promotionalMessage = { ...element, metadata: { "promotional-message": true } };
+ return (
+
+ );
+ })
+ .add("Custom", () => {
+ // eslint-disable-next-line react/prop-types
+ const customTemplate = ({ element }) => ;
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.m.css b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.m.css
new file mode 100644
index 000000000..1dfcd4761
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.m.css
@@ -0,0 +1,79 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+
+.promotionalMessage p {
+ font-size: var(--arrow-fs-tiny);
+ padding-bottom: var(--arrow-spacing-xxs);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-xs);
+ padding-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.textElement p,
+.dark {
+ line-height: var(--arrow-lh-5);
+ font-size: var(--arrow-fs-s);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.promotionalMessage a,
+.textElement a {
+ color: var(--textElementHyperlinkColor);
+}
+
+.textElement a:hover {
+ border-bottom: 1px solid var(--textElementHyperlinkColor);
+ transition: border 0.3s ease;
+}
+
+.textElement.light p {
+ color: var(--arrow-c-invert-mono2);
+}
+
+.promotionalMessage a:hover {
+ color: var(--arrow-c-brand1);
+ border-bottom: 1px solid;
+ padding-bottom: 3px;
+ transition: all 0.3s ease 0s;
+}
+
+.promotionalMessage.dark p {
+ color: var(--arrow-c-mono4);
+}
+.promotionalMessage.light p {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.textElement ul,
+.textElement ol {
+ list-style: initial;
+ padding: var(--arrow-spacing-m) 0 0 var(--arrow-spacing-l);
+ @media (min-width: mobile) {
+ padding: var(--arrow-spacing-m) 0 0 var(--arrow-spacing-xl);
+ }
+}
+
+.textElement ol {
+ list-style: auto;
+}
+
+.textElement :global .cta-anchor {
+ background-color: var(--arrow-c-brand1);
+ color: var(--arrow-c-light);
+ border-radius: var(--arrow-spacing-xxs);
+ box-shadow: 0 var(--arrow-spacing-xxs) var(-arrow-spacing-xs) 0 var(--arrow-c-mono6);
+ font-size: var(--arrow-fs-s);
+ padding: var(--arrow-spacing-s) var(--arrow-spacing-m);
+ display: block;
+ text-align: center;
+
+ @media (min-width: mobile) {
+ display: inline-block;
+ font-size: var(--arrow-fs-m);
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-l);
+ }
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.test.js
new file mode 100644
index 000000000..86b8a259d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Text/text.test.js
@@ -0,0 +1,42 @@
+import * as React from "react";
+import { shallow, mount } from "enzyme";
+import { Text } from "./index";
+import { generateStoryElementData } from "../../../Fixture";
+
+const element = generateStoryElementData("text");
+
+describe("Text Story Element", () => {
+ it("should render default template", () => {
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "text" }).prop("className")).toMatch(/text-element/);
+ });
+
+ it("should render promotional message when it is enabled", () => {
+ const promotionalMessage = { ...element, metadata: { "promotional-message": true } };
+ const wrapper = mount( );
+ expect(wrapper.find({ "data-test-id": "promotional-message" }).prop("className")).toMatch(/promotionalMessage/);
+ });
+
+ it("should render default template with external link", () => {
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe(
+ "Virtual reality is the air guitar solo of modern cinema: a frenetic imagined activity in a made-up world that exists one level below the already made-up world of the story. Steven Spielberg 2019s Ready Player One .
"
+ );
+ });
+
+ it("should render default template without external link", () => {
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe(
+ 'Virtual reality is the air guitar solo of modern cinema: a frenetic imagined activity in a made-up world that exists one level below the already made-up world of the story. Steven Spielberg 2019s Ready Player One .
'
+ );
+ });
+
+ it("should render custom template", () => {
+ // eslint-disable-next-line react/prop-types
+ const customTemplate = ({ element }) => ;
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe(
+ 'Virtual reality is the air guitar solo of modern cinema: a frenetic imagined activity in a made-up world that exists one level below the already made-up world of the story. Steven Spielberg 2019s Ready Player One .
'
+ );
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Video/README.md b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/README.md
new file mode 100644
index 000000000..eb3a81883
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/README.md
@@ -0,0 +1,9 @@
+# Video Story Element
+
+Video story element is a simple component used to display a video story.
+
+## Usage
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Video/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/index.js
new file mode 100644
index 000000000..e2aac544e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/index.js
@@ -0,0 +1,23 @@
+import React from "react";
+import { StoryElement } from "@quintype/components";
+import PropTypes from "prop-types";
+import { withElementWrapper } from "../withElementWrapper";
+import { shapeStory, shapeConfig } from "../../../../utils/utils";
+import "./video.m.css";
+
+const Videobase = ({ element, story = {}, config = {}, loadIframeOnClick = false, ...restProps }) => {
+ return (
+
+
+
+ );
+};
+
+Videobase.propTypes = {
+ element: PropTypes.object,
+ story: shapeStory,
+ config: shapeConfig,
+ loadIframeOnClick: PropTypes.bool,
+};
+
+export const Video = withElementWrapper(Videobase);
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Video/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/stories.js
new file mode 100644
index 000000000..9bfc981ec
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/stories.js
@@ -0,0 +1,24 @@
+import React from "react";
+import { withStore } from "../../../../../storybook";
+import { Video } from "./index";
+import Readme from "./README.md";
+import { generateStoryElementData } from "../../../Fixture";
+
+const elementYoutube = generateStoryElementData("youtube-video");
+const elementDailyMotion = generateStoryElementData("dailymotion-video");
+const elementTwitter = generateStoryElementData("tweet");
+withStore(
+ "Atoms/Story Elements/Video",
+
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .add("Default with youtube video", () => )
+ .add("Dailymotion video", () => )
+ .add("Twitter video", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.m.css b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.m.css
new file mode 100644
index 000000000..f40a4c4eb
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.m.css
@@ -0,0 +1,90 @@
+/*eslint-disable scss/operator-no-unspaced */
+.container {
+ :global {
+ .youtube-playBtn {
+ position: absolute;
+ background-color: var(--arrow-c-mono4);
+ border-radius: 50%/10%;
+ font-size: 0;
+ height: 48px;
+ width: 70px;
+ top: 50%;
+ left: 50%;
+ border: none;
+ cursor: pointer;
+ z-index: var(--z-index-1);
+ transform: translateX(-50%) translateY(-50%);
+ }
+
+ .youtube-playBtn:hover {
+ background-color: var(--arrow-c-accent4);
+ }
+
+ .youtube-playBtn::after {
+ content: "";
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 9px 0 9px 18px;
+ border-color: transparent transparent transparent var(--arrow-c-invert-mono3);
+ margin: 0 auto;
+ }
+
+ .thumbnail-wrapper {
+ position: relative;
+ padding-top: 56.25%;
+ height: 100%;
+ width: 100%;
+ img,
+ iframe {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ height: 100%;
+ object-fit: cover;
+ width: 100%;
+ }
+ iframe {
+ z-index: var(--z-index-9);
+ }
+ }
+
+ .story-element-jsembed .thumbnail-wrapper img {
+ width: 100%;
+ }
+
+ .dailymotion-playBtn {
+ background-color: var(--arrow-c-invert-mono5);
+ border-radius: 50%;
+ font-size: 0;
+ height: 64px;
+ padding: 0;
+ width: 64px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ border: none;
+ cursor: pointer;
+ z-index: var(--z-index-1);
+ transform: translateX(-50%) translateY(-50%);
+ }
+
+ .dailymotion-playBtn:hover {
+ background-color: var(--arrow-c-brand1);
+ }
+
+ .dailymotion-playBtn::after {
+ position: relative;
+ content: "";
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 12px 0 12px 18px;
+ border-color: transparent transparent transparent var(--arrow-c-invert-mono3);
+ margin: 0 auto;
+ left: 3px;
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.test.js
new file mode 100644
index 000000000..d41f62b63
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/Video/video.test.js
@@ -0,0 +1,13 @@
+import React from "react";
+import { shallow } from "enzyme";
+import { Video } from ".";
+import { generateStoryElementData } from "../../../Fixture";
+import { StoryElement } from "@quintype/components";
+const element = generateStoryElementData("youtube-video");
+
+describe("Video Story Element", () => {
+ it("should render with correct elements", () => {
+ const wrapper = shallow( ).dive();
+ expect(wrapper.find(StoryElement).length).toEqual(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/README.md b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/README.md
new file mode 100644
index 000000000..c234c4800
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/README.md
@@ -0,0 +1,18 @@
+# Image Gallery Element
+
+Displays the image gallery story element of the story
+
+## Usage
+
+#### Default Image story element
+
+```jsx
+ );
+```
+#### template (with 6 images and rest count on the last image for desktop and 4 images for mobile and rest count on last image) Image story element
+
+```jsx
+ );
+```
+
+
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.m.css b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.m.css
new file mode 100644
index 000000000..c8e08d45a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.m.css
@@ -0,0 +1,52 @@
+@value viewports: "../../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+
+.image-gallery {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-gap: var(--arrow-spacing-xxs);
+ @media (min-width: mobile) {
+ grid-template-columns: repeat(3, 1fr);
+ grid-gap: var(--arrow-spacing-xs);
+ }
+}
+
+.image-gallery figure,
+.image-gallery .image {
+ padding-top: 100%;
+}
+
+.image-gallery figure.image {
+ position: relative;
+ cursor: pointer;
+}
+
+.image img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 100%;
+}
+
+.template-2 .left-images {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0.4);
+ font-size: var(--arrow-fs-l);
+ color: var(--arrow-c-light);
+}
+.left-images::before {
+ content: "+";
+}
+
+.hyperlink-gallery-image {
+ position: relative;
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.test.js
new file mode 100644
index 000000000..f4758a2f5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/image-gallery.test.js
@@ -0,0 +1,19 @@
+import React from "react";
+import { mount } from "enzyme";
+import { Provider } from "react-redux";
+import { ImageGallery } from ".";
+import { generateStoryElementData, generateStore } from "../../../Fixture";
+import { FullScreenImages } from "../../../Molecules/FullScreenImages";
+
+const element = generateStoryElementData("image-gallery");
+
+describe("Image Gallery Story Element", () => {
+ it("should render with correct elements", () => {
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find(FullScreenImages).length).toEqual(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/index.js b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/index.js
new file mode 100644
index 000000000..57ed5f329
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/index.js
@@ -0,0 +1,82 @@
+/* eslint-disable no-unused-vars */
+import React from "react";
+import PropTypes from "prop-types";
+import { ResponsiveImage } from "@quintype/components";
+import { withElementWrapper } from "../withElementWrapper";
+import { FullScreenImages } from "../../../Molecules/FullScreenImages";
+
+import { shapeStory, shapeConfig, clientWidth } from "../../../../utils/utils";
+
+import "./image-gallery.m.css";
+import { HyperLink } from "../../Hyperlink";
+
+const ImageGalleryBase = ({ element, template = "", opts = {}, story = {}, config = {}, ...restProps }) => {
+ if (!element) return null;
+ const isMobile = clientWidth("mobile");
+ const imageArr = element["story-elements"];
+ const imageBaseOnTemp = template === "template-2" && imageArr.length;
+ const renderImageDesktop = imageBaseOnTemp > 6 ? imageArr.slice(0, 6) : imageArr;
+ const renderMobile = imageBaseOnTemp > 4 ? imageArr.slice(0, 4) : imageArr;
+ const renderImage = isMobile ? renderMobile : renderImageDesktop;
+ const classes = template === "template-2" ? "template-2" : "";
+ const hyperlinkClass = (hyperlink) => (hyperlink ? "hyperlink-gallery-image" : "");
+
+ const LeftImages = () => {
+ const imageCountDesktop = imageBaseOnTemp > 6 && imageArr.length - 6;
+ const imageCountMobile = imageBaseOnTemp > 4 && imageArr.length - 4;
+ const imageCount = isMobile ? imageCountMobile : imageCountDesktop;
+ return {imageCount}
;
+ };
+
+ const ImageGalleryImagesTemplate = ({ onClickHandler }) => {
+ const images = renderImage.map((image, index) => (
+
+ onClickHandler && onClickHandler(index)}
+ >
+
+
+ {image.hyperlink && }
+
+ ));
+
+ return (
+
+ {images}
+
+ );
+ };
+
+ ImageGalleryImagesTemplate.propTypes = {
+ onClickHandler: PropTypes.func,
+ };
+
+ return ;
+};
+
+ImageGalleryBase.propTypes = {
+ element: PropTypes.shape({
+ "story-elements": PropTypes.array,
+ }),
+ opts: PropTypes.shape({ imageWidths: PropTypes.array }),
+ story: shapeStory,
+ config: shapeConfig,
+ template: PropTypes.string,
+};
+
+export const ImageGallery = withElementWrapper(ImageGalleryBase);
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/stories.js b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/stories.js
new file mode 100644
index 000000000..e52770333
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/imageGallery/stories.js
@@ -0,0 +1,23 @@
+import React from "react";
+import { withStore, optionalSelect } from "../../../../../storybook";
+import { ImageGallery } from "./index";
+import Readme from "./README.md";
+import { generateStoryElementData } from "../../../Fixture";
+
+const element = generateStoryElementData("image-gallery");
+const imgaeGalleryTemplate = {
+ default: "",
+ template2: "template-2",
+};
+
+withStore(
+ "Atoms/Story Elements/Image gallery",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+).add("Default", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.js b/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.js
new file mode 100644
index 000000000..91ffd51e6
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.js
@@ -0,0 +1,14 @@
+/* eslint-disable react/display-name, react/prop-types */
+import React from "react";
+import PropTypes from "prop-types";
+
+export const withElementWrapper = (StoryElement) => (props) => {
+ const { element, render, ...restProps } = props;
+ if (!element) return null;
+ if (render && typeof render === "function") return render(props);
+ return ;
+};
+
+withElementWrapper.propTypes = {
+ StoryElement: PropTypes.element,
+};
diff --git a/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.test.js b/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.test.js
new file mode 100644
index 000000000..e3bb59ac1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryElements/withElementWrapper.test.js
@@ -0,0 +1,31 @@
+/* eslint-disable react/prop-types */
+import * as React from "react";
+import { shallow } from "enzyme";
+import { withElementWrapper } from "./withElementWrapper";
+
+describe("Story Element Wrapper", () => {
+ const element = {
+ description: "",
+ type: "text",
+ metadata: {},
+ text: "Coronavirus in India News Live Updates
",
+ };
+ const StoryElement = ({ element }) =>
;
+ const Component = withElementWrapper(StoryElement);
+
+ it("should return null if element not present", () => {
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe(null);
+ });
+
+ it("should call render if it's present and is a function", () => {
+ const customTemplate = ({ element }) => ;
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe("Coronavirus in India News Live Updates
");
+ });
+
+ it("should return the component passed if element is present and render is not valid", () => {
+ const wrapper = shallow( );
+ expect(wrapper.html()).toBe("Coronavirus in India News Live Updates
");
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryHeadline/index.js b/app/isomorphic/arrow/components/Atoms/StoryHeadline/index.js
new file mode 100644
index 000000000..b71b2bc7f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryHeadline/index.js
@@ -0,0 +1,36 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { useStateValue } from "../../SharedContext";
+import { getTextColor } from "../../../utils/utils";
+
+import "./story-headline.m.css";
+import { PremiumStoryIcon } from "../PremiumStoryIcon";
+
+export const StoryHeadline = ({ story, premiumStoryIconConfig = {} }) => {
+ const config = useStateValue() || {};
+ const textColor = getTextColor(config.theme);
+ const { iconColor = "#F7B500", iconStyle = "star", enablePremiumStoryIcon = false } = premiumStoryIconConfig;
+ const premiumStory = enablePremiumStoryIcon && get(story, ["access"]) === "subscription";
+ const wrapperClass = premiumStory ? "premium-wrapper" : "wrapper";
+ const iconSize = "40px";
+ return (
+
+ {premiumStory && (
+
+ )}
+
+
+
+
+ );
+};
+
+StoryHeadline.propTypes = {
+ story: PropTypes.object.isRequired,
+ premiumStoryIconConfig: PropTypes.shape({
+ iconColor: PropTypes.string,
+ iconType: PropTypes.string,
+ enablePremiumStoryIcon: PropTypes.bool,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Atoms/StoryHeadline/story-headline.m.css b/app/isomorphic/arrow/components/Atoms/StoryHeadline/story-headline.m.css
new file mode 100644
index 000000000..d7e332f98
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryHeadline/story-headline.m.css
@@ -0,0 +1,36 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ display: block;
+}
+
+:global(.arrow-component.arr--story-headline h1) {
+ margin: 0;
+}
+
+.headline {
+ margin-bottom: var(--arrow-spacing-xxs);
+ color: var(--arrow-c-mono2);
+ font-weight: var(--arrow-fw-semi-bold);
+ text-transform: unset;
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.premium-wrapper .headline {
+ display: inline;
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+html[dir="rtl"] {
+ .headline bdi {
+ direction: rtl;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Atoms/StoryTags/README.md b/app/isomorphic/arrow/components/Atoms/StoryTags/README.md
new file mode 100644
index 000000000..dfc3ed793
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryTags/README.md
@@ -0,0 +1,9 @@
+## Story Tags
+
+Story tags describes the story and makes it easy for the readers to find what they're looking for.
+
+## Usage
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Atoms/StoryTags/index.js b/app/isomorphic/arrow/components/Atoms/StoryTags/index.js
new file mode 100644
index 000000000..f3be476e8
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryTags/index.js
@@ -0,0 +1,33 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes, { string } from "prop-types";
+import { useStateValue } from "../../SharedContext";
+import { Link } from "@quintype/components";
+import "./tags.m.css";
+import { getTextColor } from "../../../utils/utils";
+
+export const StoryTags = ({ tags = [], template = "", config, borderColor = "" }) => {
+ const configData = useStateValue() || {};
+ const textColor = getTextColor(configData.theme);
+ const mountAt = get(config, ["mountAt"], "");
+ const templateStyle = template === "roundCorners" ? "roundCorners" : "";
+ return (
+
+ {tags.length > 0 &&
+ tags.map((tag, index) => (
+
+
+ {tag.name}
+
+
+ ))}
+
+ );
+};
+
+StoryTags.propTypes = {
+ tags: PropTypes.array,
+ borderColor: PropTypes.string,
+ config: PropTypes.object,
+ template: string,
+};
diff --git a/app/isomorphic/arrow/components/Atoms/StoryTags/stories.js b/app/isomorphic/arrow/components/Atoms/StoryTags/stories.js
new file mode 100644
index 000000000..2959176e1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryTags/stories.js
@@ -0,0 +1,25 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { withStore, optionalSelect } from "../../../../storybook";
+import { StoryTags } from "./index";
+import Readme from "./README.md";
+
+const story = generateStory();
+
+const templateStyles = {
+ default: "",
+ "Round corners": "roundCorners",
+};
+
+withStore(
+ "Atoms/StoryTags",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/StoryTags/story-tags.test.js b/app/isomorphic/arrow/components/Atoms/StoryTags/story-tags.test.js
new file mode 100644
index 000000000..38dd33f4e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryTags/story-tags.test.js
@@ -0,0 +1,18 @@
+import React from "react";
+import { mount } from "enzyme";
+import { Provider } from "react-redux";
+import { StoryTags } from ".";
+import { generateStore, generateStory } from "../../Fixture";
+
+const story = generateStory();
+
+describe("Story Tags", () => {
+ it("Should render default template with tags", () => {
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find(StoryTags)).toHaveLength(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Atoms/StoryTags/tags.m.css b/app/isomorphic/arrow/components/Atoms/StoryTags/tags.m.css
new file mode 100644
index 000000000..eaadb386c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/StoryTags/tags.m.css
@@ -0,0 +1,34 @@
+.tags {
+ display: inline-block;
+ font-size: var(--arrow-fs-xs);
+ text-align: center;
+ border-radius: 3px;
+ margin: var(--arrow-spacing-xs);
+ padding: 5px var(--arrow-fs-xs) 7px;
+}
+
+.tags.roundCorners {
+ border-radius: var(--arrow-fs-xs);
+}
+
+.tags.dark {
+ color: var(--arrow-c-mono4);
+ border: 1px solid var(--dark-border);
+}
+
+.tags.light {
+ color: var(--arrow-c-invert-mono4);
+ border: 1px solid var(--light-border);
+}
+
+.tags.dark:hover {
+ background-color: var(--arrow-c-mono7);
+}
+
+.tags.light:hover {
+ background-color: var(--arrow-c-invert-mono7);
+}
+
+:global(.arr--story-tags) > :first-child {
+ margin-left: 0px;
+}
diff --git a/app/isomorphic/arrow/components/Atoms/Subheadline/README.md b/app/isomorphic/arrow/components/Atoms/Subheadline/README.md
new file mode 100644
index 000000000..a5bd1aff7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/Subheadline/README.md
@@ -0,0 +1,17 @@
+# Subheadline
+
+Displays the subheadline of the story in the story card.
+
+## Usage
+
+```jsx
+
+```
+
+
+
+We can pass `showFullContent` as props to show the complete sunheadline without truncating.
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Atoms/Subheadline/index.js b/app/isomorphic/arrow/components/Atoms/Subheadline/index.js
new file mode 100644
index 000000000..0636a7f4b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/Subheadline/index.js
@@ -0,0 +1,55 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { useStateValue } from "../../SharedContext";
+
+import { getTextColor, truncate } from "../../../utils/utils";
+
+import "./subheadline.m.css";
+
+// either truncate or line clamp should be used
+export const Subheadline = ({
+ story,
+ truncateChars = 200,
+ lineClamp,
+ clazzName = "",
+ forceShowAtom,
+ showFullContent,
+}) => {
+ const config = useStateValue() || {};
+ const subheadline = forceShowAtom || get(config, ["showSubheadline"], true);
+ if (!subheadline) return null;
+ const textColor = getTextColor(config.theme);
+
+ return (
+
+ {showFullContent || typeof lineClamp === "number"
+ ? story.subheadline
+ : truncate(story.subheadline, truncateChars, true)}
+
+ );
+};
+
+Subheadline.propTypes = {
+ /** The Story Object from the API response */
+ story: PropTypes.object.isRequired,
+ // Restricts the number of lines for subheadline.
+ lineClamp: PropTypes.number,
+ /** Number of characters to truncate the subheadline */
+ truncateChars: PropTypes.number,
+ clazzName: PropTypes.string,
+ forceShowAtom: PropTypes.bool,
+ showFullContent: PropTypes.bool,
+};
+
+Subheadline.defaultProps = {
+ forceShowAtom: false,
+ showFullContent: false,
+};
diff --git a/app/isomorphic/arrow/components/Atoms/Subheadline/stories.js b/app/isomorphic/arrow/components/Atoms/Subheadline/stories.js
new file mode 100644
index 000000000..fd48e813a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/Subheadline/stories.js
@@ -0,0 +1,12 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { withStore } from "../../../../storybook";
+import { Subheadline } from "./index";
+import Readme from "./README.md";
+
+const story = generateStory();
+
+withStore("Atoms/Subheadline", {}, Readme)
+ .add("Default", () => )
+ .add("with truncate chars of 50", () => )
+ .add("with line clamp of 1", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/Subheadline/subheadline.m.css b/app/isomorphic/arrow/components/Atoms/Subheadline/subheadline.m.css
new file mode 100644
index 000000000..af9147552
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/Subheadline/subheadline.m.css
@@ -0,0 +1,17 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.subheadline {
+ margin-bottom: var(--arrow-spacing-xxs);
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+}
+.dark {
+ color: var(--arrow-c-mono4);
+}
+.light {
+ color: var(--arrow-c-invert-mono4);
+}
diff --git a/app/isomorphic/arrow/components/Atoms/TimeStamp/README.md b/app/isomorphic/arrow/components/Atoms/TimeStamp/README.md
new file mode 100644
index 000000000..762b17358
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/TimeStamp/README.md
@@ -0,0 +1,9 @@
+# Timestamp
+The timestamp component for story card based on the `last-published-at` or `first-published-at`.
+
+## Usage
+```jsx
+
+```
+
+
\ No newline at end of file
diff --git a/app/isomorphic/arrow/components/Atoms/TimeStamp/index.js b/app/isomorphic/arrow/components/Atoms/TimeStamp/index.js
new file mode 100644
index 000000000..6c3206b02
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/TimeStamp/index.js
@@ -0,0 +1,41 @@
+import React from "react";
+import get from "lodash/get";
+import { useSelector } from "react-redux";
+import { useStateValue } from "../../SharedContext";
+import { formatter, getTextColor, getTimeStamp } from "../../../utils/utils";
+import PropTypes from "prop-types";
+import "./timestamp.m.css";
+
+export const TimeStamp = ({ story, isBottom, config = {} }) => {
+ const configState = useStateValue() || {};
+ const isTime = get(config, ["showTime"], get(configState, ["showTime"], true));
+ const time = story["last-published-at"] || story["first-published-at"];
+ const isBottomClasses = isBottom ? "bottom-fix" : "";
+ const textColor = getTextColor(configState.theme);
+ const isLocalizedNumber = useSelector((state) => get(state, ["qt", "config", "isLocalizedNumber"], false));
+ const languageCode = isLocalizedNumber
+ ? useSelector((state) => get(state, ["qt", "config", "language", "ietf-code"], "en"))
+ : "en";
+
+ return (
+ <>
+ {isTime && (
+
+
{getTimeStamp(time, formatter, config, languageCode)}
+
+ )}
+ >
+ );
+};
+
+TimeStamp.propTypes = {
+ /** The Story Object from the API response */
+ story: PropTypes.object,
+ isBottom: PropTypes.bool,
+ config: PropTypes.object,
+ languageCode: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Atoms/TimeStamp/stories.js b/app/isomorphic/arrow/components/Atoms/TimeStamp/stories.js
new file mode 100644
index 000000000..99d257b80
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/TimeStamp/stories.js
@@ -0,0 +1,9 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { withStore } from "../../../../storybook";
+import { TimeStamp } from "./index";
+import Readme from "./README.md";
+
+const story = generateStory();
+
+withStore("Atoms/Time Stamp", {}, Readme).add("default", () => );
diff --git a/app/isomorphic/arrow/components/Atoms/TimeStamp/timestamp.m.css b/app/isomorphic/arrow/components/Atoms/TimeStamp/timestamp.m.css
new file mode 100644
index 000000000..5b6d4ec74
--- /dev/null
+++ b/app/isomorphic/arrow/components/Atoms/TimeStamp/timestamp.m.css
@@ -0,0 +1,22 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.time {
+ display: flex;
+ margin-bottom: 2px;
+}
+
+.time-wrapper {
+ margin-bottom: var(--arrow-spacing-xxs);
+}
+
+.bottom-fix {
+ margin-top: auto;
+}
+
+.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.light {
+ color: var(--arrow-c-invert-mono4);
+}
diff --git a/app/isomorphic/arrow/components/Fixture/config.json b/app/isomorphic/arrow/components/Fixture/config.json
new file mode 100644
index 000000000..fe1422507
--- /dev/null
+++ b/app/isomorphic/arrow/components/Fixture/config.json
@@ -0,0 +1,2703 @@
+{
+ "stripe-publishable-key": null,
+ "sketches-host": "https://ace-web.qtstage.io",
+ "public-integrations": {
+ "facebook": {
+ "app-id": "296173334434957"
+ },
+ "linkedin": {
+ "app-id": "818zdjpe6p28n0"
+ },
+ "twitter": {
+ "app-id": "cCauAWFScNgFJv3WLWbxqZ4OD"
+ },
+ "google": {
+ "app-id": "568104192219-fo3pplg00om7824fshreh5e3rafpevfl.apps.googleusercontent.com"
+ },
+ "one-signal": {
+ "app-id": "03ef0b4b-3920-4b9f-9933-2ec237321000"
+ }
+ },
+ "theme-attributes": {
+ "subscription_color": "#800080",
+ "one_signal_app_id": "hfjkzdfighje",
+ "enableAccesstype": "true",
+ "footer-quintype-link": "true",
+ "subscription-color": "#ff0000",
+ "authorFilter": "true",
+ "secondary_color": "#a3a3a3",
+ "timezone": "Asia/Calcutta",
+ "accesstypeKey": "9rm1ijqQmGfJ2kwQ9zt2gWhK",
+ "adbutlerConfig": {
+ "publisherId": "176031",
+ "Horizontal-Ad": "357146",
+ "Mrec": "357151",
+ "Vertical-Ad": "357148",
+ "Story-Mrec": "357152",
+ "Story-Middle-Ad": "357149",
+ "Story-Bottom-Ad": "357150"
+ },
+ "primaryFont": "Noto Sans Devanagari",
+ "q-and-a-element": "default",
+ "disableMetering": "false",
+ "amp_enabled": "true",
+ "secondary_header_text_color": "#000000",
+ "logo": "https://quintype-dropbox.s3-accelerate.amazonaws.com/itsman.quintype.com/2018-12-04/1712/c9b0221acd904f0d968c9feba0c6fefd.png",
+ "plan-page-layout": "grid-view",
+ "header_text_color": "#a72723",
+ "social-share-style": "vertical-color",
+ "header_background_color": "#ffffff",
+ "photo-story": "hero-priority",
+ "footer_background_color": "#0b0b0c",
+ "imageFallbackUrl": "",
+ "secondary_logo": "https://quintype-dropbox.s3-accelerate.amazonaws.com/itsman.quintype.com/2018-12-04/1712/c9b0221acd904f0d968c9feba0c6fefd.png",
+ "secondaryFont": "Mukta Mahee",
+ "plan_page_title": "",
+ "visual-story-monogram": "https://images.pexels.com/photos/326055/pexels-photo-326055.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
+ "accesstype-key": "9rm1ijqQmGfJ2kwQ9zt2gWhK",
+ "filterOption": {
+ "author-name": "true",
+ "story-template": "true",
+ "date": "true"
+ },
+ "primary_header_background_color": "#0433ff",
+ "ga_id": "UA-131537578-1",
+ "one_signal_apple_web_id": "gshdjksldj",
+ "story-elements": {
+ "summary": "border",
+ "blurb": "blurb2",
+ "question-element": "question-element-default",
+ "answer-element": "answer-element-default",
+ "q-and-a-element": "q-and-a-element-default",
+ "attachment-element": "attachment-element-1"
+ },
+ "dfp_network_id": "54630992873",
+ "header_icon_color": "#1e66a7",
+ "video-story": "hero-priority",
+ "storyTemplateFilter": "true",
+ "lang": "en",
+ "primary_menu_count": "11",
+ "gtm_id": "web.onesignal.auto.516349e1-446e-4cea-8b68-2396bd3ac49a",
+ "secondary_header_background_color": "#ffffff",
+ "adService": "adbutler",
+ "language_direction": "left",
+ "metype-config": {
+ "accountId": "2",
+ "host": "https://www.metype.com",
+ "primaryColor": "#FF4500",
+ "bgColor": "#000000",
+ "fontColor": "#FFD700"
+ },
+ "secondary_menu_count": "9",
+ "text-story": "default",
+ "row-title-style": "style-1",
+ "plan_page_desc": "",
+ "monogram": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxMTEhUTEhIVFRUXGBcXGBgVFRUWFxgXGBcWFxUVGBUYHSggGBolGxUVITEhJSkrLi4uFx8zODMtNygtLisBCgoKDg0OGhAQGy0mICUtLS0tLS0tLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLf/AABEIAKgBLAMBIgACEQEDEQH/xAAcAAABBQEBAQAAAAAAAAAAAAAEAQIDBQYABwj/xABAEAACAQMDAgUCAwYFAQcFAAABAhEAAyEEEjEFQQYiUWFxE4EykaEHFELB0fAjUnKx4fEVJDOCssLSNENTYpL/xAAaAQADAQEBAQAAAAAAAAAAAAACAwQBAAUG/8QALBEAAgICAgIBAwMDBQAAAAAAAAECEQMhEjEEQRMiUWEycaEU0eEFQoGRwf/aAAwDAQACEQMRAD8AzjLQ4MMKsMUJct5r7OUbWj5uLLXTviq7rpxROmoHrD8Cl5l9BuL9YZ0y0AtGtihunLCijSKLGqijpvbKzUpNRhoMURfWhLozP6VsuzF0G2walk0Lp7vajVBNbZg1rsCTXMJE0SdI2JXFNtnkelbWrD46sw/iW3GaK6KnlFS+KbWDNJ0S7CjFeUkl5DK228SLi01Nv+1NZ5ru1Wt6olRV3hNUurXa01otRbrP9QEGvO8mNIrwu2DahiRU2jiKR1lah0fPpHM+v8u1SXUiirWgommXeKlfigrt0iik6BirYSLxYknk+kDPwKlunFC6V/z/ANqkvPWwdI2Stjlb1pHao1mnMR35rUzmhwWoLsA4pWMcUJffNBKWg1HYbcfFRdxkdsjIyJ/nUSnFKtc3ZiVE20AHPf0qN2p4PakvOTM/oABgRWG/uV105plS3EqMikPseuh9o1K9R20OPenT61q6BfYMVE54HOYJ+KiqV3g1FU0uxyPT70ih1uE0WWkUIB6V9lNV0fORDNHeg8/eg+sNJEetTaNc1X9WaHAHep80nwG40uRe6B/KKJL0H0+0dsxxzU1wGnxehEuyO7zUV1abvIMGnlhNFa7O6JLSRRuneh7bgiMdvmiLK0DZhrNGq3EjEgVmdZ5bjDvRNpnTzW2j2NQ9Phr7G+Z9PSkqXFtfccqaKXX9MNwbnHlFBWSpMKIAq68addVLZS2tZ/o3AqdU81FDtYi0S3S3LeKkUU1zVjSolVlZfJFVeuANXGtB9T6frP8AvmqDWNBrzvIdaK8S9i/RG2q3bDGKtrb+Wqw/jqPJ6KMb7CkfFV+qB5o65Q1wSYpcug4PYRo7YinXlxU9m3Apt8eU03pC+2CPc+B8CO0UP9Ud/wCx/WnMRTFt9+faluQ5I4XpOOK68IHAMxnOKaBHApznyHy9xB7DBkcZ7flQ+g/ZHZFS7ak0tiR/cUjKROAe3/NEmqAadjaQR6+s/wAv5/lXIJpbiEGCK1nUB6gZrgmKW4snHNEWVEeaRjEd6V7GegRTXMtc39/9ftStQmgr02nvTKnfY1HoK6nFLpbmaE1S+ldorkc19bzfJJng8dWixW4ZxVbqGm6AaN+uBNVl+9N0GleQ0q/cLEtmp0zkLinPcqDTXRtzTWOaqi6RK1sa7SaW7AXHP8qhbmnh5MGlzaaoZHWwzRr3ij9OuaG06YorTkzWegG7YY1QXl71KwxUF65g0BxiPFl2fzp/RtQABnNDeKDmoeisO9ecptZ2ejxvCjWJexTlYHmgw2MU2TXpciLiTasiKy3UVhq0N6Yqj6rJ55nnOeIH2iofLbaKsFJjUOKr7+Ho8OCBAihNbGIn3+a8+T0iqK2x7NIqBsNIOJ74PtjMfnUyHFQai3ADSvMRPm4mY9PeskbBbLC3fxUWovTxUFpsU8LIouWjFHZEDOOPc/711rv3j07/ABNQPIMVLZn4BoBqLHR9JvXVDBIWJBO0SPtJP3+K0HQ/Bn7xaP1LzowMhFCke5k8nb8QfaCc9Y11yzi25j0PH/BrT9F8XoI3yjYyc59Qw7f6gPmqcaxvsVNz9FF1jw7e0u0M4dCxVSJEN7oeJEGRM1f6HoFp0AFoFhgswM7u/v8AarbxN4g0d/S7jcti4ly0xXcssocSyCfMRzAzjvUdjxlpdNZDg/XvNJW3bI2qTy1x+FkzjJjtmjXxxbFuU2utiN4P0lhHbUCSZYksUW2I/wD1IHv88TgV5s75McGecmOw+Yo7r3Xb+rfddaBOEWdi/wDyPuf0qtZTMc0rJOLdJDYQaVt7B7y1JZNLqLflM8gxFQ2XAERmp72OrQ+6KZ2pbh9KaGrGciJUBME7R681BUt0VFU8uxy6N7AJNDOIapNM3vSsAa+qf1K0eEtMj3yaitrNwTTA21jTbN4lww4GPueP9qlnO2r+45R1o0RWFxUNu4eKepLCqzUMQaqnLjtE8YuXYW96DTLOol6gt6guZYycCT7CB+gFcqQw/lSHNvYzglo1GlvCBRVq5VZpcCjbLVUuiRrYYXoS+Oam+pUT961I4wviRZYD3qfp/TiBNT9UtzdUe9anS9LJtiB2ryeF5Wz1E0saRQ2pqYXI5puvRrZgigbupLGTzVfyKJK4WFX7oiqbXvINEO9AasVJnych+KNMi0rVDqzmpNLE5mPbmo9Uc1G/0lKX1Ets4oW6M1MtwTgQPSoL5zQvoJLYUgHalLRTLAxTrNss4A7miB9i6bTm44kH8q0Ou6cq2uO1avovh1DbB7xUHiDSbEIqqOBxhbFvKnKkeacUk1M9rcxVASZo+x0jaNz1NFNjnRRXrYnNT7fKDSa8DdjtTVOOKFaYbHhTE9qjinrdbgGkitvZlaILwplkZqVxUKUD7DXQRcGKHFH6LQXLx221J94MCfU9q0FvwBdN8WvqoRE7lUn7R6896YsM5/pQHOMf1Mxdyoq2+o/Z9f8APtdWKciGB5/pmqrU+DtShAYJkT+MHGR6e1Ky+Lli9xDhmg+mWGnNLuhiJBjuODQqPTgR969j5NJHm8TtYARzGKH6NySQD89vipNc/lioejmkd5lY5axujTI2KDvJNTIcVDqLwkwNo9ASYz75r08jTRFGJEgEzxTfqeb4pR60I9zzVFPXQ+O+y9sakkZqz01+qC2YAnvVpo2kVRid9E+WNbZYPdpn1DFKAKUsIqimT2ZfqOqCXlY8A16D0fr9o2wJBry/xI3nEUmicgSDFeR8jjlkepxTxo23iW+j/hqkvENGIoWzfLcmacXp1p7Yp2tIivW6DvHBo65cqv1Nypsq9obj/IEGhqZqSSJp4IqC6/IqRsrSJNLS3xTdMafeE1taMv6ibSQTBYKI5P8AtSaG9tdWPANQWqnRfStRz0eoeH/EdoAAsKXxbrrV1QqMJPFeWaq4JBUkesVy3mEMGMjjJNUf1DapoT8KTtHpHSPD1m0m9jJ5JNZ/xPrkZtiH5j0qlu9dvOu0t7UGVIIzE8kyY9z3oZ5U48YqgoQd2wbUTgEmBgT2Ek/7kn71MWJQA528VvdN4J0rW1Z77PuybltlVMSTsUqd2BzPbiOM/wCKfC7aMqQ5e05YKxBUgrna3aSCCCOc+lRwywlKkUuLSM0cUfo+j37rFLdp3YKGgKRMwY8+3sTxORiRmo9H9P61v6s/S3DfEk7ZzgZNeu6bUoyL9K5bv2SPJuwwjsrHM1Xix8/ZPknx9Hmb+ENcInTkTiS1uB/q82K2PQ/A1q2qtd8xcQfY1sNPePeT7Nkj23dx80FdvbLhsn8LeZfb2q7D40FL8/klnmnJUh+n6Gq7EVQqWzwuJH9gGiLjKmpUrhaL0lw49ePmndQ0YkPFOUkpU/sSOTb+om0wAvXMYYTUWp6ajEHb2/rU/TWDnd7RRGoYAx7UlyalSF20zx7qPhk21Jzu/Ss7bXMd+PvXoek8QC6hs3bTC9BgRyQP+Kqel6JZYsgO7ORkMMRWxxKTVOkejbrZRN0Z3AVZnkkiAB3ifxGmr0VdPeVTcJRiJPeMSa1mr1MAALuYdhyB3P2qm0d121W4IGCKJLEBQT6k/wB5pOd44PkvQ3EpS+k3er8KaEaE6pLp2xEn/NO2PWZrzN4n2r0nr2qQ6QWvroUukN5CCAQRPHxWBu2rI3Bbsxjgf0qTxfNhxanJ9js3jSbTikC6c5MD86Au/jPzVgoXO1iaGbQMTIIp78rFLpifgmvRKjzE1a6YxVOllhg1baVvL7/Fej40lLokzQa7DDcorS9Pu3UZkAgGMmJOJz7bl59fY1Xs8dv79a1XgfVLcttZIjJYHs0gAj5gx8EVTllxjaJlH3RjepeCtS8uNvlmVJM45HzQ/Seh3Pp32uIZW0do772ZQI+27869F6l1c6IkXbbut3CbVLMLoGVgZO5QT7bT61ndb1Qvo7t9Rt3PbtqDjg3Cf/Sfyry3HHylJvZbGU3FKtGMtqynawIPoaeWmobjncSzAn1mnKy92/LNT/LFRu9DXjk5VQoqBwO/v+farDQ62yvPmPParXpti1qQSlq4CORsYj/+lEViyQnpNBfHKO2jGXMGuUKGDXFkDO3In2kZFaq+2mXAYBh2gg/lFUes2lox98UjLxXtDoW/RadP0GkvL/hblbuCWO359s/pWl0fgexeRg+/6kNtKtHrtxBBgkD7e9YGxYKNuDhDOIPm+cVseleI7loKT5zOBG0x8TBpmPNir6gZ4sn+0hv+AhbZouM6AqBO1TJHB9c4xV90vwdYsIbt4C5OR9T8KgDJI+c5qu8WeO7GoWymlVvqF1e7u8mz6bKxG4+Uzt54ifWg/Feu1OrBL3bS2x/9i2XwB3dio3/7e1FLyfHxtUBHDnyLZS+LtXYvXVGnRVCg7mUBVYmIgDkADnvOIAFUZEYq8sdBVre/97sq8SLZ3z2gFgIBz+lO0/QEMu+qti2IyisWYkSAqPtxH8RMfNRy8rG223/DKY+PNJKigArQdA8NXdUGfetq2p2m4wJzEkBRyAMk9p70xelabdP7w7IBuO1F3bQM4LRIPpNWHRdPZ02rt3Hui5bRmBQ4eGR0aQJAInsaCfkw4/S919go4ZXtaCfCnTltnqCreFwrpLjo9oeXegN1SwMgGbUd8EicmqbXeKNRqLH0LlxWTcHPkUNMyPNGMk8Vr+l6rRW9PqlV7Vq5ct37SKhMfTcG3bPngloa6+TGVE+mEvdMtKGZNSNoMDcgUkev4zn2FJhmg5ty/HoY8cq0VlwRRFrUPbZfpXDamN0MSrHPmZTg/BqYaYZ23EjuXOyPTGf0qG7pQDtV0ue6k7T8FgKr+WD9ifjkvRtdD4s1NhQdRY+rb/8AyWTIHyh4+ZAq11firRaq2Gt3wl63kLd8k+oDHH6mst4f1JtCL922qREq31GOMBVXlhicj+VJqn0Nx/MttsEEhXtuW3ES232EyT3q2GeOqyK/zr+eieXjvvi/+P7Hqnh/Urftq6ma0N2xKRXjfhXrdvQ6lbYf/AuRgkyjHjn+E/zHrXtmnuB03KZFHPLyqS/jZ5nk4nCRjb+9C4VoAFUSdavDBz71rNfblmHc/wA6z9zp+a9jx5wa+pHQafZYdR6YA63do3KRJ9VJg/equ9es2yZbMgwBMTIM+mR3rWdS6VdZTtuMDB4ja2OCCMfIrNPoLe7dAUwFb52BgT7ghqkxcJK2/wDoZDIpFSEYO/4VnAbB8vrjmqH9z09tHN1jd/EXBPl3YAO0cEHjmrbrfULSrvJhAOBHmJjEek1ltTq31LbyNqLgKOMcYHJqDycmOPq6LsUZP8EaaXBKMEBlgkRA7T70Ndv3GJ8iif8AKAAMVf8ASvDep1AuBLBLLks7i2qCJJMnis51LSvbd03IQpjdbcFSe8HuK8aW3ZdFroJ0eogQqk4/E2B7x6065fRecn2qusm60SRAxmIgekc08rBgEsePQAVjNROvVQBgflV3o72s1ihLFlSEEs0bQoAkl2OBVBpWQeVY3ZJLHHvEDmrC11i/btPa3/4bfiVJQn5IyeBXKTXTMcbB9Z1B5FtDucnbAk5OBXofSbRtsCpOQJzyR3/1c+xzyCa8uN9Xub0tEGZO0k5EQ3sZFaqx4hLbPrbrWRLCCD77uFPz/wA16v8Ap+eC5Rm9vol8nG5JUj0nxLbOq0N7ZK6iyourt/FuQ7lK+zQV+5HavOuua4N07Tzg3LofzT/DbOTHvdq3ueKvor9VLyNtEzMMw4I2nDgmAVx9iKxdjqtu8EW5b/8ACBCqBIBcklVTPlhVEmeBXeXxxqSvbX/qFeJilaT6Tv8AgrNTrVMCVP8A5YE/0qEuoyp9sZHzTtQ7u8LaYLMKoTM8DAHPtUtvpF8IbhtOq5EsjQYgETECJz8V5aieg2EdMNhbi/vJuKgMn6YTeRBMLukAzAk+9NbXDcy23uW7foLhJIjvGDPwOaAa2ABKkTnn+UURpemPeBNtVAGCWaCT8cmttIzi2QtqF3TkjjOef7FTXmtKxncRAK8fr+vpTn0n0gN33JAj7Ac/f2phJYwRHaIX+xxXcjeITa1FqAYzjI2/lMyTThr1DAr9TEwZAaSCIkhsZ4xQa3cQogT2wKGuX2B/lOB+VMWWvQLgW+n6sQPMUUgQCFCtxGcZ+TUN7rhJMKskyWY75IHJmfnig21ZOQAoJOMws4wDSGwDkrJOZJjucwKVKXLsOKroM1eu3Y327m4KcjAkAkEwCGBEY/Wom1oDfw4PoI+w+BUGsUwCwlT+E9iRyATUGisguJGJBOe05+aFRRrkyxGsMYt7ge+51j3EMFk+4NRXtMVUMvf0LHb8mI9a3PU/BVm5ZW7ZJ3QDAOD6j2rDdYZlYJBAXG33707L48sTSfsXjzRmtCaZ1YEFSx/zNOPtMVHqtQS0sskYGYEeoHA/Khkcrg8UR03Qm/dW2v8AEcn0Hc0uMLdIJypC6bS3Ls/Sts0DMQR+Zqa1p7q+eJjsSPL6yDXsej0FvS2PIgGIA9cV5R4m0bKxucbiTA4+9WZ/D+OKd79k+LyOcmiv1uoZ5LKfnFB2b7A4P50jX2gAmpbSYGR9qkooseAbhM4B9q9Z/Zj43RFOm1D5UeVj3H9a8mcFRmahuvPsabim8b/AnNijljTPfrnV0d3KHExNQ3NaJ7V4voPEN62CN0j9a0+i8U2Ng3Ez7zXu4PNwyW9fuefLxHHo2KftdtR/9JeI7TsB/RqwXiTxlfv3XKp9JHOR+IxEA+xifzql018kFY59Ofb4q30dpLS7vxPG5p/hEQIryVklL3RVHDjxu0tlZeuWioDFi8ctNM0WqZSVUn1X1n+tP1blxuVV29o49zHNN6Raf6yLbttcumCo2zx7d45+1Jbp2yj0b7xTYupo7RfUWRdKQUUMXPE/UudoE4ivM71yRBMie3H9a0niTQ6gXP8AvJaRAVXXYxXtttQCRnkgTmgvD3Rvqaq2t0KttvOd5CSgyILHMmBjsTSm0ts2C0O6L0TWasf91sM4GNwhUHrBOD9qd1/wf1DR2w9+yyoSRuVg4n325AP619I+GtdplW1ZW5YDsvltq9vfhcgIDP4VZsDGawH7Qf2tW4fT6K3vcEo126u1VYSp2IfMzDIyAJ7Gk83LaGUkeJ6ZwCDJXHbMx2j7Vf2ELIuyGdgSxtkkjP8AFiJPAFZ9lXljJ5Pz7x3qXp2ua0TsJUkxIMe4rZxdaNg9msTTKtsMpttcMs5LgHLNgkH0mgtVp3YEklEccx5SCMEYnb7xzj5N8I9Kvat3thgm9JuXmRrjBdwVVQAHzmCAJGN2fVNfo/oeX94draEbNwyw4LC2YOCoWJ4FKg3dDWlRQP0rYAQ4knaPKATPueBnvW1tdIsaTTkM5v3QQ1z6RlFkAbS8CRIAg8zMYrMf9tyrf4aln5MZmVwT/Epj19cVJ1Tq2ouBFunZ+HG7aIX8JKzjMH7DvVKy8eo/4E8L7ZLrPqW0+ohS3DD/AAtodwoIkk8TySs8enZ9jw9qbs6i5etWtsEhzBA4kKTu7ryBz2iqu7cY3Ha5c+o4HmIypMAAkjkDBz6RWg8MdNOoj/FVXCvt+u8WWuDhQOWeCCFwMGgUuW2dO0tFH4hs3BBZ2ujA3bAFEAjaIYmMT2H3wKrRaq4kFCR/f/WvTdL4R1R+rcYWrChSC94KGu4JZQ0wT/pheM+uBs9CdluXBAVSQFaVZsjgHsAe9a0DCdnP1MuVBPBJx8EAT6Zqu1QEkDuZJp/0QrANjvOY/Omaq9uMcDtAx+dDBbGSeiH65GAa7T22Y+VZPeotk1ZadjaTziAT25z602VJArYCo53EArwsTM8/FS6zW7gAoiOfek+sp3YzwuO3rUF23txg/FAjmMe6xAUkkCYE4E84p9iBOYJx+dFJofJLYJ4oW6oBgZrezj0n9n/VSqBWbenGatvGPhNdRbN7TR9QDI/zD0rz/wAI9UW2xVjE5FeidJ69tPlNevhccuJRkedlUoT5RPG7qwSCIIwQeZHY16Z+yPoQ2PqrgxO1Z9Bz+tUv7RFsl/rW02s/4o4J9aP6d40tJpUsp5FQZHcmp8UI48r5ProbklKeNcV2a7xB1FTycV5/4o6tbK7BBNV3WvEr3fKuB61nnYzkzW5/K5aidhwcdsN6bqhbfeVDD0Naq5bsOof6aAn4rDq8UbpdQeCcVEUSRrbXQbJE3WkdgD+lO1/Q7FsCBBPE1n9R1xlAVYxQeo6xduHzGa2L+4PGRN1DT2wYAHyKrDZ9KV70mlmtbTCSaC7+rO8suD3jAJ78dqZ+83BIkgNkxiRJHPzIqW0kkqGCqn8UZYk4WQOcnJ7CnXtMh8w8vaAeTyTFa+rZi7oCa6xMDHbFW3TUIu2yLhVgU8wDsRkEkhDuHoQMxVW9oA4kyYE9pxmKnOlZD/4gg8kTtODjtP3ooTik7RzT6LbxFrbd25NqVUDmSCzD+KGJb0ABJiqyy0jJmJGTMf3ihygn8ROD6eld/CBtjdge9TNMZGlo2v7J2I6pprkAIbjLP+q3ctqqn5aa2P8A2D05esXdLqVL3r153toQ4QC4GvLnC7cxOTIjFea2tdq0NtkZn+kRdUxgBG3iT/lBWfacV7v1/wAOjWa/pXUrOBILnuU2Netf+8f+cUvsNnkHW7fT9SwGks3dPsYht4RQwPckudjLHpmfvQlrSItndCFWkJucElu77B8HmOfirXW9PtPqdVedgLLai/8ATRHBZ5uuRwPKDI7yAeDR3RP2fXNShv3N1uym7aIgvyYtbgSBJmSDOY9annlSvekOUCm8LeKX0ibQNyMwd7ewHecQWYkEERAiOO+areuXje2XWRFVtygA+YkMdzN9iFkRxxVl13Qi19ILbCqyr5XAFyXUtvmMiAIJ9eKGPSwX2W7qmRP4TOIYBgJjMnngSfSsi4p8l7NadUGdJ6datLv/AIzwWxuX1UH8Kk8d/wA6peqp9aG2kADc0ZMTg/qc1Z6HqOm+vGqDNbXGyAw3IpHmfkjd24oEDebgtuqoqBir4d8KSixzmfTAoouV2zGlRWWAu4jYypiQTDEfcxk0Y/UEYDgBQIBGBBxzyaG11tgs3VKhgpUQTMjykkccT603S6W2ULFwNp/DPmIpr3sD8FuNVd1OotNgBCp825klYg7WMEk1cDpaXGf96utvgMikx5T2RE5JPr6TWO0HU2W5iAowB/P3NXfTtTdbc124gcxtZiZUfxBT2J/SnLonkqeip6iBuaPwziQfXj7VW6u4CB7dqtOv3/OVAgT/AHFUa0KQy7RI/quKl0152MFhHPm4ojSdOa4pf+BefmgtdG4bRECt7O6D+p2VUAJBxJIzVYGNS2tUQIp1pFaSTmtWlsx76EOpPfNSWMTNLYRJMn4nH3qFWJML+dEtGMjZYPFWnSNSwaDcIFV7n1OaitvBmiUmtoyr7LPqd1rjFS0gcVWvYNFacF2meKg1lzNc58ns5KuhWXawBqB+aRnNKGxWWaE2/MoUximFBMVPpYiuvCOKxSNaIvprHvQxGae1yo5otAkq2PeptsYoQsalRcZNYnR1WOaAcTGe/wCtEXLjWmKkgsMEggjI/wAw5wYq11l27zcWACdxMAz6bZwR+fNV91UbhRA7rAOe/wD0/wCaXyvsOqG6Cwrybh7SMgD5M+3zNKdOWhfqiM7dzQMnkYzzz+VJqrSqsBsTxEwPn+lXnhLQWGcNdLEHCgFrY3ADaxcA5gtiR+WKxyVWck7oDPRgtvetwXuzBbdwlBM7iQIjjn2qC50tgS262wj8BeHHyrDBxx71f6y+ltosFoMBlaWHLEwcTIYduSfQUT0LwyuquD6lwW0LNuKJtMAGFHYGQO39KS81K2N+O9IF8P8AgnX3bbXLdy3aGAqNcg3AScAjBUDPPpXt/QOsr07pWmXXSj20W0VUbyIkIfLONoFee+IesOBY0tqy30RtTbtJZwCA2w8gsB+Zp3ii62oW3as2/Mpj6ab3cIpgLfdjlhmCam/qMjd+mM+GNUJ0v9ml7US4uhLRzbe7JuOMAMbSmUBXOc4AjvUt/r+ttaa5oSBqQrG0l87kP04KhQpEkiRknsBmq3rfUOp2Vt6e+CioMOfxshwoZwxDAAQKodb4i1JsrY3BLdt2YbAJLE8knJNCoyl3TRukEdTsXDeS3evEMgHmvEREKpDATgcAycDAFP6H0pXuXN99VS0cwGm4ogNsRfxeUGtLpOl6RNOj60revFN1xtxYquSQowB/CMe9ZLrHXVGLNtEXO1LQ4BHmliZOO1HBuWkdJJbZW9b0llHdrbeSfKsduZiTtAEYJNVCXZYGSD2ORjgYqy6dauX8G3KTB24jvz60JrLqjU/4Yb6afhnJHqT96pivQl/cfYONt1oQHAIOSDOR6UBastLbcAEwfarG5o7t5WvgygwCcTHt+dJ1C/IQ2bJtwAGnMkcn70SMYK2ldVVnQhZxOJ949KM1F88RkQRxGaZ1bqX7xD3D5lSPQY9AKqNNqI75/lRVqwbCb+muP5v9/wCVQNonE8Y5zUp1JDr3C5jtTupEg7ojeP0o1+QWRWtbcCG2GhWORRPSxaZybvEQPmqoTHtUlqIOc1zRqYdqrFuGK5jsKg04wTtpmjcq08x2Pejura1XAKDb6gVy7owSx0lriG5vX2FC6EMGKhZ9YoW3dIIIJrSaa6ioXGGIomBbRnrogmmbDROseTJFQu2MV1hFto9SoTaq5iqjUoQTNLZZhkCnXXLfNYYDgUrU8/FNtiTXGhWn0+JJpLzdhXX7sCBQm6sRo9jTK4GnxiiMOtAUrPSWlmil09ZZxpLmy8QLjXEXcAXCSM4+D/wIqp1VnYYV5XMHgkdpArq6p4sc0Kmk3jhkziZgj1BjNHWumXltDYpKbiYMyGIEtt4zAH2rq6hlNoKEEy26Rq7dq3c/w0uOwUAsS218lgoiBAgSPejfDt7fqSwcopYNyfKwXb5ROTEj2pK6hmrs6LqiPxlqGS6rIxe2DiMQ0SYjPfmk0ni6/bJ2i1aUgTsXLCOWPdq6uoYQjKKTRspNO0d1TxG94Q17yzMETJqp6iykq9tVC4gEzuYd47UtdTFjUXSB5trYDf1lxgEJgySY79xQwtMF3lsSAQeYnMelLXUdU6QPeyw6h1piPp2VFq0QJ29yO9UZk4DHP4viurqJKlox7LldeFQWxlVEx70MNWdsu0z/AAjsK6urkjmzr2m3oSgAUfrVMLMV1dTABXtGRiKJ24G8yO1dXVjNH6m2EUYyaDvMBwK6uokjGLbs+WTimEiKSurjA+3ZD2570x9WQNtdXUKNYE9yTk0tsSa6uomYTs0Ypu+KWurDSFmmutJJxXV1b6MJr+mjNDhRXV1aujmKyUwiurq2jCTTvFHLfFdXUDQR/9k=",
+ "cache-burst": 1562307337536,
+ "primary_header_text_color": "#ffffff",
+ "footer_text_color": "#bbbdc1",
+ "enableAdvancedSearch": "true",
+ "header_theme": "header_1",
+ "also-read-element": "vertical",
+ "breaking-news-config": {
+ "template": "template1",
+ "title": "Breaking news",
+ "bg-color": "#1d1daa",
+ "scrollSpeed": "1",
+ "delimiter": "pipe"
+ },
+ "static_pages": [
+ "aboutUs",
+ "privacyPolicy",
+ "contactUs"
+ ],
+ "banner-source": "/privacy-policy",
+ "structured_data_news_article": true,
+ "enableSSO": "true",
+ "accesstypeEnvironment": "staging",
+ "primary_logo": "",
+ "menu_count": "7",
+ "adsense_client_id": "dbjdfnjff",
+ "manifest_logo": "https://images.assettype.com/samachara/2018-09/e5dafadd-902a-4639-982a-2b9564f6d69e/b7f17273_7077_46d4_8a85_f649ba8e3946.jpg",
+ "widgets": "",
+ "infinite-scroll": "Section Based",
+ "custom-metadata": {
+ "facebook-verification": {
+ "property": "fb:pages",
+ "content": "fbmeow"
+ },
+ "google-site-verification": {
+ "name": "google-site-verification",
+ "content": "gtag"
+ },
+ "yandex-verification": {
+ "name": "yandex-verification",
+ "content": "ytag"
+ }
+ },
+ "sectionFilter": "false",
+ "primary_color": "#ff0004"
+ },
+ "facebook": {
+ "app-id": "296173334434957"
+ },
+ "sections": [
+ {
+ "domain-slug": null,
+ "slug": "news",
+ "name": "News",
+ "section-url": "https://ace-web.qtstage.io/section/news",
+ "id": 5670,
+ "parent-id": 5773,
+ "display-name": "News ",
+ "collection": {
+ "slug": "news",
+ "name": "News",
+ "id": 3795
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "other",
+ "name": "Other",
+ "section-url": "https://ace-web.qtstage.io/section/other",
+ "id": 5747,
+ "parent-id": null,
+ "display-name": "Other",
+ "collection": {
+ "slug": "science",
+ "name": "Science",
+ "id": 2723
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "film",
+ "name": "Film",
+ "section-url": "https://ace-web.qtstage.io/section/film",
+ "id": 5749,
+ "parent-id": 5670,
+ "display-name": "Film Display name",
+ "collection": {
+ "slug": "film-manual-collection",
+ "name": "Film",
+ "id": 2725
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "lifestyle",
+ "name": "Lifestyle",
+ "section-url": "https://ace-web.qtstage.io/section/lifestyle",
+ "id": 5751,
+ "parent-id": null,
+ "display-name": "Lifestyle",
+ "collection": {
+ "slug": "lifestyle-updated-slug",
+ "name": "sports",
+ "id": 2728
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "business",
+ "name": "Business",
+ "section-url": "https://ace-web.qtstage.io/section/business",
+ "id": 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ "collection": {
+ "slug": "business1",
+ "name": "Business News",
+ "id": 2729
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "recent-stories",
+ "name": "Recent stories",
+ "section-url": "https://ace-web.qtstage.io/section/recent-stories",
+ "id": 5773,
+ "parent-id": null,
+ "display-name": "Recent stories",
+ "collection": {
+ "slug": "recent",
+ "name": "Recent stories",
+ "id": 2760
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "food-culture",
+ "name": "Food culture",
+ "section-url": "https://ace-web.qtstage.io/section/food-culture",
+ "id": 6448,
+ "parent-id": null,
+ "display-name": "Food culture",
+ "collection": {
+ "slug": "food-culture",
+ "name": "Food culture",
+ "id": 3947
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "current-affairs",
+ "name": "Current Affairs",
+ "section-url": "https://ace-web.qtstage.io/section/current-affairs",
+ "id": 6449,
+ "parent-id": null,
+ "display-name": "Current Affairs",
+ "collection": null,
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "regional",
+ "name": "Regional",
+ "section-url": "https://ace-web.qtstage.io/section/regional",
+ "id": 6450,
+ "parent-id": null,
+ "display-name": "Regional",
+ "collection": {
+ "slug": "regional",
+ "name": "Regional",
+ "id": 3949
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "weather",
+ "name": "Weather",
+ "section-url": "https://ace-web.qtstage.io/section/weather",
+ "id": 6452,
+ "parent-id": null,
+ "display-name": "Weather",
+ "collection": {
+ "slug": "weather-ed",
+ "name": "Weather",
+ "id": 3951
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "",
+ "name": "\u0CB8\u0C82\u0CA6\u0CB0\u0CCD\u0CB6\u0CA8\u0C97\u0CB3\u0CC1",
+ "section-url": "https://ace-web.qtstage.io/section/",
+ "id": 6566,
+ "parent-id": null,
+ "display-name": "\u0CB8\u0C82\u0CA6\u0CB0\u0CCD\u0CB6\u0CA8\u0C97\u0CB3\u0CC1",
+ "collection": {
+ "slug": "sndrshngllu",
+ "name": "\u0CB8\u0C82\u0CA6\u0CB0\u0CCD\u0CB6\u0CA8\u0C97\u0CB3\u0CC1",
+ "id": 4800
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "global-issues",
+ "name": "Global Issues",
+ "section-url": "https://ace-web.qtstage.io/section/global-issues",
+ "id": 6657,
+ "parent-id": null,
+ "display-name": "Global Issues",
+ "collection": {
+ "slug": "global-issues",
+ "name": "Global Issues",
+ "id": 5690
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "health",
+ "name": "Health",
+ "section-url": "https://ace-web.qtstage.io/section/health",
+ "id": 11181,
+ "parent-id": null,
+ "display-name": "Health",
+ "collection": {
+ "slug": "health",
+ "name": "Health",
+ "id": 15603
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "sample",
+ "name": "Sample",
+ "section-url": "https://ace-web.qtstage.io/section/sample",
+ "id": 11238,
+ "parent-id": null,
+ "display-name": "Sample",
+ "collection": {
+ "slug": "sample",
+ "name": "Sample",
+ "id": 15732
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "parent-sec",
+ "name": "parent-sec",
+ "section-url": "https://ace-web.qtstage.io/section/parent-sec",
+ "id": 11824,
+ "parent-id": null,
+ "display-name": "parent-sec",
+ "collection": {
+ "slug": "parent-sec",
+ "name": "parent-sec",
+ "id": 17114
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "child-sec",
+ "name": "child-sec",
+ "section-url": "https://ace-web.qtstage.io/section/child-sec",
+ "id": 11825,
+ "parent-id": 11824,
+ "display-name": "child-sec",
+ "collection": {
+ "slug": "child-sec-parent-sec",
+ "name": "child-sec (parent-sec)",
+ "id": 17115
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "test12345",
+ "name": "test12345",
+ "section-url": "https://ace-web.qtstage.io/section/test12345",
+ "id": 12022,
+ "parent-id": null,
+ "display-name": "premium",
+ "collection": {
+ "slug": "test12345",
+ "name": "test12345",
+ "id": 17593
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "firstsection",
+ "name": "Firstsection",
+ "section-url": "https://ace-web.qtstage.io/section/firstsection",
+ "id": 12025,
+ "parent-id": null,
+ "display-name": "Firstsection",
+ "collection": {
+ "slug": "firstsection",
+ "name": "Firstsection",
+ "id": 17596
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "firstsubsection",
+ "name": "Firstsubsection",
+ "section-url": "https://ace-web.qtstage.io/section/firstsubsection",
+ "id": 12026,
+ "parent-id": 12025,
+ "display-name": "Firstsubsection",
+ "collection": {
+ "slug": "firstsubsection-firstsection",
+ "name": "Firstsubsection (Firstsection)",
+ "id": 17597
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "subsection",
+ "name": "Subsection",
+ "section-url": "https://ace-web.qtstage.io/section/subsection",
+ "id": 12031,
+ "parent-id": 12025,
+ "display-name": "Subsection",
+ "collection": {
+ "slug": "subsection-firstsection",
+ "name": "Subsection (Firstsection)",
+ "id": 17612
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "secondsubsection",
+ "name": "secondsubsection",
+ "section-url": "https://ace-web.qtstage.io/section/secondsubsection",
+ "id": 12032,
+ "parent-id": 12025,
+ "display-name": "secondsubsection",
+ "collection": {
+ "slug": "secondsubsection-firstsection",
+ "name": "secondsubsection (Firstsection)",
+ "id": 17613
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "newsection",
+ "name": "Newsection",
+ "section-url": "https://ace-web.qtstage.io/section/newsection",
+ "id": 12102,
+ "parent-id": null,
+ "display-name": "Newsection",
+ "collection": {
+ "slug": "newsection",
+ "name": "Newsection",
+ "id": 17810
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "newsubsection",
+ "name": "Newsubsection",
+ "section-url": "https://ace-web.qtstage.io/section/newsubsection",
+ "id": 12103,
+ "parent-id": 12102,
+ "display-name": "Newsubsection",
+ "collection": {
+ "slug": "newsubsection-newsection",
+ "name": "Newsubsection (Newsection)",
+ "id": 17811
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "newsectionsample",
+ "name": "Newsectionsample",
+ "section-url": "https://ace-web.qtstage.io/section/newsectionsample",
+ "id": 12190,
+ "parent-id": null,
+ "display-name": "Newsectionsample",
+ "collection": {
+ "slug": "newsectionsample",
+ "name": "Newsectionsample",
+ "id": 17970
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "newsubsectionsample",
+ "name": "Newsubsectionsample",
+ "section-url": "https://ace-web.qtstage.io/section/newsubsectionsample",
+ "id": 12191,
+ "parent-id": 12190,
+ "display-name": "Newsubsectionsample",
+ "collection": {
+ "slug": "newsubsectionsample-newsectionsample",
+ "name": "Newsubsectionsample (Newsectionsample)",
+ "id": 17971
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "testsection1",
+ "name": "Testsection1",
+ "section-url": "https://ace-web.qtstage.io/section/testsection1",
+ "id": 12193,
+ "parent-id": null,
+ "display-name": "Testsection1",
+ "collection": {
+ "slug": "testsection1",
+ "name": "Testsection1",
+ "id": 17973
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "testsubsection1",
+ "name": "Testsubsection1",
+ "section-url": "https://ace-web.qtstage.io/section/testsubsection1",
+ "id": 12194,
+ "parent-id": 12193,
+ "display-name": "Testsubsection1",
+ "collection": {
+ "slug": "testsubsection1-testsection1",
+ "name": "Testsubsection1 (Testsection1)",
+ "id": 17974
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "testsubsection2",
+ "name": "Testsubsection2",
+ "section-url": "https://ace-web.qtstage.io/section/testsubsection2",
+ "id": 12195,
+ "parent-id": 12193,
+ "display-name": "Testsubsection2",
+ "collection": {
+ "slug": "testsubsection2-testsection1",
+ "name": "Testsubsection2 (Testsection1)",
+ "id": 17975
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "onesection",
+ "name": "onesection",
+ "section-url": "https://ace-web.qtstage.io/section/onesection",
+ "id": 12206,
+ "parent-id": null,
+ "display-name": "onesection",
+ "collection": {
+ "slug": "onesection",
+ "name": "onesection",
+ "id": 17984
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "sectioncollection",
+ "name": "sectioncollection",
+ "section-url": "https://ace-web.qtstage.io/section/sectioncollection",
+ "id": 12772,
+ "parent-id": null,
+ "display-name": "sectioncollection",
+ "collection": {
+ "slug": "sectioncollection",
+ "name": "sectioncollection",
+ "id": 18558
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "subsectioncollection",
+ "name": "subsectioncollection",
+ "section-url": "https://ace-web.qtstage.io/section/subsectioncollection",
+ "id": 12773,
+ "parent-id": 12772,
+ "display-name": "subsectioncollection",
+ "collection": {
+ "slug": "subsectioncollection-sectioncollection",
+ "name": "subsectioncollection (sectioncollection)",
+ "id": 18559
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "rams-collection",
+ "name": "Ram's collection",
+ "section-url": "https://ace-web.qtstage.io/section/rams-collection",
+ "id": 14469,
+ "parent-id": null,
+ "display-name": "Ram's collection",
+ "collection": {
+ "slug": "rams-collection",
+ "name": "Ram\u2019s collection",
+ "id": 20441
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "elections-2018",
+ "name": "Elections 2018",
+ "section-url": "https://ace-web.qtstage.io/section/elections-2018",
+ "id": 18454,
+ "parent-id": null,
+ "display-name": "Elections 2018",
+ "collection": {
+ "slug": "elections-2018",
+ "name": "Elections 2018",
+ "id": 23928
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "empty-collection",
+ "name": "Empty Collection",
+ "section-url": "https://ace-web.qtstage.io/section/empty-collection",
+ "id": 19005,
+ "parent-id": null,
+ "display-name": "Empty Collection",
+ "collection": {
+ "slug": "empty-collection",
+ "name": "Empty Collection",
+ "id": 24824
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "testsection",
+ "name": "TestSection",
+ "section-url": "https://ace-web.qtstage.io/section/testsection",
+ "id": 23016,
+ "parent-id": 6448,
+ "display-name": "Test",
+ "collection": {
+ "slug": "testsection-current-affairs",
+ "name": "TestSection (Current Affairs)",
+ "id": 32859
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "test-section-san",
+ "name": "Test Section- San",
+ "section-url": "https://ace-web.qtstage.io/section/test-section-san",
+ "id": 23097,
+ "parent-id": null,
+ "display-name": "TestSectionSan",
+ "collection": {
+ "slug": "test-section-san",
+ "name": "Test Section- San",
+ "id": 33158
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "full-img-slider",
+ "name": "Full-Img-Slider",
+ "section-url": "https://ace-web.qtstage.io/section/full-img-slider",
+ "id": 23099,
+ "parent-id": null,
+ "display-name": "FullimgSlide",
+ "collection": {
+ "slug": "full-img-slider",
+ "name": "Full-Img-Slider",
+ "id": 33160
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "fourcolgrid",
+ "name": "FourColGrid",
+ "section-url": "https://ace-web.qtstage.io/section/fourcolgrid",
+ "id": 23102,
+ "parent-id": null,
+ "display-name": "Four-Col-Grid",
+ "collection": {
+ "slug": "fourcolgrid",
+ "name": "FourColGrid",
+ "id": 33163
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "skcoll",
+ "name": "SK-Coll",
+ "section-url": "https://ace-web.qtstage.io/section/skcoll",
+ "id": 23174,
+ "parent-id": null,
+ "display-name": "SanSection",
+ "collection": {
+ "slug": "skcoll",
+ "name": "SK-Coll",
+ "id": 34582
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "entertainment",
+ "name": "Entertainment",
+ "section-url": "https://ace-web.qtstage.io/section/entertainment",
+ "id": 23194,
+ "parent-id": null,
+ "display-name": "Entertainment",
+ "collection": {
+ "slug": "entertainment",
+ "name": "Entertainment",
+ "id": 34644
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "with-parent",
+ "name": "With Parent",
+ "section-url": "https://ace-web.qtstage.io/section/with-parent",
+ "id": 24564,
+ "parent-id": 6449,
+ "display-name": "With Parent",
+ "collection": {
+ "slug": "with-parent-current-affairs",
+ "name": "With Parent (Current Affairs)",
+ "id": 35864
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "the-count",
+ "name": "The Count",
+ "section-url": "https://ace-web.qtstage.io/section/the-count",
+ "id": 24565,
+ "parent-id": null,
+ "display-name": "The Count",
+ "collection": {
+ "slug": "the-count",
+ "name": "The Count",
+ "id": 35865
+ },
+ "data": null
+ },
+ {
+ "domain-slug": null,
+ "slug": "literature",
+ "name": "Literature",
+ "section-url": "https://ace-web.qtstage.io/section/literature",
+ "id": 24698,
+ "parent-id": null,
+ "display-name": "Literature",
+ "collection": {
+ "slug": "literature",
+ "name": "Literature",
+ "id": 36309
+ },
+ "data": null
+ }
+ ],
+ "social-links": {
+ "facebook-url": "https://www.facebook.com/QuintypeInc/",
+ "google-plus-url": "https://plus.google.com/109975125732886477523",
+ "instagram-url": "https://www.instagram.com/quintypestories",
+ "twitter-url": "https://www.twitter.com"
+ },
+ "layout": {
+ "stories-between-stacks": 4,
+ "menu": [
+ {
+ "updated-at": 1552564819033,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": null,
+ "rank": 822,
+ "title": "Times",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 3405,
+ "parent-id": null,
+ "url": "http://transportandlogisticsme.com/",
+ "created-at": 1538040312837,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000",
+ "link": "http://transportandlogisticsme.com/"
+ }
+ },
+ {
+ "updated-at": 1557472381289,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": null,
+ "rank": 834,
+ "title": "Film",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 848,
+ "parent-id": 1522,
+ "url": "https://www.google.com/",
+ "created-at": 1506336796940,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#49c6dd",
+ "link": "https://www.google.com/"
+ }
+ },
+ {
+ "updated-at": 1557474959822,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 18454,
+ "rank": 1073,
+ "title": "Politics",
+ "item-type": "section",
+ "section-slug": "elections-2018",
+ "tag-slug": null,
+ "id": 818,
+ "parent-id": 1522,
+ "url": "https://ace-web.qtstage.io/section/elections-2018",
+ "created-at": 1505453353162,
+ "section-name": "Elections 2018",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#47cd85",
+ "link": ""
+ }
+ },
+ {
+ "updated-at": 1557472042370,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "header",
+ "item-id": 5670,
+ "rank": 1318,
+ "title": "News",
+ "item-type": "section",
+ "section-slug": "news",
+ "tag-slug": null,
+ "id": 822,
+ "parent-id": 848,
+ "url": "https://ace-web.qtstage.io/section/news",
+ "created-at": 1505811047360,
+ "section-name": "News",
+ "menu-group-id": 44,
+ "data": {
+ "color": "#65036b",
+ "link": ""
+ }
+ },
+ {
+ "updated-at": 1557472039453,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 5752,
+ "rank": 1320,
+ "title": "Business",
+ "item-type": "section",
+ "section-slug": "business",
+ "tag-slug": null,
+ "id": 1073,
+ "parent-id": 822,
+ "url": "https://ace-web.qtstage.io/section/business",
+ "created-at": 1507113313397,
+ "section-name": "Business",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#43fc5c",
+ "link": ""
+ }
+ },
+ {
+ "updated-at": 1557472175708,
+ "tag-name": "tags",
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 164300,
+ "rank": 2820,
+ "title": "Food culture",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "tags",
+ "id": 1522,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/topic/tags",
+ "created-at": 1528779877461,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6447,
+ "rank": 3146,
+ "title": "About Us",
+ "item-type": "section",
+ "section-slug": "entertainment",
+ "tag-slug": null,
+ "id": 2000,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/entertainment",
+ "created-at": 1531910982329,
+ "section-name": "Entertainment",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000",
+ "link": ""
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": "film",
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 163460,
+ "rank": 3218,
+ "title": "Automotive Menu ",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "film",
+ "id": 2439,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/topic/film",
+ "created-at": 1533706920952,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": null,
+ "rank": 3219,
+ "title": "INDIA",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 3217,
+ "parent-id": null,
+ "url": "#",
+ "created-at": 1537430249429,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000",
+ "link": "#"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": null,
+ "rank": 3406,
+ "title": "Cities",
+ "item-type": "placeholder",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 3218,
+ "parent-id": null,
+ "url": null,
+ "created-at": 1537430266340,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000",
+ "link": "#"
+ }
+ },
+ {
+ "updated-at": 1548926316076,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "header",
+ "item-id": 5748,
+ "rank": 3452,
+ "title": "Roman",
+ "item-type": "section",
+ "section-slug": "sports",
+ "tag-slug": null,
+ "id": 3406,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/sports",
+ "created-at": 1538040329674,
+ "section-name": "Sports",
+ "menu-group-id": 44,
+ "data": {
+ "color": "#000000",
+ "link": "/"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6447,
+ "rank": 3453,
+ "title": "Recent stories",
+ "item-type": "section",
+ "section-slug": "entertainment",
+ "tag-slug": null,
+ "id": 3452,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/entertainment",
+ "created-at": 1538626542936,
+ "section-name": "Entertainment",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 164216,
+ "rank": 3675,
+ "title": "States",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 3675,
+ "parent-id": null,
+ "url": "https://www.pennews.net/",
+ "created-at": 1540814301207,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000",
+ "link": "https://www.pennews.net/"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6449,
+ "rank": 4060,
+ "title": "CurrentAffairs",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 4060,
+ "parent-id": 3452,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1542620427491,
+ "section-name": "Current Affairs",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": "tags1",
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 164275,
+ "rank": 4062,
+ "title": "Cinema",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "tags1",
+ "id": 4062,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/topic/tags1",
+ "created-at": 1542622548832,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 5670,
+ "rank": 4065,
+ "title": "News",
+ "item-type": "section",
+ "section-slug": "news",
+ "tag-slug": null,
+ "id": 4065,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/news",
+ "created-at": 1542794210872,
+ "section-name": "News",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": null,
+ "rank": 4066,
+ "title": "sports",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4066,
+ "parent-id": 848,
+ "url": "https://madrid.quintype.io",
+ "created-at": 1542801843550,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000",
+ "link": "https://madrid.quintype.io"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 11181,
+ "rank": 4067,
+ "title": "Health",
+ "item-type": "section",
+ "section-slug": "health",
+ "tag-slug": null,
+ "id": 4067,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/health",
+ "created-at": 1542801924654,
+ "section-name": "Health",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 5670,
+ "rank": 4068,
+ "title": "International",
+ "item-type": "section",
+ "section-slug": "news",
+ "tag-slug": null,
+ "id": 4068,
+ "parent-id": 4065,
+ "url": "https://ace-web.qtstage.io/section/news",
+ "created-at": 1542802305382,
+ "section-name": "News",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6449,
+ "rank": 4069,
+ "title": "National",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 4069,
+ "parent-id": 1522,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1542802374551,
+ "section-name": "Current Affairs",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1554111858361,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 6657,
+ "rank": 4070,
+ "title": "Economy",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4070,
+ "parent-id": null,
+ "url": "https://beta-madrid.quintype.io",
+ "created-at": 1542802433571,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000",
+ "link": "https://beta-madrid.quintype.io"
+ }
+ },
+ {
+ "updated-at": 1557474975893,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 5752,
+ "rank": 4071,
+ "title": "Weekend Special",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4071,
+ "parent-id": 1522,
+ "url": "https://www.google.com/",
+ "created-at": 1542802544832,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000",
+ "link": "https://www.google.com/"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": null,
+ "rank": 4072,
+ "title": "Technology",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4072,
+ "parent-id": null,
+ "url": "https://www.tgdaily.com/",
+ "created-at": 1542802728754,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000",
+ "link": "https://www.tgdaily.com/"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": null,
+ "rank": 4073,
+ "title": "Web",
+ "item-type": "placeholder",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4073,
+ "parent-id": null,
+ "url": null,
+ "created-at": 1542802766194,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1557475002240,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 5746,
+ "rank": 4074,
+ "title": "Sunday",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4074,
+ "parent-id": 4071,
+ "url": "https://www.google.com/",
+ "created-at": 1542802888444,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000",
+ "link": "https://www.google.com/"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": "modi",
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 201129,
+ "rank": 4075,
+ "title": "Saturday",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "modi",
+ "id": 4075,
+ "parent-id": 4071,
+ "url": "https://ace-web.qtstage.io/topic/modi",
+ "created-at": 1542802966656,
+ "section-name": null,
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 5751,
+ "rank": 4076,
+ "title": "Software",
+ "item-type": "section",
+ "section-slug": "lifestyle",
+ "tag-slug": null,
+ "id": 4076,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/lifestyle",
+ "created-at": 1542803018746,
+ "section-name": "Lifestyle",
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 5751,
+ "rank": 4077,
+ "title": "Lifestyle",
+ "item-type": "section",
+ "section-slug": "lifestyle",
+ "tag-slug": null,
+ "id": 4077,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/lifestyle",
+ "created-at": 1542803308765,
+ "section-name": "Lifestyle",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6450,
+ "rank": 4078,
+ "title": "Regional",
+ "item-type": "section",
+ "section-slug": "regional",
+ "tag-slug": null,
+ "id": 4078,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/regional",
+ "created-at": 1542803338480,
+ "section-name": "Regional",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6452,
+ "rank": 4079,
+ "title": "Weather",
+ "item-type": "section",
+ "section-slug": "weather",
+ "tag-slug": null,
+ "id": 4079,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/weather",
+ "created-at": 1542803359151,
+ "section-name": "Weather",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": "t100",
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 197961,
+ "rank": 4080,
+ "title": "Opinion",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "t100",
+ "id": 4080,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/topic/t100",
+ "created-at": 1542803488800,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 12193,
+ "rank": 4081,
+ "title": "Reviews",
+ "item-type": "section",
+ "section-slug": "testsection1",
+ "tag-slug": null,
+ "id": 4081,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/testsection1",
+ "created-at": 1542803868459,
+ "section-name": "Testsection1",
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316080,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 6657,
+ "rank": 4082,
+ "title": "Global News",
+ "item-type": "section",
+ "section-slug": "global-issues",
+ "tag-slug": null,
+ "id": 4082,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/global-issues",
+ "created-at": 1542803916512,
+ "section-name": "Global Issues",
+ "menu-group-id": 45,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316072,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6449,
+ "rank": 4083,
+ "title": "Book Review",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 4083,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1542804019603,
+ "section-name": "Current Affairs",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1555481131134,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6448,
+ "rank": 4084,
+ "title": "climate",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 4084,
+ "parent-id": 4079,
+ "url": "https://www.google.com/",
+ "created-at": 1542804158991,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000",
+ "link": "https://www.google.com/"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": "tags1",
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 164275,
+ "rank": 4085,
+ "title": "State1",
+ "item-type": "tag",
+ "section-slug": null,
+ "tag-slug": "tags1",
+ "id": 4085,
+ "parent-id": 3675,
+ "url": "https://ace-web.qtstage.io/topic/tags1",
+ "created-at": 1542804435609,
+ "section-name": null,
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1548926316068,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 5746,
+ "rank": 4086,
+ "title": "Suppliers",
+ "item-type": "section",
+ "section-slug": "politics",
+ "tag-slug": null,
+ "id": 4086,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/politics",
+ "created-at": 1542805068091,
+ "section-name": "Politics",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564289863,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "header",
+ "item-id": null,
+ "rank": 4087,
+ "title": "Testing sidebar Menu",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 5746,
+ "parent-id": null,
+ "url": "Google.com",
+ "created-at": 1552558297664,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000",
+ "link": "Google.com"
+ }
+ },
+ {
+ "updated-at": 1552564289862,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 5670,
+ "rank": 4101,
+ "title": "World",
+ "item-type": "section",
+ "section-slug": "news",
+ "tag-slug": null,
+ "id": 4087,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/news",
+ "created-at": 1542805144108,
+ "section-name": "News",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564285505,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6657,
+ "rank": 4595,
+ "title": "Asia",
+ "item-type": "section",
+ "section-slug": "global-issues",
+ "tag-slug": null,
+ "id": 4101,
+ "parent-id": 3217,
+ "url": "https://ace-web.qtstage.io/section/global-issues",
+ "created-at": 1542876792540,
+ "section-name": "Global Issues",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564283543,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6449,
+ "rank": 5165,
+ "title": "Transport",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 4595,
+ "parent-id": null,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1545297180936,
+ "section-name": "Current Affairs",
+ "menu-group-id": 42,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564281066,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "footer",
+ "item-id": null,
+ "rank": 5166,
+ "title": "Privacy Policy",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 5165,
+ "parent-id": null,
+ "url": "/privacy-policy",
+ "created-at": 1549015968607,
+ "section-name": null,
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000",
+ "link": "/privacy-policy"
+ }
+ },
+ {
+ "updated-at": 1552564276746,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "footer",
+ "item-id": null,
+ "rank": 5167,
+ "title": "Contact Us",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 5166,
+ "parent-id": null,
+ "url": "/contact-us",
+ "created-at": 1549016032161,
+ "section-name": null,
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000",
+ "link": "/contact-us"
+ }
+ },
+ {
+ "updated-at": 1552564274624,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "footer",
+ "item-id": null,
+ "rank": 5169,
+ "title": "About Us",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 5167,
+ "parent-id": null,
+ "url": "/about-us",
+ "created-at": 1549016072811,
+ "section-name": null,
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000",
+ "link": "/about-us"
+ }
+ },
+ {
+ "updated-at": 1552564270740,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "secondary-menu",
+ "item-id": 6449,
+ "rank": 5170,
+ "title": "My menu",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 5169,
+ "parent-id": 822,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1549033379532,
+ "section-name": "Current Affairs",
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564047190,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "sidebar-menu",
+ "item-id": 18454,
+ "rank": 5185,
+ "title": "Mymenu",
+ "item-type": "section",
+ "section-slug": "elections-2018",
+ "tag-slug": null,
+ "id": 5170,
+ "parent-id": 822,
+ "url": "https://ace-web.qtstage.io/section/elections-2018",
+ "created-at": 1549033759338,
+ "section-name": "Elections 2018",
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564045310,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "header",
+ "item-id": 5751,
+ "rank": 5745,
+ "title": "MyMenuItem",
+ "item-type": "section",
+ "section-slug": "lifestyle",
+ "tag-slug": null,
+ "id": 5185,
+ "parent-id": 4077,
+ "url": "https://ace-web.qtstage.io/section/lifestyle",
+ "created-at": 1549614180849,
+ "section-name": "Lifestyle",
+ "menu-group-id": null,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1552564042031,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "header",
+ "item-id": null,
+ "rank": 5746,
+ "title": "Testing Header Menu",
+ "item-type": "link",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 5745,
+ "parent-id": null,
+ "url": "google.com",
+ "created-at": 1552558276605,
+ "section-name": null,
+ "menu-group-id": 44,
+ "data": {
+ "color": "#000000",
+ "link": "google.com"
+ }
+ },
+ {
+ "updated-at": 1557309038083,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": null,
+ "rank": 6552,
+ "title": "Test",
+ "item-type": "placeholder",
+ "section-slug": null,
+ "tag-slug": null,
+ "id": 6552,
+ "parent-id": null,
+ "url": null,
+ "created-at": 1557309038083,
+ "section-name": null,
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ },
+ {
+ "updated-at": 1557309285086,
+ "tag-name": null,
+ "publisher-id": 97,
+ "menu-group-slug": "default",
+ "item-id": 6449,
+ "rank": 6553,
+ "title": "Test-child 1",
+ "item-type": "section",
+ "section-slug": "current-affairs",
+ "tag-slug": null,
+ "id": 6553,
+ "parent-id": 6552,
+ "url": "https://ace-web.qtstage.io/section/current-affairs",
+ "created-at": 1557309275053,
+ "section-name": "Current Affairs",
+ "menu-group-id": 43,
+ "data": {
+ "color": "#000000"
+ }
+ }
+ ],
+ "stacks": []
+ },
+ "domains": [],
+ "cdn-name": "https://thumbor-stg.assettype.com/",
+ "publisher-id": 97,
+ "publisher-settings": {
+ "title": "title",
+ "description": "This is the desc inside admin settings to come",
+ "publisher-logo": {
+ "temp-s3-key": "ace.staging.quintype.com/2018-02-16/1646/b5928b9c-f5e1-4734-8028-35a89edc560f.svg",
+ "url": "https://quintype-dropbox-accelerated.s3-accelerate.amazonaws.com/ace.staging.quintype.com/2018-02-16/1646/b5928b9c-f5e1-4734-8028-35a89edc560f.svg"
+ },
+ "theme-color": "#fff000",
+ "publisher-logo-to-show-on-header": "logo",
+ "copyright": "\u00A9 ace 2019"
+ },
+ "num-headlines": 5,
+ "publisher-name": "ace",
+ "env": "staging",
+ "initial-stories-per-page": 40,
+ "seo-metadata": [
+ {
+ "id": 80,
+ "publisher-id": 97,
+ "owner-type": "section",
+ "owner-id": 6447,
+ "unused-guid": null,
+ "data": {
+ "page-title": "Entertainment",
+ "title": "Entertainment"
+ },
+ "created-at": 1528795311426,
+ "updated-at": 1550120889135
+ },
+ {
+ "id": 82,
+ "publisher-id": 97,
+ "owner-type": "section",
+ "owner-id": 5746,
+ "unused-guid": null,
+ "data": {
+ "page-title": "Politics ",
+ "title": "Politics Meta Title",
+ "description": "Politics Description",
+ "keywords": "Politics, Bjp, congress, modi"
+ },
+ "created-at": 1531988798136,
+ "updated-at": 1531988798136
+ },
+ {
+ "id": 84,
+ "publisher-id": 97,
+ "owner-type": "home",
+ "owner-id": null,
+ "unused-guid": null,
+ "data": {
+ "page-title": "Ace",
+ "title": "ace ",
+ "description": "lorem ipsum"
+ },
+ "created-at": 1535711639327,
+ "updated-at": 1535711652608
+ }
+ ],
+ "typekit-id": "ntm3kec",
+ "cdn-image": "thumbor-stg.assettype.com",
+ "story-slug-format": "{{section}}/{{first-published-at|YYYY/MM/dd}}",
+ "android": {
+ "notification-style": "roll-up"
+ },
+ "shrubbery-host": "https://stg-analytics.qlitics.com",
+ "static-page-urls": [
+ "https://ace-web.qtstage.io/TestStaticPage194837",
+ "https://ace-web.qtstage.io/TestStaticPage52917",
+ "https://ace-web.qtstage.io/TestStaticPage95821",
+ "https://ace-web.qtstage.io/TestStaticPage113457",
+ "https://ace-web.qtstage.io/TestStaticPage131548",
+ "https://ace-web.qtstage.io/TestStaticPage131325",
+ "https://ace-web.qtstage.io/TestStaticPage73623",
+ "https://ace-web.qtstage.io/TestStaticPage94537",
+ "https://ace-web.qtstage.io/TestStaticPage131940",
+ "https://ace-web.qtstage.io/TestStaticPage55252",
+ "https://ace-web.qtstage.io/TestStaticPage7306",
+ "https://ace-web.qtstage.io/TestStaticPage54725",
+ "https://ace-web.qtstage.io/TestStaticPage125551",
+ "https://ace-web.qtstage.io/TestStaticPage61940",
+ "https://ace-web.qtstage.io/TestStaticPage65044",
+ "https://ace-web.qtstage.io/TestStaticPage7340",
+ "https://ace-web.qtstage.io/TestStaticPage93414",
+ "https://ace-web.qtstage.io/TestStaticPage111642",
+ "https://ace-web.qtstage.io/TestStaticPage9398",
+ "https://ace-web.qtstage.io/TestStaticPage102756",
+ "https://ace-web.qtstage.io/TestStaticPage115949",
+ "https://ace-web.qtstage.io/TestStaticPage95247",
+ "https://ace-web.qtstage.io/TestStaticPage9821",
+ "https://ace-web.qtstage.io/TestStaticPage175546",
+ "https://ace-web.qtstage.io/TestStaticPage71339",
+ "https://ace-web.qtstage.io/TestStaticPage174539",
+ "https://ace-web.qtstage.io/TestStaticPage20526",
+ "https://ace-web.qtstage.io/TestStaticPage182937",
+ "https://ace-web.qtstage.io/TestStaticPage104410",
+ "https://ace-web.qtstage.io/TestStaticPage52635",
+ "https://ace-web.qtstage.io/TestStaticPage53336",
+ "https://ace-web.qtstage.io/TestStaticPage84052",
+ "https://ace-web.qtstage.io/TestStaticPage174043",
+ "https://ace-web.qtstage.io/TestStaticPage14343",
+ "https://ace-web.qtstage.io/TestStaticPage72113",
+ "https://ace-web.qtstage.io/TestStaticPage182737",
+ "https://ace-web.qtstage.io/TestStaticPage92929",
+ "https://ace-web.qtstage.io/TestStaticPage91244",
+ "https://ace-web.qtstage.io/TestStaticPage73212",
+ "https://ace-web.qtstage.io/TestStaticPage93015",
+ "https://ace-web.qtstage.io/TestStaticPage35756",
+ "https://ace-web.qtstage.io/TestStaticPage103221",
+ "https://ace-web.qtstage.io/TestStaticPage1165",
+ "https://ace-web.qtstage.io/TestStaticPage14253",
+ "https://ace-web.qtstage.io/TestStaticPage45522",
+ "https://ace-web.qtstage.io/TestStaticPage4310",
+ "https://ace-web.qtstage.io/TestStaticPage1756",
+ "https://ace-web.qtstage.io/TestStaticPage15191",
+ "https://ace-web.qtstage.io/TestStaticPage12410",
+ "https://ace-web.qtstage.io/form",
+ "https://ace-web.qtstage.io/TestStaticPage115630",
+ "https://ace-web.qtstage.io/TestStaticPage43538",
+ "https://ace-web.qtstage.io/TestStaticPage134331",
+ "https://ace-web.qtstage.io/TestStaticPage74839",
+ "https://ace-web.qtstage.io/testing123",
+ "https://ace-web.qtstage.io/TestStaticPage43014",
+ "https://ace-web.qtstage.io/TestStaticPage131159",
+ "https://ace-web.qtstage.io/TestStaticPage45713",
+ "https://ace-web.qtstage.io/banner-source",
+ "https://ace-web.qtstage.io/TestStaticPage8323",
+ "https://ace-web.qtstage.io/TestStaticPage8372",
+ "https://ace-web.qtstage.io/TestStaticPage171245",
+ "https://ace-web.qtstage.io/TestStaticPage173535",
+ "https://ace-web.qtstage.io/TestStaticPage45947",
+ "https://ace-web.qtstage.io/TestStaticPage41337",
+ "https://ace-web.qtstage.io/TestStaticPage610",
+ "https://ace-web.qtstage.io/TestStaticPage132317",
+ "https://ace-web.qtstage.io/TestStaticPage104644",
+ "https://ace-web.qtstage.io/TestStaticPage152442",
+ "https://ace-web.qtstage.io/TestStaticPage10230",
+ "https://ace-web.qtstage.io/TestStaticPage8418",
+ "https://ace-web.qtstage.io/TestStaticPage63313",
+ "https://ace-web.qtstage.io/TestStaticPage5453",
+ "https://ace-web.qtstage.io/TestStaticPage12645",
+ "https://ace-web.qtstage.io/TestStaticPage103436",
+ "https://ace-web.qtstage.io/TestStaticPage19354",
+ "https://ace-web.qtstage.io/TestStaticPage65452",
+ "https://ace-web.qtstage.io/TestStaticPage41626",
+ "https://ace-web.qtstage.io/TestStaticPage22322",
+ "https://ace-web.qtstage.io/TestStaticPage54918",
+ "https://ace-web.qtstage.io/TestStaticPage114131",
+ "https://ace-web.qtstage.io/TestStaticPage45038",
+ "https://ace-web.qtstage.io/TestStaticPage12438",
+ "https://ace-web.qtstage.io/TestStaticPage43620",
+ "https://ace-web.qtstage.io/TestStaticPage10485",
+ "https://ace-web.qtstage.io/TestStaticPage3464",
+ "https://ace-web.qtstage.io/TestStaticPage105459",
+ "https://ace-web.qtstage.io/TestStaticPage104012",
+ "https://ace-web.qtstage.io/TestStaticPage11356",
+ "https://ace-web.qtstage.io/TestStaticPage64219",
+ "https://ace-web.qtstage.io/TestStaticPage6329",
+ "https://ace-web.qtstage.io/TestStaticPage8255",
+ "https://ace-web.qtstage.io/TestStaticPage41451",
+ "https://ace-web.qtstage.io/TestStaticPage6325",
+ "https://ace-web.qtstage.io/TestStaticPage6328",
+ "https://ace-web.qtstage.io/TestStaticPage105126",
+ "https://ace-web.qtstage.io/TestStaticPage91111",
+ "https://ace-web.qtstage.io/TestStaticPage124256",
+ "https://ace-web.qtstage.io/TestStaticPage61350",
+ "https://ace-web.qtstage.io/TestStaticPage19613",
+ "https://ace-web.qtstage.io/TestStaticPage34843",
+ "https://ace-web.qtstage.io/TestStaticPage5598",
+ "https://ace-web.qtstage.io/TestStaticPage55258",
+ "https://ace-web.qtstage.io/TestStaticPage12586",
+ "https://ace-web.qtstage.io/TestStaticPage6833",
+ "https://ace-web.qtstage.io/TestStaticPage115423",
+ "https://ace-web.qtstage.io/TestStaticPage95020",
+ "https://ace-web.qtstage.io/TestStaticPage111526",
+ "https://ace-web.qtstage.io/contact-us",
+ "https://ace-web.qtstage.io/TestStaticPage6718",
+ "https://ace-web.qtstage.io/TestStaticPage14256",
+ "https://ace-web.qtstage.io/TestStaticPage91010",
+ "https://ace-web.qtstage.io/communitysheroes",
+ "https://ace-web.qtstage.io/TestStaticPage92626",
+ "https://ace-web.qtstage.io/TestStaticPage91921",
+ "https://ace-web.qtstage.io/TestStaticPage14811",
+ "https://ace-web.qtstage.io/TestStaticPage41632",
+ "https://ace-web.qtstage.io/TestStaticPage41224",
+ "https://ace-web.qtstage.io/TestStaticPage17048",
+ "https://ace-web.qtstage.io/TestStaticPage121132",
+ "https://ace-web.qtstage.io/TestStaticPage174537",
+ "https://ace-web.qtstage.io/TestStaticPage6653",
+ "https://ace-web.qtstage.io/TestStaticPage54740",
+ "https://ace-web.qtstage.io/TestStaticPage13651",
+ "https://ace-web.qtstage.io/TestStaticPage6336",
+ "https://ace-web.qtstage.io/a1-static-page",
+ "https://ace-web.qtstage.io/TestStaticPage71422",
+ "https://ace-web.qtstage.io/TestStaticPage4223",
+ "https://ace-web.qtstage.io/TestStaticPage175331",
+ "https://ace-web.qtstage.io/TestStaticPage9139",
+ "https://ace-web.qtstage.io/TestStaticPage8394",
+ "https://ace-web.qtstage.io/TestStaticPage1479",
+ "https://ace-web.qtstage.io/TestStaticPage85452",
+ "https://ace-web.qtstage.io/TestStaticPage43352",
+ "https://ace-web.qtstage.io/TestStaticPage105437",
+ "https://ace-web.qtstage.io/TestStaticPage105321",
+ "https://ace-web.qtstage.io/TestStaticPage11138",
+ "https://ace-web.qtstage.io/TestStaticPage5954",
+ "https://ace-web.qtstage.io/TestStaticPage165417",
+ "https://ace-web.qtstage.io/TestStaticPage9331",
+ "https://ace-web.qtstage.io/TestStaticPage112133",
+ "https://ace-web.qtstage.io/TestStaticPage44711",
+ "https://ace-web.qtstage.io/TestStaticPage131131",
+ "https://ace-web.qtstage.io/TestStaticPage14346",
+ "https://ace-web.qtstage.io/TestStaticPage10919",
+ "https://ace-web.qtstage.io/TestStaticPage14328",
+ "https://ace-web.qtstage.io/TestStaticPage14313",
+ "https://ace-web.qtstage.io/TestStaticPage121712",
+ "https://ace-web.qtstage.io/TestStaticPage102159",
+ "https://ace-web.qtstage.io/TestStaticPage72951",
+ "https://ace-web.qtstage.io/TestStaticPage10293",
+ "https://ace-web.qtstage.io/TestStaticPage152219",
+ "https://ace-web.qtstage.io/TestStaticPage62419",
+ "https://ace-web.qtstage.io/TestStaticPage52939",
+ "https://ace-web.qtstage.io/TestStaticPage3164734",
+ "https://ace-web.qtstage.io/TestStaticPage316220",
+ "https://ace-web.qtstage.io/TestStaticPage3",
+ "https://ace-web.qtstage.io/TestStaticPage124753",
+ "https://ace-web.qtstage.io/TestStaticPage82028",
+ "https://ace-web.qtstage.io/TestStaticPage103025",
+ "https://ace-web.qtstage.io/TestStaticPage85241",
+ "https://ace-web.qtstage.io/TestStaticPage11335",
+ "https://ace-web.qtstage.io/TestStaticPage13428",
+ "https://ace-web.qtstage.io/TestStaticPage10132",
+ "https://ace-web.qtstage.io/TestStaticPage14249",
+ "https://ace-web.qtstage.io/augusttest",
+ "https://ace-web.qtstage.io/TestStaticPage91738",
+ "https://ace-web.qtstage.io/TestStaticPage124336",
+ "https://ace-web.qtstage.io/TestStaticPage123333",
+ "https://ace-web.qtstage.io/TestStaticPage93546",
+ "https://ace-web.qtstage.io/TestStaticPage10461",
+ "https://ace-web.qtstage.io/TestStaticPage125739",
+ "https://ace-web.qtstage.io/TestStaticPage3104540",
+ "https://ace-web.qtstage.io/TestStaticPage9251",
+ "https://ace-web.qtstage.io/TestStaticPage114115",
+ "https://ace-web.qtstage.io/TestStaticPage10164",
+ "https://ace-web.qtstage.io/TestStaticPage101948",
+ "https://ace-web.qtstage.io/TestStaticPage61825",
+ "https://ace-web.qtstage.io/TestStaticPage93616",
+ "https://ace-web.qtstage.io/TestStaticPage72541",
+ "https://ace-web.qtstage.io/TestStaticPage123946",
+ "https://ace-web.qtstage.io/TestStaticPage43910",
+ "https://ace-web.qtstage.io/TestStaticPage441",
+ "https://ace-web.qtstage.io/TestStaticPage1444",
+ "https://ace-web.qtstage.io/TestStaticPage112616",
+ "https://ace-web.qtstage.io/TestStaticPage44514",
+ "https://ace-web.qtstage.io/TestStaticPage9483",
+ "https://ace-web.qtstage.io/TestStaticPage102244",
+ "https://ace-web.qtstage.io/TestStaticPage115450",
+ "https://ace-web.qtstage.io/TestStaticPage122538",
+ "https://ace-web.qtstage.io/TestStaticPage6154",
+ "https://ace-web.qtstage.io/TestStaticPage102948",
+ "https://ace-web.qtstage.io/TestStaticPage163525",
+ "https://ace-web.qtstage.io/TestStaticPage63735",
+ "https://ace-web.qtstage.io/TestStaticPage12536",
+ "https://ace-web.qtstage.io/TestStaticPage9461",
+ "https://ace-web.qtstage.io/TestStaticPage43816",
+ "https://ace-web.qtstage.io/TestStaticPage95836",
+ "https://ace-web.qtstage.io/TestStaticPage14105",
+ "https://ace-web.qtstage.io/TestStaticPage6829",
+ "https://ace-web.qtstage.io/TestStaticPage33937",
+ "https://ace-web.qtstage.io//audio",
+ "https://ace-web.qtstage.io/TestStaticPage161433",
+ "https://ace-web.qtstage.io/TestStaticPage4263",
+ "https://ace-web.qtstage.io/TestStaticPage73344",
+ "https://ace-web.qtstage.io/TestStaticPage14312",
+ "https://ace-web.qtstage.io/TestStaticPage94259",
+ "https://ace-web.qtstage.io/TestStaticPage92521",
+ "https://ace-web.qtstage.io/TestStaticPage81125",
+ "https://ace-web.qtstage.io/TestStaticPage14854",
+ "https://ace-web.qtstage.io/TestStaticPage121451",
+ "https://ace-web.qtstage.io/TestStaticPage153632",
+ "https://ace-web.qtstage.io/TestStaticPage94018",
+ "https://ace-web.qtstage.io/TestStaticPage221731",
+ "https://ace-web.qtstage.io/TestStaticPage124241",
+ "https://ace-web.qtstage.io/TestStaticPage201447",
+ "https://ace-web.qtstage.io/TestStaticPage14634",
+ "https://ace-web.qtstage.io/TestStaticPage185332",
+ "https://ace-web.qtstage.io/TestStaticPage14251",
+ "https://ace-web.qtstage.io/TestStaticPage91727",
+ "https://ace-web.qtstage.io/TestStaticPage21631",
+ "https://ace-web.qtstage.io/TestStaticPage55618",
+ "https://ace-web.qtstage.io/TestStaticPage10429",
+ "https://ace-web.qtstage.io/test-test123",
+ "https://ace-web.qtstage.io/TestStaticPage103542",
+ "https://ace-web.qtstage.io/TestStaticPage61828",
+ "https://ace-web.qtstage.io/TestStaticPage113918",
+ "https://ace-web.qtstage.io/TestStaticPage8911",
+ "https://ace-web.qtstage.io/TestStaticPage9617",
+ "https://ace-web.qtstage.io/TestStaticPage53151",
+ "https://ace-web.qtstage.io/TestStaticPage9356",
+ "https://ace-web.qtstage.io/TestStaticPage94656",
+ "https://ace-web.qtstage.io/TestStaticPage19251",
+ "https://ace-web.qtstage.io/TestStaticPage4850",
+ "https://ace-web.qtstage.io/TestStaticPage6387",
+ "https://ace-web.qtstage.io/TestStaticPage103234",
+ "https://ace-web.qtstage.io/TestStaticPage113528",
+ "https://ace-web.qtstage.io/TestStaticPage6161",
+ "https://ace-web.qtstage.io/TestStaticPage1294",
+ "https://ace-web.qtstage.io/TestStaticPage75337",
+ "https://ace-web.qtstage.io/TestStaticPage63818",
+ "https://ace-web.qtstage.io/TestStaticPage43716",
+ "https://ace-web.qtstage.io/TestStaticPage6582",
+ "https://ace-web.qtstage.io/TestStaticPage73228",
+ "https://ace-web.qtstage.io/TestStaticPage1347",
+ "https://ace-web.qtstage.io/TestStaticPage14914",
+ "https://ace-web.qtstage.io/TestStaticPage83132",
+ "https://ace-web.qtstage.io/TestStaticPage14242",
+ "https://ace-web.qtstage.io/TestStaticPage16454",
+ "https://ace-web.qtstage.io/TestStaticPage14757",
+ "https://ace-web.qtstage.io/TestStaticPage92541",
+ "https://ace-web.qtstage.io/TestStaticPage133412",
+ "https://ace-web.qtstage.io/TestStaticPage93641",
+ "https://ace-web.qtstage.io/TestStaticPage104258",
+ "https://ace-web.qtstage.io/TestStaticPage16170",
+ "https://ace-web.qtstage.io/TestStaticPage111146",
+ "https://ace-web.qtstage.io/TestStaticPage102116",
+ "https://ace-web.qtstage.io/TestStaticPage105111",
+ "https://ace-web.qtstage.io/TestStaticPage141558",
+ "https://ace-web.qtstage.io/TestStaticPage10327",
+ "https://ace-web.qtstage.io/TestStaticPage10521",
+ "https://ace-web.qtstage.io/TestStaticPage171029",
+ "https://ace-web.qtstage.io/TestStaticPage12045",
+ "https://ace-web.qtstage.io/TestStaticPage14317",
+ "https://ace-web.qtstage.io/TestStaticPage3155757",
+ "https://ace-web.qtstage.io/TestStaticPage41321",
+ "https://ace-web.qtstage.io/TestStaticPage75319",
+ "https://ace-web.qtstage.io/TestStaticPage19450",
+ "https://ace-web.qtstage.io/TestStaticPage114521",
+ "https://ace-web.qtstage.io/TestStaticPage10373",
+ "https://ace-web.qtstage.io/TestStaticPage84422",
+ "https://ace-web.qtstage.io/TestStaticPage72024",
+ "https://ace-web.qtstage.io/TestStaticPage1381",
+ "https://ace-web.qtstage.io/TestStaticPage142359",
+ "https://ace-web.qtstage.io/TestStaticPage4533",
+ "https://ace-web.qtstage.io/TestStaticPage83939",
+ "https://ace-web.qtstage.io/TestStaticPage164057",
+ "https://ace-web.qtstage.io/TestStaticPage5924",
+ "https://ace-web.qtstage.io/TestStaticPage4718",
+ "https://ace-web.qtstage.io/TestStaticPage12649",
+ "https://ace-web.qtstage.io/TestStaticPage64659",
+ "https://ace-web.qtstage.io/TestStaticPage123512",
+ "https://ace-web.qtstage.io/TestStaticPage10442",
+ "https://ace-web.qtstage.io/TestStaticPage183638",
+ "https://ace-web.qtstage.io/TestStaticPage175329",
+ "https://ace-web.qtstage.io/TestStaticPage13390",
+ "https://ace-web.qtstage.io/sample2",
+ "https://ace-web.qtstage.io/TestStaticPage3155719",
+ "https://ace-web.qtstage.io/TestStaticPage14617",
+ "https://ace-web.qtstage.io/privacy-policy",
+ "https://ace-web.qtstage.io/TestStaticPage11553",
+ "https://ace-web.qtstage.io/TestStaticPage112712",
+ "https://ace-web.qtstage.io/TestStaticPage84144",
+ "https://ace-web.qtstage.io/TestStaticPage6011",
+ "https://ace-web.qtstage.io/TestStaticPage41251",
+ "https://ace-web.qtstage.io/TestStaticPage102845",
+ "https://ace-web.qtstage.io/TestStaticPage7410",
+ "https://ace-web.qtstage.io/TestStaticPage53116",
+ "https://ace-web.qtstage.io/TestStaticPage596",
+ "https://ace-web.qtstage.io/TestStaticPage63030",
+ "https://ace-web.qtstage.io/TestStaticPage104551",
+ "http://ace-web.staging.quintype.io/privacy-policy",
+ "https://ace-web.qtstage.io/TestStaticPage74314",
+ "https://ace-web.qtstage.io/TestStaticPage14330",
+ "https://ace-web.qtstage.io/TestStaticPage11322",
+ "https://ace-web.qtstage.io/TestStaticPage162728",
+ "http://ace-web.staging.quintype.io/about-us",
+ "https://ace-web.qtstage.io/TestStaticPage62115",
+ "https://ace-web.qtstage.io/TestStaticPage41914",
+ "https://ace-web.qtstage.io/TestStaticPage72757",
+ "https://ace-web.qtstage.io/TestStaticPage85245",
+ "https://ace-web.qtstage.io/TestStaticPage63628",
+ "https://ace-web.qtstage.io/TestStaticPage113859",
+ "https://ace-web.qtstage.io/TestStaticPage93656",
+ "https://ace-web.qtstage.io/TestStaticPage4269",
+ "https://ace-web.qtstage.io/TestStaticPage41543",
+ "https://ace-web.qtstage.io/TestStaticPage92516",
+ "https://ace-web.qtstage.io/TestStaticPage152752",
+ "https://ace-web.qtstage.io/TestStaticPage95517",
+ "https://ace-web.qtstage.io/TestStaticPage52819",
+ "https://ace-web.qtstage.io/TestStaticPage4935",
+ "https://ace-web.qtstage.io/TestStaticPage9954",
+ "https://ace-web.qtstage.io/TestStaticPage7452",
+ "https://ace-web.qtstage.io/TestStaticPage112849",
+ "https://ace-web.qtstage.io/TestStaticPage105557",
+ "https://ace-web.qtstage.io/TestStaticPage52515",
+ "https://ace-web.qtstage.io/TestStaticPage92649",
+ "https://ace-web.qtstage.io/TestStaticPage10638",
+ "https://ace-web.qtstage.io/TestStaticPage10645",
+ "https://ace-web.qtstage.io/TestStaticPage42119",
+ "https://ace-web.qtstage.io/TestStaticPage10307",
+ "https://ace-web.qtstage.io/TestStaticPage122653",
+ "https://ace-web.qtstage.io/TestStaticPage9916",
+ "https://ace-web.qtstage.io/TestStaticPage102535",
+ "https://ace-web.qtstage.io/TestStaticPage94323",
+ "https://ace-web.qtstage.io/TestStaticPage93353",
+ "https://ace-web.qtstage.io/TestStaticPage62925",
+ "https://ace-web.qtstage.io/TestStaticPage92734",
+ "https://ace-web.qtstage.io/TestStaticPage8204",
+ "https://ace-web.qtstage.io/TestStaticPage95345",
+ "https://ace-web.qtstage.io/TestStaticPage133344",
+ "https://ace-web.qtstage.io/TestStaticPage61443",
+ "https://ace-web.qtstage.io/TestStaticPage81246",
+ "https://ace-web.qtstage.io/TestStaticPage7110",
+ "https://ace-web.qtstage.io/community",
+ "https://ace-web.qtstage.io/TestStaticPage43611",
+ "https://ace-web.qtstage.io/TestStaticPage10140",
+ "https://ace-web.qtstage.io/TestStaticPage860",
+ "https://ace-web.qtstage.io/TestStaticPage124351",
+ "https://ace-web.qtstage.io/TestStaticPage51851",
+ "https://ace-web.qtstage.io/TestStaticPage83722",
+ "https://ace-web.qtstage.io/TestStaticPage112358",
+ "https://ace-web.qtstage.io/TestStaticPage42525",
+ "https://ace-web.qtstage.io/TestStaticPage7722",
+ "https://ace-web.qtstage.io/TestStaticPage71120",
+ "https://ace-web.qtstage.io/TestStaticPage6831",
+ "https://ace-web.qtstage.io/TestStaticPage82447",
+ "https://ace-web.qtstage.io/TestStaticPage93124",
+ "https://ace-web.qtstage.io/TestStaticPage75853",
+ "https://ace-web.qtstage.io/TestStaticPage112512",
+ "https://ace-web.qtstage.io/TestStaticPage6452",
+ "https://ace-web.qtstage.io/TestStaticPage51757",
+ "https://ace-web.qtstage.io/TestStaticPage53243",
+ "https://ace-web.qtstage.io/TestStaticPage133951",
+ "https://ace-web.qtstage.io/TestStaticPage54017",
+ "https://ace-web.qtstage.io/TestStaticPage11489",
+ "https://ace-web.qtstage.io/TestStaticPage14319",
+ "https://ace-web.qtstage.io/TestStaticPage71853",
+ "https://ace-web.qtstage.io/TestStaticPage92711",
+ "https://ace-web.qtstage.io/TestStaticPage6487",
+ "https://ace-web.qtstage.io/TestStaticPage7734",
+ "https://ace-web.qtstage.io/TestStaticPage1082",
+ "https://ace-web.qtstage.io/TestStaticPage154911",
+ "https://ace-web.qtstage.io/TestStaticPage42340",
+ "https://ace-web.qtstage.io/TestStaticPage165234",
+ "https://ace-web.qtstage.io/TestStaticPage115422",
+ "https://ace-web.qtstage.io/TestStaticPage101711",
+ "https://ace-web.qtstage.io/TestStaticPage114122",
+ "https://ace-web.qtstage.io/TestStaticPage7455",
+ "https://ace-web.qtstage.io/TestStaticPage72245",
+ "https://ace-web.qtstage.io/TestStaticPage153330",
+ "https://ace-web.qtstage.io/TestStaticPage72020",
+ "https://ace-web.qtstage.io/TestStaticPage125918",
+ "https://ace-web.qtstage.io/TestStaticPage5345",
+ "https://ace-web.qtstage.io/TestStaticPage61851",
+ "https://ace-web.qtstage.io/TestStaticPage111430",
+ "https://ace-web.qtstage.io/TestStaticPage9833",
+ "https://ace-web.qtstage.io/TestStaticPage1",
+ "https://ace-web.qtstage.io/TestStaticPage105214",
+ "https://ace-web.qtstage.io/TestStaticPage10157",
+ "https://ace-web.qtstage.io/TestStaticPage12922",
+ "https://ace-web.qtstage.io/TestStaticPage14324",
+ "https://ace-web.qtstage.io/partners",
+ "https://ace-web.qtstage.io/TestStaticPage102235",
+ "https://ace-web.qtstage.io/TestStaticPage102158",
+ "https://ace-web.qtstage.io/TestStaticPage95757",
+ "https://ace-web.qtstage.io/TestStaticPage45657",
+ "https://ace-web.qtstage.io/TestStaticPage113323",
+ "https://ace-web.qtstage.io/TestStaticPage83019",
+ "https://ace-web.qtstage.io/TestStaticPage123212",
+ "https://ace-web.qtstage.io/TestStaticPage114444",
+ "https://ace-web.qtstage.io/TestStaticPage14355",
+ "https://ace-web.qtstage.io/TestStaticPage62117",
+ "https://ace-web.qtstage.io/testing-test",
+ "https://ace-web.qtstage.io/TestStaticPage8355",
+ "https://ace-web.qtstage.io/TestStaticPage93313",
+ "https://ace-web.qtstage.io/TestStaticPage113232",
+ "https://ace-web.qtstage.io/TestStaticPage6029",
+ "https://ace-web.qtstage.io/TestStaticPage12615",
+ "https://ace-web.qtstage.io/TestStaticPage14259",
+ "https://ace-web.qtstage.io/TestStaticPage133558",
+ "https://ace-web.qtstage.io/TestStaticPage14326",
+ "https://ace-web.qtstage.io/TestStaticPage13755",
+ "https://ace-web.qtstage.io/TestStaticPage63948",
+ "https://ace-web.qtstage.io/TestStaticPage44025",
+ "https://ace-web.qtstage.io/TestStaticPage44047",
+ "https://ace-web.qtstage.io/TestStaticPage85244",
+ "https://ace-web.qtstage.io/TestStaticPage855",
+ "https://ace-web.qtstage.io/TestStaticPage141856",
+ "https://ace-web.qtstage.io/TestStaticPage61647",
+ "https://ace-web.qtstage.io/TestStaticPage102225",
+ "https://ace-web.qtstage.io/TestStaticPage141324",
+ "https://ace-web.qtstage.io/TestStaticPage43749",
+ "https://ace-web.qtstage.io/TestStaticPage103540",
+ "https://ace-web.qtstage.io/TestStaticPage125915",
+ "https://ace-web.qtstage.io/TestStaticPage111958",
+ "https://ace-web.qtstage.io/TestStaticPage582",
+ "https://ace-web.qtstage.io/TestStaticPage6201",
+ "https://ace-web.qtstage.io/TestStaticPage174515",
+ "https://ace-web.qtstage.io/TestStaticPage11142",
+ "https://ace-web.qtstage.io/TestStaticPage11511",
+ "https://ace-web.qtstage.io/TestStaticPage41237",
+ "https://ace-web.qtstage.io/TestStaticPage4429",
+ "https://ace-web.qtstage.io/TestStaticPage11238",
+ "https://ace-web.qtstage.io/TestStaticPage15848",
+ "https://ace-web.qtstage.io/election-tracker",
+ "https://ace-web.qtstage.io/TestStaticPage14331",
+ "https://ace-web.qtstage.io/TestStaticPage81539",
+ "https://ace-web.qtstage.io/TestStaticPage4934",
+ "https://ace-web.qtstage.io/TestStaticPage43049",
+ "https://ace-web.qtstage.io/TestStaticPage11537",
+ "https://ace-web.qtstage.io/TestStaticPage155150",
+ "https://ace-web.qtstage.io/TestStaticPage145822",
+ "https://ace-web.qtstage.io/TestStaticPage135330",
+ "https://ace-web.qtstage.io/TestStaticPage5525",
+ "https://ace-web.qtstage.io/TestStaticPage7394",
+ "https://ace-web.qtstage.io/TestStaticPage131531",
+ "https://ace-web.qtstage.io/TestStaticPage55310",
+ "https://ace-web.qtstage.io/TestStaticPage8357",
+ "https://ace-web.qtstage.io/TestStaticPage61852",
+ "https://ace-web.qtstage.io/TestStaticPage121314",
+ "https://ace-web.qtstage.io/TestStaticPage12035",
+ "https://ace-web.qtstage.io/TestStaticPage141211",
+ "https://ace-web.qtstage.io/TestStaticPage1762",
+ "https://ace-web.qtstage.io/TestStaticPage132350",
+ "https://ace-web.qtstage.io/TestStaticPage14349",
+ "https://ace-web.qtstage.io/TestStaticPage192816",
+ "https://ace-web.qtstage.io/TestStaticPage42658",
+ "https://ace-web.qtstage.io/TestStaticPage95820",
+ "https://ace-web.qtstage.io/TestStaticPage14332",
+ "https://ace-web.qtstage.io/TestStaticPage45543",
+ "https://ace-web.qtstage.io/TestStaticPage113953",
+ "https://ace-web.qtstage.io/TestStaticPage4750",
+ "https://ace-web.qtstage.io/TestStaticPage73548",
+ "https://ace-web.qtstage.io/TestStaticPage133144",
+ "https://ace-web.qtstage.io/TestStaticPage124433",
+ "https://ace-web.qtstage.io/TestStaticPage11913",
+ "https://ace-web.qtstage.io/TestStaticPage103117",
+ "https://ace-web.qtstage.io/TestStaticPage152025",
+ "https://ace-web.qtstage.io/TestStaticPage9163",
+ "https://ace-web.qtstage.io/TestStaticPage13146",
+ "https://ace-web.qtstage.io/TestStaticPage34517",
+ "https://ace-web.qtstage.io/TestStaticPage83245",
+ "https://ace-web.qtstage.io/TestStaticPage94350",
+ "https://ace-web.qtstage.io/TestStaticPage125954",
+ "https://ace-web.qtstage.io/TestStaticPage51236",
+ "https://ace-web.qtstage.io/TestStaticPage163026",
+ "https://ace-web.qtstage.io/TestStaticPage125542",
+ "https://ace-web.qtstage.io/TestStaticPage1133",
+ "https://ace-web.qtstage.io/TestStaticPage94836",
+ "https://ace-web.qtstage.io/TestStaticPage5037",
+ "https://ace-web.qtstage.io/TestStaticPage94731",
+ "https://ace-web.qtstage.io/TestStaticPage11347",
+ "https://ace-web.qtstage.io/TestStaticPage946",
+ "https://ace-web.qtstage.io/TestStaticPage133212",
+ "https://ace-web.qtstage.io/TestStaticPage114353",
+ "https://ace-web.qtstage.io/TestStaticPage7351",
+ "https://ace-web.qtstage.io/TestStaticPage54823",
+ "https://ace-web.qtstage.io/TestStaticPage85522",
+ "https://ace-web.qtstage.io/TestStaticPage62537",
+ "https://ace-web.qtstage.io/TestStaticPage7163",
+ "https://ace-web.qtstage.io/TestStaticPage92147",
+ "https://ace-web.qtstage.io/TestStaticPage101551",
+ "https://ace-web.qtstage.io/TestStaticPage64734",
+ "https://ace-web.qtstage.io/TestStaticPage65144",
+ "https://ace-web.qtstage.io/TestStaticPage105119",
+ "https://ace-web.qtstage.io/TestStaticPage14341",
+ "https://ace-web.qtstage.io/TestStaticPage104545",
+ "https://ace-web.qtstage.io/TestStaticPage6948",
+ "https://ace-web.qtstage.io/TestStaticPage13278",
+ "https://ace-web.qtstage.io/TestStaticPage64618",
+ "https://ace-web.qtstage.io/TestStaticPage73822",
+ "https://ace-web.qtstage.io/TestStaticPage55518",
+ "https://ace-web.qtstage.io/test-1",
+ "https://ace-web.qtstage.io/TestStaticPage173721",
+ "https://ace-web.qtstage.io/TestStaticPage64439",
+ "https://ace-web.qtstage.io/TestStaticPage123042",
+ "https://ace-web.qtstage.io/TestStaticPage22575",
+ "https://ace-web.qtstage.io/TestStaticPage14633",
+ "https://ace-web.qtstage.io/TestStaticPage45016",
+ "https://ace-web.qtstage.io/TestStaticPage43153",
+ "https://ace-web.qtstage.io/TestStaticPage62821",
+ "https://ace-web.qtstage.io/TestStaticPage43826",
+ "https://ace-web.qtstage.io/TestStaticPage71955",
+ "https://ace-web.qtstage.io/TestStaticPage42426",
+ "https://ace-web.qtstage.io/TestStaticPage101830",
+ "https://ace-web.qtstage.io/TestStaticPage43833",
+ "https://ace-web.qtstage.io/TestStaticPage53551",
+ "https://ace-web.qtstage.io/TestStaticPage14217",
+ "https://ace-web.qtstage.io/TestStaticPage94632",
+ "https://ace-web.qtstage.io/TestStaticPage94215",
+ "https://ace-web.qtstage.io/TestStaticPage15213",
+ "https://ace-web.qtstage.io/TestStaticPage175127",
+ "https://ace-web.qtstage.io/TestStaticPage",
+ "https://ace-web.qtstage.io/TestStaticPage65816",
+ "https://ace-web.qtstage.io/contact",
+ "https://ace-web.qtstage.io/TestStaticPage63712",
+ "https://ace-web.qtstage.io/TestStaticPage14752",
+ "https://ace-web.qtstage.io/TestStaticPage65935",
+ "https://ace-web.qtstage.io/TestStaticPage65430",
+ "https://ace-web.qtstage.io/TestStaticPage93122",
+ "https://ace-web.qtstage.io/TestStaticPage74223",
+ "https://ace-web.qtstage.io/TestStaticPage102412",
+ "https://ace-web.qtstage.io/TestStaticPage51754",
+ "https://ace-web.qtstage.io/TestStaticPage18456",
+ "https://ace-web.qtstage.io/TestStaticPage7149",
+ "https://ace-web.qtstage.io/TestStaticPage20531",
+ "https://ace-web.qtstage.io/TestStaticPage7272",
+ "https://ace-web.qtstage.io/TestStaticPage101333",
+ "https://ace-web.qtstage.io/TestStaticPage6511",
+ "https://ace-web.qtstage.io/about-us",
+ "https://ace-web.qtstage.io/TestStaticPage5317",
+ "https://ace-web.qtstage.io/TestStaticPage12505",
+ "https://ace-web.qtstage.io/TestStaticPage204627",
+ "https://ace-web.qtstage.io/TestStaticPage93741",
+ "https://ace-web.qtstage.io/TestStaticPage123143",
+ "https://ace-web.qtstage.io/TestStaticPage52235",
+ "https://ace-web.qtstage.io/TestStaticPage2",
+ "https://ace-web.qtstage.io/TestStaticPage75753",
+ "https://ace-web.qtstage.io/TestStaticPage73010",
+ "https://ace-web.qtstage.io/sample",
+ "https://ace-web.qtstage.io/TestStaticPage224045",
+ "https://ace-web.qtstage.io/TestStaticPage958",
+ "https://ace-web.qtstage.io/TestStaticPage72712",
+ "https://ace-web.qtstage.io/TestStaticPage31621",
+ "https://ace-web.qtstage.io/TestStaticPage101149",
+ "https://ace-web.qtstage.io/TestStaticPage7726",
+ "http://ace-web.staging.quintype.io/terms-and-conditions",
+ "https://ace-web.qtstage.io/TestStaticPage65323",
+ "https://ace-web.qtstage.io/TestStaticPage12713",
+ "https://ace-web.qtstage.io/TestStaticPage103040",
+ "https://ace-web.qtstage.io/TestStaticPage44221",
+ "https://ace-web.qtstage.io/TestStaticPage103815",
+ "https://ace-web.qtstage.io/TestStaticPage131744",
+ "https://ace-web.qtstage.io/TestStaticPage134512",
+ "https://ace-web.qtstage.io/TestStaticPage63645",
+ "https://ace-web.qtstage.io/TestStaticPage14955",
+ "https://ace-web.qtstage.io/TestStaticPage64821",
+ "https://ace-web.qtstage.io/TestStaticPage82347",
+ "https://ace-web.qtstage.io/TestStaticPage52859",
+ "https://ace-web.qtstage.io/TestStaticPage1342",
+ "https://ace-web.qtstage.io/TestStaticPage12651",
+ "https://ace-web.qtstage.io/xxx123xxx",
+ "https://ace-web.qtstage.io/TestStaticPage9114",
+ "https://ace-web.qtstage.io/TestStaticPage14244",
+ "https://ace-web.qtstage.io/source",
+ "https://ace-web.qtstage.io/TestStaticPage123716",
+ "https://ace-web.qtstage.io/TestStaticPage6115",
+ "https://ace-web.qtstage.io/TestStaticPage71827",
+ "https://ace-web.qtstage.io/TestStaticPage81957",
+ "https://ace-web.qtstage.io/TestStaticPage13715",
+ "https://ace-web.qtstage.io/TestStaticPage51432",
+ "https://ace-web.qtstage.io/TestStaticPage316232",
+ "https://ace-web.qtstage.io/TestStaticPage114326",
+ "https://ace-web.qtstage.io/TestStaticPage82326",
+ "https://ace-web.qtstage.io/TestStaticPage74535",
+ "https://ace-web.qtstage.io/TestStaticPage10594",
+ "https://ace-web.qtstage.io/TestStaticPage101846",
+ "https://ace-web.qtstage.io/TestStaticPage14642",
+ "https://ace-web.qtstage.io/TestStaticPage123955",
+ "https://ace-web.qtstage.io/TestStaticPage94229",
+ "https://ace-web.qtstage.io/TestStaticPage102126",
+ "https://ace-web.qtstage.io/TestStaticPage62237",
+ "https://ace-web.qtstage.io/TestStaticPage12445",
+ "https://ace-web.qtstage.io/TestStaticPage141623",
+ "https://ace-web.qtstage.io/TestStaticPage1413",
+ "https://ace-web.qtstage.io/TestStaticPage43052",
+ "https://ace-web.qtstage.io/TestStaticPage4261",
+ "https://ace-web.qtstage.io/TestStaticPage53443",
+ "https://ace-web.qtstage.io/TestStaticPage114230",
+ "https://ace-web.qtstage.io/TestStaticPage14255",
+ "https://ace-web.qtstage.io/TestStaticPage112624",
+ "https://ace-web.qtstage.io/TestStaticPage13911",
+ "https://ace-web.qtstage.io/TestStaticPage94728",
+ "https://ace-web.qtstage.io/TestStaticPage134715",
+ "https://ace-web.qtstage.io/TestStaticPage52118",
+ "https://ace-web.qtstage.io/TestStaticPage124333",
+ "https://ace-web.qtstage.io/TestStaticPage73413",
+ "https://ace-web.qtstage.io/TestStaticPage123851",
+ "https://ace-web.qtstage.io/TestStaticPage51425",
+ "https://ace-web.qtstage.io/TestStaticPage134050",
+ "https://ace-web.qtstage.io/TestStaticPage51925",
+ "https://ace-web.qtstage.io/TestStaticPage93749",
+ "https://ace-web.qtstage.io/TestStaticPage111425",
+ "https://ace-web.qtstage.io/TestStaticPage131228",
+ "https://ace-web.qtstage.io/TestStaticPage121242",
+ "https://ace-web.qtstage.io/TestStaticPage9735",
+ "https://ace-web.qtstage.io/TestStaticPage13925",
+ "https://ace-web.qtstage.io/TestStaticPage115036",
+ "https://ace-web.qtstage.io/TestStaticPage504",
+ "https://ace-web.qtstage.io/TestStaticPage44437",
+ "https://ace-web.qtstage.io/TestStaticPage13627",
+ "https://ace-web.qtstage.io/TestStaticPage112642",
+ "https://ace-web.qtstage.io/TestStaticPage91932",
+ "https://ace-web.qtstage.io/TestStaticPage9545",
+ "https://ace-web.qtstage.io/TestStaticPage122734",
+ "https://ace-web.qtstage.io/TestStaticPage112812",
+ "https://ace-web.qtstage.io/TestStaticPage43253",
+ "https://ace-web.qtstage.io/TestStaticPage103531",
+ "https://ace-web.qtstage.io/TestStaticPage55323",
+ "https://ace-web.qtstage.io/TestStaticPage103211",
+ "https://ace-web.qtstage.io/TestStaticPage4859",
+ "https://ace-web.qtstage.io/TestStaticPage71340",
+ "https://ace-web.qtstage.io/TestStaticPage7354",
+ "https://ace-web.qtstage.io/TestStaticPage1482",
+ "https://ace-web.qtstage.io/TestStaticPage16594",
+ "https://ace-web.qtstage.io/TestStaticPage183756",
+ "https://ace-web.qtstage.io/TestStaticPage101419",
+ "https://ace-web.qtstage.io/TestStaticPage162054",
+ "https://ace-web.qtstage.io/TestStaticPage114035",
+ "https://ace-web.qtstage.io/TestStaticPage5335",
+ "https://ace-web.qtstage.io/TestStaticPage43758",
+ "https://ace-web.qtstage.io/TestStaticPage83439",
+ "https://ace-web.qtstage.io/TestStaticPage43845",
+ "https://ace-web.qtstage.io/TestStaticPage102541",
+ "https://ace-web.qtstage.io/TestStaticPage14235",
+ "https://ace-web.qtstage.io/TestStaticPage13334",
+ "https://ace-web.qtstage.io/TestStaticPage63915",
+ "https://ace-web.qtstage.io/TestStaticPage63458",
+ "https://ace-web.qtstage.io/TestStaticPage21510",
+ "https://ace-web.qtstage.io/brochure",
+ "https://ace-web.qtstage.io/TestStaticPage123543",
+ "https://ace-web.qtstage.io/TestStaticPage4051",
+ "https://ace-web.qtstage.io/TestStaticPage113555",
+ "https://ace-web.qtstage.io/TestStaticPage112235",
+ "https://ace-web.qtstage.io/TestStaticPage4237",
+ "https://ace-web.qtstage.io/TestStaticPage101726",
+ "https://ace-web.qtstage.io/TestStaticPage71525",
+ "https://ace-web.qtstage.io/TestStaticPage95832",
+ "https://ace-web.qtstage.io/TestStaticPage174842",
+ "https://ace-web.qtstage.io/TestStaticPage94558",
+ "https://ace-web.qtstage.io/TestStaticPage1437",
+ "https://ace-web.qtstage.io/TestStaticPage8588",
+ "https://ace-web.qtstage.io/TestStaticPage43529",
+ "https://ace-web.qtstage.io/TestStaticPage12735",
+ "https://ace-web.qtstage.io/about-us-1",
+ "https://ace-web.qtstage.io/TestStaticPage112651",
+ "https://ace-web.qtstage.io/TestStaticPage141626",
+ "https://ace-web.qtstage.io/TestStaticPage6820",
+ "https://ace-web.qtstage.io/TestStaticPage1137",
+ "https://ace-web.qtstage.io/TestStaticPage65750",
+ "https://ace-web.qtstage.io/TestStaticPage102929",
+ "https://ace-web.qtstage.io/TestStaticPage154629",
+ "https://ace-web.qtstage.io/TestStaticPage43444",
+ "https://ace-web.qtstage.io/TestStaticPage174117",
+ "https://ace-web.qtstage.io/TestStaticPage10851",
+ "https://ace-web.qtstage.io/TestStaticPage122559",
+ "https://ace-web.qtstage.io/TestStaticPage175316",
+ "https://ace-web.qtstage.io/TestStaticPage93426",
+ "https://ace-web.qtstage.io/TestStaticPage53836",
+ "https://ace-web.qtstage.io/TestStaticPage62010",
+ "https://ace-web.qtstage.io/TestStaticPage93832",
+ "https://ace-web.qtstage.io/TestStaticPage54154",
+ "https://ace-web.qtstage.io/TestStaticPage84517",
+ "https://ace-web.qtstage.io/TestStaticPage122514"
+ ],
+ "nudge-host": "https://ace.qtstage.io",
+ "num-more-stories": 20,
+ "polltype-host": "https://polltype-api.qtstage.io",
+ "apps-data": {},
+ "razorpay-gateway-key": null,
+ "story-attributes": [
+ {
+ "type": "story",
+ "name": "source",
+ "values": [
+ "India",
+ "HK",
+ "Thailand"
+ ],
+ "display-name": "Source",
+ "attribute-level": "single",
+ "data-type": "multi-valued-strings"
+ },
+ {
+ "type": "story",
+ "name": "demo",
+ "values": [
+ "de",
+ "se"
+ ],
+ "display-name": "demo",
+ "attribute-level": "multi",
+ "data-type": "multi-valued-strings"
+ },
+ {
+ "type": "story",
+ "name": "ampstory",
+ "values": [
+ "true"
+ ],
+ "display-name": "AMP story",
+ "attribute-level": "single",
+ "data-type": "multi-valued-strings"
+ },
+ {
+ "type": "card",
+ "name": "visualstorystyle",
+ "values": [
+ "Shadow",
+ "Box"
+ ],
+ "display-name": "Visual Story Style",
+ "attribute-level": "single",
+ "data-type": "multi-valued-strings"
+ },
+ {
+ "type": "card",
+ "name": "visualstoryposition",
+ "values": [
+ "Top",
+ "Bottom",
+ "Centre"
+ ],
+ "display-name": "Visual Story Position",
+ "attribute-level": "single",
+ "data-type": "multi-valued-strings"
+ },
+ {
+ "type": "card",
+ "name": "elementalignment",
+ "values": [
+ "left",
+ "right",
+ "Left",
+ "Right"
+ ],
+ "display-name": "Element Alignment",
+ "attribute-level": "single",
+ "data-type": "multi-valued-strings"
+ }
+ ],
+ "mins-between-refreshes": 25
+}
\ No newline at end of file
diff --git a/app/isomorphic/arrow/components/Fixture/dummy-collection.js b/app/isomorphic/arrow/components/Fixture/dummy-collection.js
new file mode 100644
index 000000000..8227fc012
--- /dev/null
+++ b/app/isomorphic/arrow/components/Fixture/dummy-collection.js
@@ -0,0 +1,2771 @@
+export const dummyCollection = {
+ "updated-at": 1571041608154,
+ slug: "dummy",
+ fallback: false,
+ name: "dummy",
+ "data-source": "manual",
+ automated: false,
+ template: "default",
+ rules: {},
+ summary: "dummy",
+ id: 76330,
+ "total-count": 16,
+ "collection-date": null,
+ items: [
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: 40827,
+ "associated-metadata": {},
+ type: "collection",
+ name: "col02",
+ slug: "col02",
+ template: "default",
+ metadata: {
+ "cover-image": {},
+ },
+ "collection-date": null,
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1568978484435,
+ seo: {
+ "meta-description": "Meta description",
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 299252,
+ "author-name": "Ashwini Mohan",
+ tags: [
+ {
+ id: 168475,
+ name: "news",
+ "meta-description": null,
+ slug: "news",
+ "tag-type": "Tag",
+ },
+ ],
+ headline: "Untitled Sep 20, 2019 04:30 PM",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ slug: "recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "last-published-at": 1568978485926,
+ subheadline: "TESTING DESCRIPTION FIELD.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "business",
+ name: "Business",
+ "section-url": "https://ace-web.qtstage.io/recent-stories/news/business",
+ id: 5752,
+ "parent-id": 5670,
+ "display-name": "Business",
+ collection: {
+ slug: "business1",
+ name: "Business News",
+ id: 2729,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-09/b4f33f98-8814-4675-97f0-75e447e43ab8/.01634f09-e91b-442b-90a4-521052bdffaf.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1568977220927,
+ "owner-name": "Ashwini Mohan",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1568978485926,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: "Social Description",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "attribution",
+ "bullet-type": "123",
+ id: "b4f33f98-8814-4675-97f0-75e447e43ab8",
+ "hero-image-s3-key":
+ "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/b4f33f98-8814-4675-97f0-75e447e43ab8/element/212be322-baaa-46a1-ae92-7f8f79fc4107",
+ type: "youtube-video",
+ "family-id": "e6b6aecf-3bc6-451b-ac69-33aa3d79ce6b",
+ title: "",
+ id: "212be322-baaa-46a1-ae92-7f8f79fc4107",
+ url: "https://www.youtube.com/watch?v=OY9Oq3hp0YU&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/OY9Oq3hp0YU",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1568977881112,
+ "content-version-id": "1cd5ea42-6751-4ec6-a161-395f3326ce00",
+ "card-added-at": 1568977256690,
+ status: "draft",
+ id: "8db07059-5344-4859-bfbb-441148b13a02",
+ "content-id": "8db07059-5344-4859-bfbb-441148b13a02",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Untitled Sep 20, 2019 04:30 PM",
+ message: "Social Description",
+ image: {
+ key: "ace/2019-06/3999bc52-1280-49aa-b542-20cd9e06dd34/The_Secret_to_a_Good_Quality_Sleep_Memory_Foam.jpg",
+ url: null,
+ attribution: "attribution",
+ caption: "caption",
+ metadata: {
+ width: 1024,
+ height: 576,
+ "focus-point": [676, 312],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/recent-stories/news/business/untitled-sep-20-2019-0430-pm",
+ "story-version-id": "a64fe8b4-c0aa-4bd5-93a2-6e428332e5b2",
+ "content-type": "story",
+ "content-updated-at": 1568978486084,
+ "author-id": 299252,
+ "owner-id": 299252,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1568977881440,
+ "hero-image-caption": "caption",
+ version: 7,
+ "story-template": "video",
+ "sequence-no": null,
+ "created-at": 1568978481003,
+ authors: [
+ {
+ id: 299252,
+ name: "Ashwini Mohan",
+ slug: "ashwini-mohan",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ashwini Mohan",
+ },
+ },
+ {
+ id: 42230,
+ "associated-metadata": {},
+ type: "collection",
+ name: "New Collection for test 123",
+ slug: "new-collection-for-test-123",
+ template: "section",
+ metadata: {
+ "cover-image": {
+ "cover-image-url":
+ "http://thumbor-stg.assettype.com/ace/2018-10/7caece74-bdc3-4f1e-bcc7-80d7129d770c/trailer.jpeg",
+ "cover-image-metadata": {
+ width: 720,
+ "mime-type": "image/jpeg",
+ height: 1080,
+ "focus-point": [350, 648],
+ },
+ "cover-image-s3-key": "ace/2018-10/7caece74-bdc3-4f1e-bcc7-80d7129d770c/trailer.jpeg",
+ caption: "thugs of hindustan",
+ },
+ },
+ "collection-date": null,
+ },
+ {
+ id: 67979,
+ "associated-metadata": {},
+ type: "collection",
+ name: "Collection Test",
+ slug: "collectiontest",
+ template: "default",
+ metadata: {
+ "cover-image": null,
+ },
+ "collection-date": null,
+ },
+ {
+ id: 68415,
+ "associated-metadata": {},
+ type: "collection",
+ name: "Foo test collection",
+ slug: "foo",
+ template: "default",
+ metadata: {
+ "cover-image": null,
+ },
+ "collection-date": null,
+ },
+ {
+ id: "7afda60c-1360-4320-bbe2-d6e29a8bcb1d",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1570786673740,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 187245,
+ "author-name": "Shreya Shukla",
+ tags: [],
+ headline: "Subscribed story time stamp test",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "7afda60c-1360-4320-bbe2-d6e29a8bcb1d",
+ slug: "health/time-stamp-test",
+ "linked-stories": {
+ "39b60649-aa6f-49f6-8365-0981b799b3b7": {
+ "author-name": "Shitesh Jain",
+ headline: "New Summary test",
+ "story-content-id": "39b60649-aa6f-49f6-8365-0981b799b3b7",
+ slug: "current-affairs/new-summary-test",
+ sections: [
+ {
+ id: 6449,
+ name: "Current Affairs",
+ slug: "current-affairs",
+ data: null,
+ },
+ ],
+ "hero-image-metadata": {
+ width: 960,
+ "mime-type": "image/png",
+ height: 603,
+ "focus-point": [535, 225],
+ },
+ "hero-image-s3-key": "ace/2018-12/d65b8758-9130-417f-bd97-c181661d6701/cat_1285634_960_720.png",
+ url: "https://ace-web.qtstage.io/current-affairs/new-summary-test",
+ "content-updated-at": 1555409545429,
+ "author-id": 234915,
+ "first-published-at": 1555408930663,
+ authors: [
+ {
+ id: 234915,
+ name: "Shitesh Jain",
+ slug: "shitesh-jain-4",
+ "avatar-url": "https://lh3.googleusercontent.com/a-/AAuE7mBO5TOGmzwz6_e4-1i0wQ2-Z8qEnkyp3GWL-aGm",
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ },
+ },
+ "last-published-at": 1570786676787,
+ subheadline:
+ "time stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp test",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "health",
+ name: "Health",
+ "section-url": "https://ace-web.qtstage.io/health",
+ id: 11181,
+ "parent-id": null,
+ "display-name": "Health",
+ collection: {
+ slug: "health",
+ name: "Health",
+ id: 15603,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-10/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/.cd246a47-5dd8-4636-8db7-d15b205ca999.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1569910912673,
+ "owner-name": "Shreya Shukla",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 266,
+ height: 190,
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1570786676787,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "Bacardi test ",
+ "bullet-type": "123",
+ id: "7afda60c-1360-4320-bbe2-d6e29a8bcb1d",
+ "hero-image-s3-key": "ace/2018-12/89a3fa9c-0b79-4140-b0ef-d1a35ea0b733/dog4.jpeg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/14890d2e-936a-459e-aed5-dd8e330c38d3",
+ type: "text",
+ "family-id": "950885ec-e6c2-4ee7-9269-d2de475338d2",
+ title: "",
+ id: "14890d2e-936a-459e-aed5-dd8e330c38d3",
+ metadata: {},
+ subtype: "summary",
+ text: "time stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp testtime stamp test
",
+ },
+ ],
+ "card-updated-at": 1569910964026,
+ "content-version-id": "fcf58ef5-1e24-41b0-ae31-d0de0594cf7f",
+ "card-added-at": 1569910912686,
+ status: "draft",
+ id: "a6781258-a4ee-49e6-aec5-f5383db3abfa",
+ "content-id": "a6781258-a4ee-49e6-aec5-f5383db3abfa",
+ version: 3,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Subscribed story time stamp test",
+ message: null,
+ image: {
+ key: "ace/2018-12/89a3fa9c-0b79-4140-b0ef-d1a35ea0b733/dog4.jpeg",
+ url: null,
+ attribution: "Bacardi test ",
+ caption: "test image caption ",
+ metadata: {
+ width: 266,
+ height: 190,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/cfd952a3-ce14-455d-bc27-6fd8ba9720c8",
+ type: "text",
+ "family-id": "3cd75898-e31f-404c-ad68-16f4b4aa6be7",
+ title: "",
+ id: "cfd952a3-ce14-455d-bc27-6fd8ba9720c8",
+ metadata: {
+ "linked-story": {
+ headline: "New Summary test",
+ "story-content-id": "39b60649-aa6f-49f6-8365-0981b799b3b7",
+ "highlighted-text": "",
+ id: "39b60649-aa6f-49f6-8365-0981b799b3b7",
+ "highlighted-headline": null,
+ },
+ "linked-story-id": "39b60649-aa6f-49f6-8365-0981b799b3b7",
+ },
+ subtype: "also-read",
+ text: "New Summary test",
+ },
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/927048d7-7410-4c11-83df-12ddcc6e0705",
+ type: "text",
+ "family-id": "575266d7-935b-4ec1-bd80-2e5deee1b7be",
+ title: "",
+ id: "927048d7-7410-4c11-83df-12ddcc6e0705",
+ metadata: {},
+ subtype: null,
+ text: "n literary theory, a text is any object that can be “read”, whether this object is a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.n literary theory, a text is any object that can be “read”, whether this object is
",
+ },
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/1b742dc6-b708-40f4-a365-03340bf111bf",
+ type: "text",
+ "family-id": "3e89c0ef-3f77-472f-a437-cc269c151578",
+ title: "",
+ id: "1b742dc6-b708-40f4-a365-03340bf111bf",
+ metadata: {},
+ subtype: null,
+ text: "a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.n literary theory, a text is any object that can be “read”, whether this object is a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.
",
+ },
+ ],
+ "card-updated-at": 1569910964026,
+ "content-version-id": "21fc3ac1-304d-46fe-a467-e114cca94ef7",
+ "card-added-at": 1569910960556,
+ status: "draft",
+ id: "399ed9ee-84b3-44fa-918c-5d3e6db3a00b",
+ "content-id": "399ed9ee-84b3-44fa-918c-5d3e6db3a00b",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Subscribed story time stamp test",
+ message: null,
+ image: {
+ key: "ace/2018-12/89a3fa9c-0b79-4140-b0ef-d1a35ea0b733/dog4.jpeg",
+ url: null,
+ attribution: "Bacardi test ",
+ caption: "test image caption ",
+ metadata: {
+ width: 266,
+ height: 190,
+ },
+ },
+ },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/6afe8b08-59af-4886-8d55-92bbf5fbebeb",
+ type: "text",
+ "family-id": "4cb68e4c-556e-49e4-917f-41a9754815f6",
+ title: "",
+ id: "6afe8b08-59af-4886-8d55-92bbf5fbebeb",
+ metadata: {},
+ subtype: null,
+ text: "a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.n literary theory, a text is any object that can be “read”, whether this object is a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.n literary theory, a text is any object that can be “read”, whether this object is a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.
",
+ },
+ ],
+ "card-updated-at": 1570786670702,
+ "content-version-id": "68b4c7d6-489c-42c3-b61a-83b159168dc3",
+ "card-added-at": 1570786670702,
+ status: "draft",
+ id: "e46eb9a5-c0eb-40a1-84c4-da93b83ec752",
+ "content-id": "e46eb9a5-c0eb-40a1-84c4-da93b83ec752",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Subscribed story time stamp test",
+ message: null,
+ image: {
+ key: "ace/2018-12/89a3fa9c-0b79-4140-b0ef-d1a35ea0b733/dog4.jpeg",
+ url: null,
+ attribution: "Bacardi test ",
+ caption: "test image caption ",
+ metadata: {
+ width: 266,
+ height: 190,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/7afda60c-1360-4320-bbe2-d6e29a8bcb1d/element/10d364a0-ddf3-433b-ab07-235f29e21041",
+ type: "text",
+ "family-id": "cfc8d303-17cc-4e8f-9bf2-0815e840358c",
+ title: "",
+ id: "10d364a0-ddf3-433b-ab07-235f29e21041",
+ metadata: {},
+ subtype: null,
+ text: "a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.n literary theory, a text is any object that can be “read”, whether this object is a work of literature, a street sign, an arrangement of buildings on a city block, or styles of clothing. It is a coherent set of signs that transmits some kind of informative message.
",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1570786670702,
+ "content-version-id": "c3a31b91-b606-4524-a36b-a35fbd174585",
+ "card-added-at": 1570786670702,
+ status: "draft",
+ id: "f96ce2f4-550b-476a-889a-24e0b1b8d704",
+ "content-id": "f96ce2f4-550b-476a-889a-24e0b1b8d704",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Subscribed story time stamp test",
+ message: null,
+ image: {
+ key: "ace/2018-12/89a3fa9c-0b79-4140-b0ef-d1a35ea0b733/dog4.jpeg",
+ url: null,
+ attribution: "Bacardi test ",
+ caption: "test image caption ",
+ metadata: {
+ width: 266,
+ height: 190,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/health/time-stamp-test",
+ "story-version-id": "fd873d9b-5f08-4d8e-ac03-eb3f30eaf619",
+ "content-type": "story",
+ "content-updated-at": 1570786676934,
+ "author-id": 187245,
+ "owner-id": 187245,
+ "linked-story-ids": ["39b60649-aa6f-49f6-8365-0981b799b3b7"],
+ access: "subscription",
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1569910964399,
+ "hero-image-caption": "test image caption ",
+ version: 5,
+ "story-template": "text",
+ "sequence-no": null,
+ "created-at": 1570786670689,
+ authors: [
+ {
+ id: 187245,
+ name: "Shreya Shukla",
+ slug: "shreya-shukla-2",
+ "avatar-url":
+ "https://lh6.googleusercontent.com/-pBdJGAfN81c/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfmrjS-gWdYzH3Gtmuib3KbVLdixw/photo.jpg",
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Shreya Shukla",
+ },
+ },
+ {
+ id: "f299b7e4-affa-4f22-8143-c700a736f1bd",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1570794627091,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 187245,
+ "author-name": "Shreya Shukla",
+ tags: [],
+ headline: "Login demo story",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "f299b7e4-affa-4f22-8143-c700a736f1bd",
+ slug: "parent-sec/child-sec/demo-story-2",
+ "last-published-at": 1570794629699,
+ subheadline: "demo story",
+ alternative: {
+ home: {
+ default: {
+ headline: "alternate story",
+ "hero-image": null,
+ },
+ },
+ },
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "child-sec",
+ name: "child-sec",
+ "section-url": "https://ace-web.qtstage.io/parent-sec/child-sec",
+ id: 11825,
+ "parent-id": 11824,
+ "display-name": "child-sec",
+ collection: {
+ slug: "child-sec-parent-sec",
+ name: "child-sec (parent-sec)",
+ id: 17115,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-10/f299b7e4-affa-4f22-8143-c700a736f1bd/.d60110cf-9fdd-4366-8e36-6f0cfb3956e7.mp3",
+ },
+ "access-level-value": null,
+ "content-created-at": 1569826641547,
+ "owner-name": "Shreya Shukla",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 760,
+ "mime-type": "image/jpeg",
+ height: 506,
+ "focus-point": [439, 232],
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1570794629699,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "happy panda",
+ "bullet-type": "123",
+ id: "f299b7e4-affa-4f22-8143-c700a736f1bd",
+ "hero-image-s3-key": "ace/2019-05/8eb4b983-f33f-4fa4-926b-115b7dd60c21/panda.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/f299b7e4-affa-4f22-8143-c700a736f1bd/element/9c89611d-b826-48f4-9a26-bab2ddc0eb88",
+ type: "text",
+ "family-id": "3845f45a-f599-4a01-ba1d-4224318a13e8",
+ title: "",
+ id: "9c89611d-b826-48f4-9a26-bab2ddc0eb88",
+ metadata: {},
+ subtype: "summary",
+ text: "We're experiencing a problem here, our developers are on the case. Please try it later.
",
+ },
+ ],
+ "card-updated-at": 1569826764243,
+ "content-version-id": "f341dc46-b043-4c8d-ac8f-ec737e5f1c25",
+ "card-added-at": 1569826743217,
+ status: "draft",
+ id: "92eb9283-6877-4b5e-a0f4-205415da4493",
+ "content-id": "92eb9283-6877-4b5e-a0f4-205415da4493",
+ version: 3,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Login demo story",
+ message: null,
+ image: {
+ key: "ace/2019-05/8eb4b983-f33f-4fa4-926b-115b7dd60c21/panda.jpg",
+ url: null,
+ attribution: "happy panda",
+ caption: "panda",
+ metadata: {
+ width: 760,
+ "mime-type": "image/jpeg",
+ height: 506,
+ "focus-point": [439, 232],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/f299b7e4-affa-4f22-8143-c700a736f1bd/element/c7686ef5-2969-44b2-b1a8-6ad7935450e5",
+ type: "text",
+ "family-id": "16677d69-5bcf-4b66-a07a-460eb77b3384",
+ title: "",
+ id: "c7686ef5-2969-44b2-b1a8-6ad7935450e5",
+ metadata: {},
+ subtype: null,
+ text: "Card2
",
+ },
+ ],
+ "card-updated-at": 1570794624997,
+ "content-version-id": "51549b8a-6773-4566-8b94-7bde39b8a750",
+ "card-added-at": 1570794624997,
+ status: "draft",
+ id: "ece586e2-eb7f-462c-841c-f74ac424fc50",
+ "content-id": "ece586e2-eb7f-462c-841c-f74ac424fc50",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Login demo story",
+ message: null,
+ image: {
+ key: "ace/2019-05/8eb4b983-f33f-4fa4-926b-115b7dd60c21/panda.jpg",
+ url: null,
+ attribution: "happy panda",
+ caption: "panda",
+ metadata: {
+ width: 760,
+ "mime-type": "image/jpeg",
+ height: 506,
+ "focus-point": [439, 232],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/f299b7e4-affa-4f22-8143-c700a736f1bd/element/2ec760e4-9194-4ea2-86a0-6ab0070cb0dd",
+ type: "text",
+ "family-id": "a60e47f7-cd38-4a4b-819f-3485678ebdb9",
+ title: "",
+ id: "2ec760e4-9194-4ea2-86a0-6ab0070cb0dd",
+ metadata: {},
+ subtype: null,
+ text: "card 3
",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1570794624997,
+ "content-version-id": "6ae26343-7c87-484f-a5a7-b4f41855b50c",
+ "card-added-at": 1570794624997,
+ status: "draft",
+ id: "18a2fdb0-3e43-40f7-99e9-67ad0bf2bbca",
+ "content-id": "18a2fdb0-3e43-40f7-99e9-67ad0bf2bbca",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Login demo story",
+ message: null,
+ image: {
+ key: "ace/2019-05/8eb4b983-f33f-4fa4-926b-115b7dd60c21/panda.jpg",
+ url: null,
+ attribution: "happy panda",
+ caption: "panda",
+ metadata: {
+ width: 760,
+ "mime-type": "image/jpeg",
+ height: 506,
+ "focus-point": [439, 232],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/parent-sec/child-sec/demo-story-2",
+ "story-version-id": "06320d9a-db59-4ff7-ad18-4b2e3649b1bb",
+ "content-type": "story",
+ "content-updated-at": 1570794629865,
+ "author-id": 187245,
+ "owner-id": 187245,
+ "linked-story-ids": [],
+ access: "login",
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1569826764621,
+ "hero-image-caption": "panda",
+ version: 9,
+ "story-template": "text",
+ "sequence-no": null,
+ "created-at": 1570794624975,
+ authors: [
+ {
+ id: 187245,
+ name: "Shreya Shukla",
+ slug: "shreya-shukla-2",
+ "avatar-url":
+ "https://lh6.googleusercontent.com/-pBdJGAfN81c/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfmrjS-gWdYzH3Gtmuib3KbVLdixw/photo.jpg",
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Shreya Shukla",
+ },
+ },
+ {
+ id: "8ed73f9f-ac4e-453b-8508-1b5fbd99e71d",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1571032556498,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 187245,
+ "author-name": "Shreya Shukla",
+ tags: [],
+ headline: "listicle with bullets.",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "8ed73f9f-ac4e-453b-8508-1b5fbd99e71d",
+ slug: "lifestyle/listicle-with-reverse-count",
+ "last-published-at": 1571032559396,
+ subheadline: "listicle with reverse countlisticle with reverse countlisticle with reverse count",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "lifestyle",
+ name: "Lifestyle",
+ "section-url": "https://ace-web.qtstage.io/lifestyle",
+ id: 5751,
+ "parent-id": null,
+ "display-name": "Lifestyle",
+ collection: {
+ slug: "lifestyle-updated-slug",
+ name: "sports",
+ id: 2728,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key":
+ "ace/story-audio/2019-10/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/.f6427b6a-1695-4718-9ced-9c1eab9303e8.mp3",
+ },
+ "access-level-value": 100,
+ "content-created-at": 1559584761098,
+ "owner-name": "Shreya Shukla",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 528,
+ height: 350,
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1571032559396,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution":
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestatt",
+ "bullet-type": "bullets",
+ id: "8ed73f9f-ac4e-453b-8508-1b5fbd99e71d",
+ "hero-image-s3-key": "ace/2018-12/c19f080d-024d-4080-8bb0-13a2d85242a2/rose_blue_flower_rose_blooms_67636.jpeg",
+ contributors: [],
+ cards: [
+ {
+ "card-type": "introduction",
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/f4e0e915-046b-485f-99a2-b6627d2952c7",
+ type: "text",
+ "family-id": "f93b4a80-b160-47d9-bea6-8e8f6b9eb502",
+ title: "",
+ id: "f4e0e915-046b-485f-99a2-b6627d2952c7",
+ metadata: {},
+ subtype: null,
+ text: "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.
",
+ },
+ ],
+ "card-updated-at": 1564139386962,
+ "content-version-id": "7d8792eb-5e65-461b-b956-44ded3f0d24b",
+ "card-added-at": 1559584761115,
+ status: "draft",
+ id: "442ab923-a0ee-46c6-8f2a-9d1e77897219",
+ "content-id": "442ab923-a0ee-46c6-8f2a-9d1e77897219",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2018-12/c19f080d-024d-4080-8bb0-13a2d85242a2/rose_blue_flower_rose_blooms_67636.jpeg",
+ url: null,
+ attribution:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestatt",
+ caption:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontesta",
+ metadata: {
+ width: 528,
+ height: 350,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/5368bde6-ccb6-4982-8d55-11847b9f3154",
+ type: "title",
+ "family-id": "6ac7aefc-ca88-40d3-b44d-d17df2f0d399",
+ title: "",
+ id: "5368bde6-ccb6-4982-8d55-11847b9f3154",
+ metadata: {},
+ subtype: null,
+ text: "hello i am first carddfghjkljhgfdfghj",
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/772acc11-18a5-4708-9bbf-358e49cd74ce",
+ type: "text",
+ "family-id": "b95f7938-cba5-4f24-a2b0-8d9a7916e5a7",
+ title: "",
+ id: "772acc11-18a5-4708-9bbf-358e49cd74ce",
+ metadata: {},
+ subtype: null,
+ text: "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.
",
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/1c9a67ee-4dd9-46ef-9302-d1e8287761f0",
+ type: "text",
+ "family-id": "9b455959-c6bb-4f1d-b632-44302281455f",
+ title: "",
+ id: "1c9a67ee-4dd9-46ef-9302-d1e8287761f0",
+ metadata: {},
+ subtype: null,
+ text: "hello
",
+ },
+ ],
+ "card-updated-at": 1571032554331,
+ "content-version-id": "1ed6c4b7-bcc0-4a86-827d-35dc78d60ecb",
+ "card-added-at": 1559584761115,
+ status: "draft",
+ id: "c1fbebd8-b22e-48e4-a4bc-2238295be273",
+ "content-id": "c1fbebd8-b22e-48e4-a4bc-2238295be273",
+ version: 4,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2018-12/c19f080d-024d-4080-8bb0-13a2d85242a2/rose_blue_flower_rose_blooms_67636.jpeg",
+ url: null,
+ attribution:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestatt",
+ caption:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontesta",
+ metadata: {
+ width: 528,
+ height: 350,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/8295725e-fc67-45ad-8a97-5df01700b9c5",
+ type: "title",
+ "family-id": "8c7bc2d4-f2dd-474a-b512-4bd4d5468922",
+ title: "",
+ id: "8295725e-fc67-45ad-8a97-5df01700b9c5",
+ metadata: {},
+ subtype: null,
+ text: "hello i am second card ",
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/27475841-6f66-49c8-a90a-1f03cd4b96a6",
+ type: "text",
+ "family-id": "eb7f0486-93f7-4264-8bc2-1412293972e7",
+ title: "",
+ id: "27475841-6f66-49c8-a90a-1f03cd4b96a6",
+ metadata: {},
+ subtype: null,
+ text: "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.
",
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/d6aaf29f-5009-4855-af3f-97db32afd742",
+ type: "text",
+ "family-id": "5b8cf546-9619-4aa1-8e50-c3cd7f4edcb2",
+ title: "",
+ id: "d6aaf29f-5009-4855-af3f-97db32afd742",
+ metadata: {},
+ subtype: "summary",
+ text: "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.
",
+ },
+ ],
+ "card-updated-at": 1559584934935,
+ "content-version-id": "879d8715-b71a-439c-a222-cb568e0a9870",
+ "card-added-at": 1559584761115,
+ status: "draft",
+ id: "677afe0a-76af-4d72-825b-3aa815e912f3",
+ "content-id": "677afe0a-76af-4d72-825b-3aa815e912f3",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2018-12/c19f080d-024d-4080-8bb0-13a2d85242a2/rose_blue_flower_rose_blooms_67636.jpeg",
+ url: null,
+ attribution:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestatt",
+ caption:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontesta",
+ metadata: {
+ width: 528,
+ height: 350,
+ },
+ },
+ },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/52f3c740-4060-4151-962a-fbe6cba30e4f",
+ type: "title",
+ "family-id": "4bb2a1d1-2f09-40f9-a872-f2459b56932f",
+ title: "",
+ id: "52f3c740-4060-4151-962a-fbe6cba30e4f",
+ metadata: {},
+ subtype: null,
+ text: "hello i am third card ",
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/6e3384be-dd15-4e1d-b4cc-2c6c9de35750",
+ type: "text",
+ "family-id": "8f1fd420-05a4-4a31-ab3f-57a4db8d40e2",
+ title: "",
+ id: "6e3384be-dd15-4e1d-b4cc-2c6c9de35750",
+ metadata: {
+ content:
+ "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.",
+ attribution: "someone",
+ },
+ subtype: "blockquote",
+ text: 'Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly. someone ',
+ },
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/f62b1a72-ee36-4eed-a730-e484d82e3678",
+ type: "text",
+ "family-id": "a6c12069-9003-46c4-8aa9-15997232c48e",
+ title: "",
+ id: "f62b1a72-ee36-4eed-a730-e484d82e3678",
+ metadata: {},
+ subtype: null,
+ text: "Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.Digital publishers often face margin squeeze on ads, and struggle with unproven revenue models, audience leakage to competition and rapidly changing technology. Overcome these problems effortlessly.
",
+ },
+ ],
+ "card-updated-at": 1559584934935,
+ "content-version-id": "d65bf540-af25-47ab-b438-2cad006de1ac",
+ "card-added-at": 1559584761115,
+ status: "draft",
+ id: "427b4e00-f182-4fc9-8e7e-ca10a56b5e9f",
+ "content-id": "427b4e00-f182-4fc9-8e7e-ca10a56b5e9f",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2018-12/c19f080d-024d-4080-8bb0-13a2d85242a2/rose_blue_flower_rose_blooms_67636.jpeg",
+ url: null,
+ attribution:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestatt",
+ caption:
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontesta",
+ metadata: {
+ width: 528,
+ height: 350,
+ },
+ },
+ },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/b50cd4c2-8e64-40f1-8825-05888179be4f",
+ type: "title",
+ "family-id": "9c4762e0-41f1-45c2-8a6b-1f49b16adaa3",
+ title: "",
+ id: "b50cd4c2-8e64-40f1-8825-05888179be4f",
+ metadata: {},
+ subtype: null,
+ text: "This is 4th card",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "mime-type": "image/jpeg",
+ width: 276,
+ height: 182,
+ },
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/2a6ccb57-a1ee-4133-aae9-b6781e6f5c2c",
+ type: "image",
+ "family-id": "16589660-75c6-4fc7-8e73-61de136d04b9",
+ "image-attribution": "",
+ title: "",
+ id: "2a6ccb57-a1ee-4133-aae9-b6781e6f5c2c",
+ "image-s3-key": "ace/2019-02/45734b07-c503-4e8a-b7f3-017aed5ba1d7/images.jpeg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ ],
+ "card-updated-at": 1561458187551,
+ "content-version-id": "1380eb36-9c21-4879-be8c-3e6e3ed8d6da",
+ "card-added-at": 1561458183510,
+ status: "draft",
+ id: "301503ba-6962-4e12-9811-e3981e787813",
+ "content-id": "301503ba-6962-4e12-9811-e3981e787813",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2019-02/45734b07-c503-4e8a-b7f3-017aed5ba1d7/images.jpeg",
+ url: null,
+ attribution: "",
+ caption: null,
+ metadata: {
+ "mime-type": "image/jpeg",
+ width: 276,
+ height: 182,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/5d399d4b-4194-4deb-b2df-97d8b49d3323",
+ type: "title",
+ "family-id": "d51bfda1-54c6-4da2-a23d-e4451e5f0c05",
+ title: "",
+ id: "5d399d4b-4194-4deb-b2df-97d8b49d3323",
+ metadata: {},
+ subtype: null,
+ text: "This is fifth card",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ width: 1024,
+ "mime-type": "image/jpeg",
+ height: 768,
+ },
+ "page-url": "/story/8ed73f9f-ac4e-453b-8508-1b5fbd99e71d/element/717d2b94-43dd-42a0-af5c-840c8d4e22fb",
+ type: "image",
+ "family-id": "f17adad4-8762-4f98-905e-130ceacb2711",
+ "image-attribution": "",
+ title: "",
+ id: "717d2b94-43dd-42a0-af5c-840c8d4e22fb",
+ "image-s3-key": "ace/2019-02/45b42adb-8eb6-4f96-8fde-2138e319a39f/jpeg_43.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1561458187551,
+ "content-version-id": "f9574387-5fbe-4c45-b918-86f2d72736e9",
+ "card-added-at": 1561458183510,
+ status: "draft",
+ id: "88cbfa23-98be-46b6-a47a-168399a45e76",
+ "content-id": "88cbfa23-98be-46b6-a47a-168399a45e76",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "listicle with bullets.",
+ message: null,
+ image: {
+ key: "ace/2019-02/45b42adb-8eb6-4f96-8fde-2138e319a39f/jpeg_43.jpg",
+ url: null,
+ attribution: "",
+ caption: null,
+ metadata: {
+ width: 1024,
+ "mime-type": "image/jpeg",
+ height: 768,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/lifestyle/listicle-with-reverse-count",
+ "story-version-id": "de2bbceb-432e-4c1b-ad47-3ee517cd3c61",
+ "content-type": "story",
+ "content-updated-at": 1571032559605,
+ "author-id": 187245,
+ "owner-id": 187245,
+ "linked-story-ids": [],
+ access: "subscription",
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1559584940824,
+ "hero-image-caption":
+ "imagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontestattributionimagecaptiontesta",
+ version: 21,
+ "story-template": "listicle",
+ "sequence-no": null,
+ "created-at": 1571032554310,
+ authors: [
+ {
+ id: 187245,
+ name: "Shreya Shukla",
+ slug: "shreya-shukla-2",
+ "avatar-url":
+ "https://lh6.googleusercontent.com/-pBdJGAfN81c/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfmrjS-gWdYzH3Gtmuib3KbVLdixw/photo.jpg",
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Shreya Shukla",
+ },
+ },
+ {
+ id: "f8803324-6cdf-434c-b9a7-b89cc5309ed7",
+ "associated-metadata": {},
+ type: "story",
+ story: {
+ "updated-at": 1571034606062,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 331449,
+ "author-name": "Ekta Patnaik",
+ tags: [],
+ headline: "live blog test hero image",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "f8803324-6cdf-434c-b9a7-b89cc5309ed7",
+ slug: "elections-2018/live-blog-test-hero-image",
+ "last-published-at": 1571034606123,
+ subheadline: "subtile test for cat",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "elections-2018",
+ name: "Elections 2018",
+ "section-url": "https://ace-web.qtstage.io/elections-2018",
+ id: 18454,
+ "parent-id": null,
+ "display-name": "Elections 2018",
+ collection: {
+ slug: "elections-2018",
+ name: "Elections 2018",
+ id: 23928,
+ },
+ data: {
+ color: "#7b4f4f",
+ },
+ },
+ ],
+ "story-audio": {
+ "s3-key": null,
+ },
+ "access-level-value": null,
+ "content-created-at": 1569328762732,
+ "owner-name": "Ekta Patnaik",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 586,
+ height: 781,
+ },
+ comments: null,
+ entities: {},
+ "published-at": 1571034606123,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": null,
+ "bullet-type": "123",
+ id: "f8803324-6cdf-434c-b9a7-b89cc5309ed7",
+ "hero-image-s3-key": "ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ contributors: [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "image-url":
+ "https://thumbor-stg.assettype.com/ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ "member-id": 94985,
+ "image-metadata": {
+ width: 586,
+ height: 781,
+ },
+ "uploaded-at": 1567659556059,
+ "page-url": "/story/f8803324-6cdf-434c-b9a7-b89cc5309ed7/element/2e9023af-9273-4f0b-ba9c-3e769fc8e3a0",
+ type: "image",
+ "family-id": "15f35589-5213-4f3b-8c6e-6c61ad9fd1d4",
+ "image-attribution": "",
+ title: "",
+ id: "2e9023af-9273-4f0b-ba9c-3e769fc8e3a0",
+ "image-s3-key": "ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1569328777159,
+ "content-version-id": "1793fbc8-a4eb-4681-8688-538a34206d0f",
+ "card-added-at": 1569328762738,
+ status: "draft",
+ id: "0524a15f-e668-4ec4-a66e-811f1ef87d6b",
+ "content-id": "0524a15f-e668-4ec4-a66e-811f1ef87d6b",
+ version: 3,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "live blog test hero image",
+ message: null,
+ image: {
+ key: "ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ attribution: "",
+ caption: null,
+ metadata: {
+ width: 586,
+ height: 781,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/elections-2018/live-blog-test-hero-image",
+ "story-version-id": "a97d774a-eca5-4fab-8fb8-722c11dafa21",
+ "content-type": "story",
+ "content-updated-at": 1571034606198,
+ "author-id": 331449,
+ "owner-id": 331449,
+ "linked-story-ids": [],
+ access: null,
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1569328777608,
+ "hero-image-caption": null,
+ version: 5,
+ "story-template": "live-blog",
+ "sequence-no": null,
+ "created-at": 1571034606062,
+ authors: [
+ {
+ id: 331449,
+ name: "Ekta Patnaik",
+ slug: "ekta-patnaik",
+ "avatar-url":
+ "https://lh4.googleusercontent.com/-dsCFV4xALkk/AAAAAAAAAAI/AAAAAAAAAA8/UrIF-fDvZYU/photo.jpg",
+ "avatar-s3-key": "ace/2019-07/8c418178-ede3-4e8e-995d-9b703cb589e7/IMG_20190502_000217.jpg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "hero-image-url":
+ "https://thumbor-stg.assettype.com/ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ "publish-at": null,
+ "assignee-name": "Ekta Patnaik",
+ },
+ },
+ ],
+ "created-at": 1571041151571,
+ metadata: {
+ "cover-image": {
+ "cover-image-s3-key": "ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ "cover-image-metadata": {
+ width: 586,
+ height: 781,
+ "focus-point": [293, 391],
+ },
+ caption: "",
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2019-09/8c6a7163-a978-42ef-8796-ef10d47a6f22/Taz_332.jpg",
+ },
+ },
+};
diff --git a/app/isomorphic/arrow/components/Fixture/dummyStory.js b/app/isomorphic/arrow/components/Fixture/dummyStory.js
new file mode 100644
index 000000000..1a29af88b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Fixture/dummyStory.js
@@ -0,0 +1,2188 @@
+function createUUID() {
+ var dt = new Date().getTime();
+ var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
+ var r = (dt + Math.random() * 16) % 16 | 0;
+ dt = Math.floor(dt / 16);
+ return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
+ });
+ return uuid;
+}
+
+export const dummyStory = {
+ access: "subscription",
+ "updated-at": 1564650013782,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 123981,
+ "author-name": "Ravigopal Kesari",
+ tags: [
+ {
+ id: 163455,
+ name: "politics",
+ "meta-description": null,
+ slug: "politics",
+ "tag-type": "Tag",
+ },
+ {
+ id: 169510,
+ name: "review",
+ "meta-description": null,
+ slug: "review",
+ "tag-type": "Tag",
+ },
+ ],
+ customSlotAfterStory: {
+ config: { targetingId: "" },
+ layout: "Leaderboard",
+ layoutLabel: "Leaderboard",
+ type: "ad",
+ },
+ headline: "Ready Player One review \u2013 Spielberg\u00A0",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "a3561065-11ce-4281-9d86-325934aa2146",
+ slug: "recent-stories/news/ready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ "linked-stories": {
+ "2d0008f7-768f-4667-822f-cb531d9627f4": {
+ "author-name": "Feed Migrator",
+ headline: "How is the coronavirus impacting people with disabilities?",
+ "story-content-id": "2d0008f7-768f-4667-822f-cb531d9627f4",
+ slug: "coronavirus/how-is-the-coronavirus-impacting-people-with-disabilities",
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "coronavirus",
+ name: "Coronavirus",
+ "section-url": "https://ace-web.qtstage.io/anything/coronavirus",
+ id: 42239,
+ "parent-id": null,
+ "display-name": "Coronavirus",
+ collection: {
+ slug: "coronavirus",
+ name: "Coronavirus",
+ id: 92836,
+ },
+ data: null,
+ },
+ ],
+ "hero-image-metadata": {
+ "original-url": "https://www.aljazeera.com/mritems/Images/2020/4/19/f90b71cdf10141ff9913b72bcfd29768_18.jpg",
+ },
+ "hero-image-s3-key": "ace/2020-04/9adec2ac-d37c-496f-abcc-98309a4bb356/f90b71cdf10141ff9913b72bcfd29768_18.jpg",
+ url: "https://ace-web.qtstage.io/anything/coronavirus/how-is-the-coronavirus-impacting-people-with-disabilities",
+ "content-updated-at": 1587491154824,
+ "author-id": 934395,
+ "first-published-at": 1587405627183,
+ authors: [
+ {
+ id: 934395,
+ name: "Feed Migrator",
+ slug: "feed-migrator",
+ "avatar-url": null,
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ },
+ ],
+ },
+ },
+ "last-published-at": 1564650017054,
+ subheadline:
+ "This spectacular gaming ride whizzes through a limitless futurescape – while also taking a puzzling detour to the shiny",
+ alternative: {
+ home: {
+ default: {
+ headline: "Movie Review: Why Ready Player One is Amazing, immerses viewers ",
+ "hero-image": {
+ "hero-image-metadata": {
+ width: 1080,
+ height: 1330,
+ "focus-point": [525, 207],
+ },
+ "hero-image-attribution": "attribution",
+ "hero-image-s3-key":
+ "ace/2018-01/7f028fc9-48fa-4e6c-b2b3-8baf1e6e8928/22520141_862918130543643_474831853139210771_o.jpg",
+ "hero-image-caption": "",
+ "hero-image-url":
+ "https://thumbor-stg.assettype.com/ace/2018-01/7f028fc9-48fa-4e6c-b2b3-8baf1e6e8928/22520141_862918130543643_474831853139210771_o.jpg",
+ "temporary-hero-image-key": null,
+ },
+ },
+ },
+ },
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "news",
+ name: "News",
+ "section-url": "https://ace-web.qtstage.io/section/news",
+ id: 5670,
+ "parent-id": 5773,
+ "display-name": "News ",
+ collection: {
+ slug: "news",
+ name: "News",
+ id: 3795,
+ },
+ data: null,
+ },
+ {
+ "domain-slug": null,
+ slug: "recent-stories",
+ name: "Recent stories",
+ "section-url": "https://ace-web.qtstage.io/anything/recent-stories",
+ id: 5773,
+ "parent-id": null,
+ "display-name": "Recent stories",
+ collection: {
+ slug: "recent",
+ name: "Recent stories",
+ id: 2760,
+ },
+ data: null,
+ },
+ {
+ "domain-slug": null,
+ slug: "film",
+ name: "Film",
+ "section-url": "https://ace-web.qtstage.io/section/film",
+ id: 5749,
+ "parent-id": 5670,
+ "display-name": "Film Display name",
+ collection: {
+ slug: "film-manual-collection",
+ name: "Film",
+ id: 2725,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key": "story-audio/ace/2019-08/a3561065-11ce-4281-9d86-325934aa2146/12132634-d337-471b-9e92-1ce7b4d15bfb.mp3",
+ },
+ "read-time": 2,
+ "access-level-value": 23,
+ "content-created-at": 1524115986915,
+ "owner-name": "Ravigopal Kesari",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-hyperlink": "https://www.google.com",
+ "hero-image-metadata": {
+ width: 1920,
+ height: 1080,
+ "mime-type": "image/png",
+ "file-size": 587085,
+ "file-name": "ready-player-one-hd-wallpapers-70749-6537851.png",
+ "focus-point": [883, 411],
+ },
+ comments: null,
+ "word-count": 323,
+ entities: {},
+ "published-at": 1564650017054,
+ "is-live-blog": false,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary:
+ "Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When writing modules, encapsulation is a virtue, so Passport delegates all other functionality to the application. This separation of concerns keeps code clean and maintainable, and makes Passport extremely easy to integrate into an application.",
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ "is-amp-supported": false,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "Quis Nostrud",
+ "bullet-type": "123",
+ id: createUUID(),
+ "hero-image-s3-key":
+ "ace/2018-01/7f028fc9-48fa-4e6c-b2b3-8baf1e6e8928/22520141_862918130543643_474831853139210771_o.jpg",
+ contributors: [],
+ "associated-series-collection-ids": [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/49ed61fb-647e-4d23-9097-3c5088cc3fa7/element/d08cb2d1-0316-4af3-ad93-192824111745",
+ type: "text",
+ "family-id": "7e42ab53-fb73-49e9-b0fe-51a889cd3f97",
+ title: "",
+ id: "d08cb2d1-0316-4af3-ad93-192824111745",
+ metadata: {
+ "cta-title": "CTA text button",
+ "cta-url": "https://www.google.com",
+ "open-in-new-tab": true,
+ "no-follow": true,
+ },
+ subtype: "cta",
+ text: 'CTA text button ',
+ },
+ {
+ description: "",
+ "embed-js":
+ "PGlmcmFtZSBmcmFtZWJvcmRlcj0iMCIgd2lkdGg9IjY0MCIgaGVpZ2h0PSIzNjAiIHNyYz0iaHR0cHM6Ly93d3cuZGFpbHltb3Rpb24uY29tL2VtYmVkL3ZpZGVvL3hlZzRjNCIgYWxsb3dmdWxsc2NyZWVuIGFsbG93PSJhdXRvcGxheSI+PC9pZnJhbWU+",
+ "page-url": "/story/6045077d-e166-4423-9505-fd52c8180429/element/2528c79d-9f8a-400c-970f-14685bb547ab",
+ type: "jsembed",
+ "family-id": "6aee389b-6d0f-4de2-9a7d-07d36c12ac13",
+ title: "",
+ id: "2528c79d-9f8a-400c-970f-14685bb547ab",
+ metadata: {
+ "video-id": "xeg4c4",
+ "dailymotion-url": "https://www.dailymotion.com/video/xeg4c4",
+ provider: "dailymotion-video",
+ },
+ subtype: "dailymotion-video",
+ },
+ {
+ description: "",
+ "page-url": "/story/7155b5c2-80a4-4922-bdf1-73f1ff04311e/element/2f648c29-ef8f-442c-903a-263f82e631dd",
+ type: "file",
+ "family-id": "95bf1052-d663-446d-ace7-d014325e0027",
+ title: "",
+ id: "2f648c29-ef8f-442c-903a-263f82e631dd",
+ "file-name": "document__7_ (1).docx",
+ url: "https://thumbor-stg.assettype.com/ace/2019-08/21f3a19b-1d98-46bc-95e3-a5bdc9045ae9/document__7___1_.docx",
+ "s3-key": "ace/2019-08/21f3a19b-1d98-46bc-95e3-a5bdc9045ae9/document__7___1_.docx",
+ "content-type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ metadata: {
+ "file-size": 8209,
+ },
+ subtype: "attachment",
+ },
+ {
+ description: "",
+ "page-url": "/story/a9068be5-70ce-4d55-86d0-687546f921ea/element/62393c55-50ed-4310-81c2-01cc0ef17446",
+ type: "file",
+ "family-id": "591f90c3-e98e-43dc-8509-9f11f5335af6",
+ title: "",
+ id: "62393c55-50ed-4310-81c2-01cc0ef17446",
+ "file-name": "resume-samples.pdf",
+ url: "https://thumbor-stg.assettype.com/ace/2019-07/6dcf2021-615b-43e6-85f3-21acb8953cea/resume_samples.pdf",
+ "s3-key": "ace/2019-07/6dcf2021-615b-43e6-85f3-21acb8953cea/resume_samples.pdf",
+ "content-type": "application/pdf",
+ metadata: {
+ "file-size": 301808,
+ },
+ subtype: "attachment",
+ },
+ {
+ "page-url": "/story/ed8a181b-4750-48b9-967e-83f29416ee2a/element/e2478f89-edab-44c8-a7df-6c6c84399c82",
+ type: "text",
+ "family-id": "481b06ad-edd4-45c1-b8bf-7996dfe0667e",
+ title: "",
+ id: "e2478f89-edab-44c8-a7df-6c6c84399c82",
+ metadata: {
+ content: "The human eye can distinguish around 10 million different colors!",
+ attribution:
+ "Color processing begins at a very early level in the visual system (even within the retina) through initial color opponent mechanisms.",
+ },
+ subtype: "bigfact",
+ text: 'The human eye can distinguish around 10 million different colors!
Color processing begins at a very early level in the visual system (even within the retina) through initial color opponent mechanisms.
',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/d42210d4-4c37-475c-ac6e-414821ad2e57",
+ type: "youtube-video",
+ "family-id": "43f1bf4c-2144-4f86-b36e-d910ff76eb8d",
+ title: "",
+ id: "d42210d4-4c37-475c-ac6e-414821ad2e57",
+ url: "https://www.youtube.com/watch?v=AST2-4db4ic&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/AST2-4db4ic",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ description: "",
+ "embed-js":
+ "PGlmcmFtZSBmcmFtZWJvcmRlcj0iMCIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyNzAiIHNyYz0iaHR0cHM6Ly93d3cuZGFpbHltb3Rpb24uY29tL2VtYmVkL3ZpZGVvL3g1dmxhOGwiIGFsbG93ZnVsbHNjcmVlbiBhbGxvdz0iYXV0b3BsYXkiPjwvaWZyYW1lPg==",
+ "page-url": "/story/d52a62c4-bb41-48c8-abd2-6f063c680506/element/c0e49d11-bd5c-4313-a3c8-e276cdf86e9a",
+ type: "jsembed",
+ "family-id": "94b2feaf-aed2-4e31-b450-5d02b0396d26",
+ title: "",
+ id: "c0e49d11-bd5c-4313-a3c8-e276cdf86e9a",
+ metadata: {
+ "dailymotion-url": "https://www.dailymotion.com/video/x5vla8l",
+ provider: "dailymotion-video",
+ "video-id": "x5vla8l",
+ },
+ subtype: "dailymotion-video",
+ },
+ {
+ description: "",
+ "embed-js":
+ "PGJsb2NrcXVvdGUgY2xhc3M9InR3aXR0ZXItdHdlZXQiPjxwIGxhbmc9ImVuIiBkaXI9Imx0ciI+T3VyIHB1cHBpZXMganVzdCBoYWQgdGhlIGJlc3QuIGRheS4gZXZlci48YnI+PGJyPlRoZXkgZ290IHRvIGV4cGxvcmUgdGhlIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vR2VvcmdpYUFxdWFyaXVtP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkBHZW9yZ2lhQXF1YXJpdW08L2E+IHdoaWxlIGl0IGlzIGNsb3NlZCB0byB0aGUgcHVibGljLiBUaGV5IG1hZGUgYWxsIHNvcnRzIG9mIGV4Y2l0aW5nIGRpc2NvdmVyaWVzIGFuZCBsb3RzIG9mIG5ldyBmcmllbmRzISA8YSBocmVmPSJodHRwczovL3QuY28vZjBpSFhmcTNBRiI+cGljLnR3aXR0ZXIuY29tL2YwaUhYZnEzQUY8L2E+PC9wPiZtZGFzaDsgQXRsYW50YSBIdW1hbmUgKEBhdGxhbnRhaHVtYW5lKSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2F0bGFudGFodW1hbmUvc3RhdHVzLzEyNDMyMTg4MzI1ODE0NDc2ODM/cmVmX3NyYz10d3NyYyU1RXRmdyI+TWFyY2ggMjYsIDIwMjA8L2E+PC9ibG9ja3F1b3RlPgo8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+Cg==",
+ "page-url": "/story/1cd537ed-a30f-44c4-ab47-9037364df994/element/92b1c15c-1a3c-40eb-92cb-b5e1f0170c85",
+ type: "jsembed",
+ "family-id": "295bcd70-a9b8-453d-8f7f-b7a577380db8",
+ title: "",
+ id: "92b1c15c-1a3c-40eb-92cb-b5e1f0170c85",
+ metadata: {
+ "tweet-url": "https://twitter.com/atlantahumane/status/1243218832581447683",
+ provider: "twitter",
+ "tweet-id": "1243218832581447683",
+ },
+ subtype: "tweet",
+ },
+ {
+ description: "",
+ "embed-js":
+ "PGJsb2NrcXVvdGUgY2xhc3M9InR3aXR0ZXItdHdlZXQiPjxwIGxhbmc9ImVuIiBkaXI9Imx0ciI+Qm90aCA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL1NoYXNoaVRoYXJvb3I/cmVmX3NyYz10d3NyYyU1RXRmdyI+QFNoYXNoaVRoYXJvb3I8L2E+IGFuZCBvdXIgUE0gPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9uYXJlbmRyYW1vZGk/cmVmX3NyYz10d3NyYyU1RXRmdyI+QG5hcmVuZHJhbW9kaTwvYT4gaGF2ZSBzb21ldGhpbmcgaW4gY29tbW9uLiBPbmUgc3BlYWtzIGluIHN1Y2ggaGlnaCBFbmdsaXNoIGFuZCBvbmUgc3BlYWtzIG9uIHN1Y2ggSGlnaCBIaW5kaSB0aGF0IHZlcnkgZmV3IHBlcmNlbnRhZ2Ugb2YgdGhlIHBvcHVsYXRpb24gdW5kZXJzdGFuZCBpdC4g8J+YgPCfmIk8L3A+Jm1kYXNoOyBGYXJhaCBLaGFuIChARmFyYWhLaGFuQWxpKSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL0ZhcmFoS2hhbkFsaS9zdGF0dXMvMTI2MDI0MjI0NTU3NzUxMDkxNj9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5NYXkgMTIsIDIwMjA8L2E+PC9ibG9ja3F1b3RlPiA8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+",
+ "page-url": "/story/403f862a-703a-4c99-9930-b79a5dfb1775/element/3ea4a656-e20b-4ed3-ba8f-58874b5e8715",
+ type: "jsembed",
+ "family-id": "b17777d5-73ac-4521-a8c1-7468667d89e9",
+ title: "",
+ id: "3ea4a656-e20b-4ed3-ba8f-58874b5e8715",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/c529557b-a4f5-4363-ba3f-7ac0eb14f299",
+ type: "text",
+ "family-id": "2b7c81f0-98f6-416c-9692-90ba504c9385",
+ title: "",
+ id: "c529557b-a4f5-4363-ba3f-7ac0eb14f299",
+ metadata: {},
+ subtype: null,
+ text: 'Virtual reality is the air guitar solo of modern cinema: a frenetic imagined activity in a made-up world that exists one level below the already made-up world of the story. Steven Spielberg 2019s Ready Player One .
',
+ },
+ {
+ description: "",
+ "image-metadata": {
+ width: 6016,
+ height: 4016,
+ "mime-type": "image/jpeg",
+ "focus-point": [2741, 1930],
+ },
+ "page-url": "/story/560b39d9-ae5b-44ef-acc7-04db4472b4f8/element/6c6cf4a8-6700-4829-8076-88373a1436b1",
+ type: "image",
+ "family-id": "286afa67-910e-45aa-a795-d13508726663",
+ "image-attribution": "Quintype Media Gallery",
+ title: "Nature as it's best",
+ id: "6c6cf4a8-6700-4829-8076-88373a1436b1",
+ "image-s3-key": "demo/2018-11/b1fa99ff-b05d-464b-97b9-83cd8f2bf65d/2072.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/4abd5e1d-4f02-433a-af48-2733a63682b9",
+ type: "composite",
+ "family-id": "aa26b0a8-f7e2-4466-a1ee-75cfc54c69d1",
+ "story-elements": [
+ {
+ description: "",
+ title: "",
+ subtype: null,
+ metadata: {
+ description:
+ "In the year 2045, people can escape their harsh reality in the OASIS, an immersive virtual world where you can go anywhere, do anything, be anyone-the only limits are your own imagination. OASIS creator James Halliday left his immense fortune and control of the Oasis to the winner of a contest designed to find a worthy heir. When unlikely hero Wade Watts conquers the first challenge of the reality-bending treasure hunt, he and his friends-known as the High Five-are hurled into a fantastical universe of discovery and danger to save the OASIS and their world.",
+ url: "https://www.rottentomatoes.com/m/ready_player_one/",
+ name: "Ready player One review from rotten tomatoes",
+ },
+ text: '',
+ id: "47afb0c6-aa50-446c-8e55-c10c604c5246",
+ "family-id": "6b236f36-ce8a-4f9a-b70e-844367f13af0",
+ type: "text",
+ },
+ ],
+ title: "",
+ id: "4abd5e1d-4f02-433a-af48-2733a63682b9",
+ metadata: {},
+ subtype: "references",
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/80f39709-fdf6-40b4-9d8c-a2db549ce621",
+ type: "text",
+ "family-id": "338160c1-cb62-4328-b866-abbb13cf0e1b",
+ title: "",
+ id: "80f39709-fdf6-40b4-9d8c-a2db549ce621",
+ metadata: {
+ content:
+ "After the death of James Halliday, the creator of the virtual reality world, his pre-recorded message reveals the hidden fortune, which makes Wade Watts, a teenager, embark on a quest.",
+ attribution: "Player",
+ },
+ subtype: "quote",
+ text: 'After the death of James Halliday, the creator of the virtual reality world, his pre-recorded message reveals the hidden fortune, which makes Wade Watts, a teenager, embark on a quest. Player ',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/b474c23d-d782-49f1-a615-fa03081c46b1",
+ type: "text",
+ "family-id": "77f6332d-73fb-4255-91bb-e2d714981c82",
+ title: "",
+ id: "b474c23d-d782-49f1-a615-fa03081c46b1",
+ metadata: {
+ question:
+ 'Is Ready Player One book better than the movie?
',
+ answer:
+ "Ready Player One is the latest example. I read Earnest Cline's book a couple months before watching Steven Spielberg's movie . ... The book generally fares better with reviewers, averaging 4.6 out of 5 stars on Amazon, while the movie scores 73% on Rotten Tomatoes.
",
+ interviewer: {
+ "author-collection-id": null,
+ "updated-at": 1565606538341,
+ slug: "reena-singh-2",
+ "last-name": null,
+ "publisher-id": 97,
+ name: "Reena Singh",
+ "avatar-url":
+ "https://thumbor-stg.assettype.com/ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ settings: null,
+ source: null,
+ "first-name": null,
+ "communication-email": null,
+ bio: null,
+ id: 300199,
+ "avatar-s3-key": "ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ "twitter-handle": null,
+ "created-at": 1551268188983,
+ metadata: {},
+ },
+ interviewee: {
+ "author-collection-id": null,
+ "updated-at": 1565606538341,
+ slug: "reena-singh-2",
+ "last-name": null,
+ "publisher-id": 97,
+ name: "Reena Singh",
+ "avatar-url":
+ "https://thumbor-stg.assettype.com/ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ settings: null,
+ source: null,
+ "first-name": null,
+ "communication-email": null,
+ bio: null,
+ id: 300199,
+ "avatar-s3-key": "ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ "twitter-handle": null,
+ "created-at": 1551268188983,
+ metadata: {},
+ },
+ },
+ subtype: "q-and-a",
+ text: 'Is Ready Player One book better than the movie?
Ready Player One is the latest example. I read Earnest Cline\'s book a couple months before watching Steven Spielberg\'s movie . ... The book generally fares better with reviewers, averaging 4.6 out of 5 stars on Amazon, while the movie scores 73% on Rotten Tomatoes.
',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/ff83a8d3-1946-4cb3-b52c-135108e54ebd",
+ type: "text",
+ "family-id": "fb690ed4-60b6-4a46-89f9-ee6f87bfa4c7",
+ title: "",
+ id: "ff83a8d3-1946-4cb3-b52c-135108e54ebd",
+ metadata: {
+ interviewer: {
+ "author-collection-id": null,
+ "updated-at": 1565606538341,
+ slug: "reena-singh-2",
+ "last-name": null,
+ "publisher-id": 97,
+ name: "Reena Singh",
+ "avatar-url":
+ "https://thumbor-stg.assettype.com/ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ settings: null,
+ source: null,
+ "first-name": null,
+ "communication-email": null,
+ bio: null,
+ id: 300199,
+ "avatar-s3-key": "ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ "twitter-handle": null,
+ "created-at": 1551268188983,
+ metadata: {},
+ },
+ },
+ subtype: "question",
+ text: 'Is Ready Player One book better than the movie?
',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/ac5e114b-5144-401b-9d60-6e706f708a87",
+ type: "text",
+ "family-id": "b857e675-9923-47b3-bb2f-a467bbc5977d",
+ title: "",
+ id: "ac5e114b-5144-401b-9d60-6e706f708a87",
+ metadata: {
+ interviewee: {
+ "author-collection-id": null,
+ "updated-at": 1565606538341,
+ slug: "reena-singh-2",
+ "last-name": null,
+ "publisher-id": 97,
+ name: "Reena Singh",
+ "avatar-url":
+ "https://thumbor-stg.assettype.com/ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ settings: null,
+ source: null,
+ "first-name": null,
+ "communication-email": null,
+ bio: null,
+ id: 300199,
+ "avatar-s3-key": "ace/2019-08/565bba26-4f05-4e63-b619-511a2a2bd5b7/images.jpeg",
+ "twitter-handle": null,
+ "created-at": 1551268188983,
+ metadata: {},
+ },
+ },
+ subtype: "answer",
+ text: "Ready Player One is the latest example. I read Earnest Cline's book a couple months before watching Steven Spielberg's movie . ... The book generally fares better with reviewers, averaging 4.6 out of 5 stars on Amazon, while the movie scores 73% on Rotten Tomatoes.
",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/0e1e9777-e0e6-45f9-b212-264db2d8eeb5",
+ type: "text",
+ "family-id": "7be340e2-8e00-4bba-b640-cdbd57ba2719",
+ title: "",
+ id: "0e1e9777-e0e6-45f9-b212-264db2d8eeb5",
+ metadata: {
+ content:
+ "Although the many story changes might be hard for book purists to accept, Steven Spielberg has lovingly captured the zeitgeist of '80s nostalgia in this adventure.",
+ },
+ subtype: "blurb",
+ text: 'Although the many story changes might be hard for book purists to accept, Steven Spielberg has lovingly captured the zeitgeist of 80s nostalgia in this adventure. ',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/48546269-f61b-42f3-945c-5358d14f3429",
+ type: "text",
+ "family-id": "33c7c7c1-1001-46f1-b77d-0553805ded84",
+ title: "",
+ id: "48546269-f61b-42f3-945c-5358d14f3429",
+ metadata: {
+ content:
+ "After the death of James Halliday, the creator of the virtual reality world, his pre-recorded message reveals the hidden fortune, which makes Wade Watts, a teenager, embark on a quest.",
+ attribution: "Player",
+ },
+ subtype: "blockquote",
+ text: 'After the death of James Halliday, the creator of the virtual reality world, his pre-recorded message reveals the hidden fortune, which makes Wade Watts, a teenager, embark on a quest. Player ',
+ },
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/a0078928-ebb8-4464-a8e6-62e77ca7f502",
+ type: "text",
+ "family-id": "bc29c3e2-9b21-444b-9e42-359968da256a",
+ title: "",
+ id: "a0078928-ebb8-4464-a8e6-62e77ca7f502",
+ metadata: {
+ "linked-story-id": "2d0008f7-768f-4667-822f-cb531d9627f4",
+ "linked-story": {
+ headline: "How is the coronavirus impacting people with disabilities?",
+ "story-content-id": "2d0008f7-768f-4667-822f-cb531d9627f4",
+ "highlighted-text": "",
+ id: "2d0008f7-768f-4667-822f-cb531d9627f4",
+ "highlighted-headline": null,
+ },
+ },
+ subtype: "also-read",
+ text: "How is the coronavirus impacting people with disabilities?",
+ },
+ {
+ description: "",
+ "page-url": "/story/39b60649-aa6f-49f6-8365-0981b799b3b7/element/373d6e0b-c5ac-4904-8d41-78e4fd5602cd",
+ type: "text",
+ "family-id": "2b60d9a6-355e-45cb-b2a4-e29fde4a4827",
+ title: "",
+ id: "373d6e0b-c5ac-4904-8d41-78e4fd5602cd",
+ metadata: {},
+ subtype: "summary",
+ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries , but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
",
+ },
+ {
+ description: "",
+ "page-url": "/story/8f1c0e1f-afd6-4169-96c2-243d88013bd1/element/b35ef956-35ce-4431-a68d-018760a4ddda",
+ type: "composite",
+ "family-id": "97761d2a-de16-4135-945f-ac5706ecc1a6",
+ "story-elements": [
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.washingtonpost.com/resizer/_I2QC1BPsP-wxv7XSvBBK48bv8E=/1440x0/smart/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/OP2ZX3T6KYI6VBGCA6JNQWIZCE.jpg",
+ },
+ type: "image",
+ "family-id": "0444a13c-f52a-43ff-8564-b52411ffdefc",
+ "image-attribution": "",
+ title: "",
+ id: "738163f6-4796-4a44-8e7b-b0160a83fa42",
+ "image-s3-key": "ace/2020-04/b6b55f73-a713-4087-849b-a7dbb04826e6/OP2ZX3T6KYI6VBGCA6JNQWIZCE.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://media3.s-nbcnews.com/j/newscms/2020_16/3314681/200419-michelle-tom-cover-cs-509p_6155bfe91e67a4f93ecc2cf00530c404.nbcnews-fp-1200-630.jpg",
+ },
+ type: "image",
+ "family-id": "5173f4e7-f94c-4e7a-ba54-806ae1a7deef",
+ "image-attribution": "",
+ title: "",
+ id: "2486b7db-4d88-4fc3-899e-425c105f48ba",
+ "image-s3-key":
+ "ace/2020-04/e58b991e-4a82-4fca-818d-c1f8fd6e98e6/200419_michelle_tom_cover_cs_509p_6155bfe91e67a4f93ecc2cf00530c404_nbcnews_fp_1200_630.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url": "https://www.dw.com/image/53181437_304.jpg",
+ },
+ type: "image",
+ "family-id": "5f454472-b7ed-4a18-b923-452332c16125",
+ "image-attribution": "",
+ title: "",
+ id: "3dd778d9-dc96-4798-876f-069b8d47c177",
+ "image-s3-key": "ace/2020-04/ea98e4fb-f8a8-4b18-a2b0-8ec03b17820c/53181437_304.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.washingtonpost.com/resizer/_f5BO1VdLp-O-jX1x-NK-GfYknU=/1440x0/smart/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ },
+ type: "image",
+ "family-id": "67ae55bd-3f34-417b-8951-fb525da321f2",
+ "image-attribution": "",
+ title: "",
+ id: "bc8d79e8-04d5-447e-80b7-99e211b1d1ec",
+ "image-s3-key": "ace/2020-04/aef8d5e1-d2fc-4f77-968b-50ff0c4764ea/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.washingtonpost.com/resizer/_f5BO1VdLp-O-jX1x-NK-GfYknU=/1440x0/smart/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ },
+ type: "image",
+ "family-id": "67ae55bd-3f34-417b-8951-fb525da321f2",
+ "image-attribution": "",
+ title: "",
+ id: "bc8d79e8-04d5-447e-80b7-99e211b1d1ec",
+ "image-s3-key": "ace/2020-04/aef8d5e1-d2fc-4f77-968b-50ff0c4764ea/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.washingtonpost.com/resizer/_f5BO1VdLp-O-jX1x-NK-GfYknU=/1440x0/smart/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ },
+ type: "image",
+ "family-id": "67ae55bd-3f34-417b-8951-fb525da321f2",
+ "image-attribution": "",
+ title: "",
+ id: "bc8d79e8-04d5-447e-80b7-99e211b1d1ec",
+ "image-s3-key": "ace/2020-04/aef8d5e1-d2fc-4f77-968b-50ff0c4764ea/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.washingtonpost.com/resizer/_f5BO1VdLp-O-jX1x-NK-GfYknU=/1440x0/smart/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ },
+ type: "image",
+ "family-id": "67ae55bd-3f34-417b-8951-fb525da321f2",
+ "image-attribution": "",
+ title: "",
+ id: "bc8d79e8-04d5-447e-80b7-99e211b1d1ec",
+ "image-s3-key": "ace/2020-04/aef8d5e1-d2fc-4f77-968b-50ff0c4764ea/ZE2IQNUBVAI6VBGCA6JNQWIZCE.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://www.aljazeera.com/mritems/Images/2020/4/19/e69c7b29e024420d95e519b96edfc7aa_18.jpg",
+ },
+ type: "image",
+ "family-id": "096ff456-dd70-45f8-9953-4ac78c6c332c",
+ "image-attribution": "",
+ title: "",
+ id: "3a7139bd-ab4c-4c2e-bf0d-4e0ff6903e19",
+ "image-s3-key":
+ "ace/2020-04/7cebf55c-27c1-4e73-839f-85d1399a354e/e69c7b29e024420d95e519b96edfc7aa_18.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://media.khou.com/assets/KHOU/images/55ceb4cc-e4a7-4a99-bc29-34ed175c4df2/55ceb4cc-e4a7-4a99-bc29-34ed175c4df2_1140x641.jpg",
+ },
+ type: "image",
+ "family-id": "69f2fa84-f55b-4176-a48e-9cc0521b610d",
+ "image-attribution": "",
+ title: "",
+ id: "5b1501e0-9a91-4720-8660-531511bded39",
+ "image-s3-key":
+ "ace/2020-04/167805cc-a440-41fe-bccf-132660d4350f/55ceb4cc_e4a7_4a99_bc29_34ed175c4df2_1140x641.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ ],
+ title: "",
+ id: "b35ef956-35ce-4431-a68d-018760a4ddda",
+ metadata: {
+ type: "gallery",
+ },
+ subtype: "image-gallery",
+ },
+ ],
+ "card-updated-at": 1587559563683,
+ "content-version-id": "11beb6bf-bcb3-4ea2-8764-7355fc40811b",
+ "card-added-at": 1524115986932,
+ status: "draft",
+ id: "954331a6-4e5e-4d96-8204-74f56ce1f837",
+ "content-id": "954331a6-4e5e-4d96-8204-74f56ce1f837",
+ version: 32,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Ready Player One review – Spielberg ",
+ message: "Ready Player One review – Spielberg spins a dizzying VR yarn",
+ image: {
+ key: "ace/2019-08/e3103795-ebb4-4c6c-93f0-2e829037e008/ready_player_one_hd_wallpapers_70749_6537851.png",
+ url: null,
+ attribution: null,
+ caption: null,
+ metadata: {
+ width: 1920,
+ height: 1080,
+ "mime-type": "image/png",
+ "file-size": 587085,
+ "file-name": "ready-player-one-hd-wallpapers-70749-6537851.png",
+ "focus-point": [883, 411],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/a3561065-11ce-4281-9d86-325934aa2146/element/d42210d4-4c37-475c-ac6e-414821ad2e57",
+ type: "youtube-video",
+ "family-id": "43f1bf4c-2144-4f86-b36e-d910ff76eb8d",
+ title: "",
+ id: "d42210d4-4c37-475c-ac6e-414821ad2e57",
+ url: "https://www.youtube.com/watch?v=cSp1dM2Vj48&feature=youtu.be",
+ "embed-url": "https://www.youtube.com/embed/cSp1dM2Vj48",
+ metadata: {},
+ subtype: null,
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: 'Virtual reality is the air guitar solo of modern cinema: a frenetic imagined activity in a made-up world that exists one level below the already made-up world of the story. Steven Spielberg 2019s Ready Player One .
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1524116209278,
+ "content-version-id": "f1e7f3ea-21f2-4801-b6d7-2022ee079478",
+ "card-added-at": 1524116147733,
+ status: "draft",
+ id: "464e6388-3a0d-49ca-aea7-bce0893259ba",
+ "content-id": "464e6388-3a0d-49ca-aea7-bce0893259ba",
+ version: 3,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Ready Player One review – Spielberg ",
+ message: "Ready Player One review – Spielberg spins a dizzying VR yarn",
+ image: {
+ key: "ace/2019-08/e3103795-ebb4-4c6c-93f0-2e829037e008/ready_player_one_hd_wallpapers_70749_6537851.png",
+ url: null,
+ attribution: null,
+ caption: null,
+ metadata: {
+ width: 1920,
+ height: 1080,
+ "mime-type": "image/png",
+ "file-size": 587085,
+ "file-name": "ready-player-one-hd-wallpapers-70749-6537851.png",
+ "focus-point": [883, 411],
+ },
+ },
+ },
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/anything/recent-stories/news/ready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ "story-version-id": "28c7b47d-3b37-49f6-a0ba-2886d14182e4",
+ "content-type": "story",
+ "content-updated-at": 1564650017530,
+ "author-id": 123981,
+ "owner-id": 123981,
+ "linked-story-ids": [],
+ "promotional-message": 'text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1524116216298,
+ "hero-image-caption": "hero image caption",
+ version: 47,
+ "story-template": "review",
+ "sequence-no": null,
+ "created-at": 1564650011136,
+ authors: [
+ {
+ id: 94980,
+ name: "Stephen Wertheim",
+ social: {
+ twitter: {
+ url: "https://www.twitter.com/sabqorg",
+ handle: "elonmusk",
+ },
+ },
+ slug: "stephen-wertheim",
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ {
+ id: 123981,
+ name: "Ravigopal Kesari",
+ slug: "ravigopal-kesari",
+ social: {
+ twitter: {
+ url: "https://www.twitter.com/sabqorg",
+ handle: "elonmusk",
+ },
+ },
+ "avatar-url":
+ "https://lh5.googleusercontent.com/-NhNrHEp1w4M/AAAAAAAAAAI/AAAAAAAAAAs/lzYwVY1BQdQ/photo.jpg?sz=50",
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ bio: null,
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ },
+ ],
+ metadata: {
+ "review-title": "Ready Player One",
+ "review-rating": {
+ label: "1",
+ value: "1",
+ },
+ "sponsored-by": "quint",
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Ravigopal Kesari",
+};
+
+export const dummyPhotoStory = {
+ access: "subscription",
+ "updated-at": 1599115419318,
+ seo: {
+ "claim-reviews": {
+ story: null,
+ },
+ },
+ "assignee-id": 187245,
+ "author-name": "Shreya Shukla",
+ "hero-image-hyperlink": "https://www.google.com",
+ tags: [
+ {
+ id: 274973,
+ name: "50th Anniversary",
+ "meta-description": "5th Anniversary of quintype",
+ "meta-title": "5th Anniversary of qt",
+ slug: "50th-anniversary",
+ "tag-type": "Tag",
+ },
+ {
+ id: 1092771,
+ name: "Newzealand",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "newzealand",
+ "tag-type": "Tag",
+ },
+ {
+ id: 1292529,
+ name: "50",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "50",
+ "tag-type": "Tag",
+ },
+ {
+ id: 1292530,
+ name: "bangalore",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "bangalore",
+ "tag-type": "Tag",
+ },
+ ],
+ customSlotAfterStory: {
+ config: { targetingId: "" },
+ layout: "Leaderboard",
+ layoutLabel: "Leaderboard",
+ type: "ad",
+ },
+ headline: "Karnataka tourism",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "231f55c0-ba92-4da6-a2c0-697c520f860f",
+ slug: "child-sec/2020/09/02/karnataka-tourism",
+ "last-published-at": 1599115422319,
+ subheadline: "At vero eos et accusamus iusto odio dignissimos ducimus qui blanditiis pre.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "child-sec",
+ name: "child-sec",
+ "section-url": "https://ace-web.qtstage.io/parent-sec/child-sec",
+ id: 11825,
+ "parent-id": 11824,
+ "display-name": "child-sec",
+ collection: {
+ slug: "child-sec-parent-sec",
+ name: "child-sec (parent-sec)",
+ id: 17115,
+ },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key": "ace/story-audio/2020-09/231f55c0-ba92-4da6-a2c0-697c520f860f/.0fc573c1-97de-4644-8a26-bf65c4d6f0d9.mp3",
+ },
+ "read-time": 2,
+ "access-level-value": null,
+ "content-created-at": 1599069527074,
+ "owner-name": "Shreya Shukla",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ "original-url": "https://www.india.com/wp-content/uploads/2014/09/vikasa-soudha-245043_960_720.jpg",
+ width: 625,
+ height: 470,
+ },
+ comments: null,
+ "word-count": 245,
+ entities: {},
+ "published-at": 1599115422319,
+ "is-live-blog": false,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ "is-amp-supported": false,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "Credits: Quintype",
+ "bullet-type": "123",
+ id: "231f55c0-ba92-4da6-a2c0-697c520f860f",
+ "hero-image-s3-key": "ace/2020-06/58fb1855-a741-4b69-9c9b-57eafe34ff04/vikasa_soudha_245043_960_720.jpg",
+ contributors: [],
+ "associated-series-collection-ids": [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "image-metadata": {
+ width: 1920,
+ height: 1080,
+ "focus-point": [964, 507],
+ },
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/7798674b-46f4-429c-95ce-cb5d334277e4",
+ type: "image",
+ "family-id": "968c8fc8-874e-4982-8eca-9ac5c6ee93aa",
+ "image-attribution": "Supreme Court Justice Clarence Thomas in 2007. ",
+ title: "nature",
+ id: "7798674b-46f4-429c-95ce-cb5d334277e4",
+ "image-s3-key": "ace/2018-12/fcdb3782-886c-47ce-a84f-0ef5e051d639/Images_9.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/664c9d54-f0c2-4016-a80e-7c7ed1d64330",
+ type: "title",
+ "family-id": "d1941b1b-1b4f-4d35-8d2d-6d5c12fabc25",
+ title: "",
+ id: "664c9d54-f0c2-4016-a80e-7c7ed1d64330",
+ metadata: {},
+ subtype: null,
+ text: "Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/3bd855a8-9db1-404b-b193-6b4ec7a4c6f2",
+ type: "text",
+ "family-id": "7994eec8-b28a-47ee-b954-e652d7b829ad",
+ title: "",
+ id: "3bd855a8-9db1-404b-b193-6b4ec7a4c6f2",
+ metadata: {},
+ subtype: null,
+ text: "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
",
+ },
+ {
+ config: { targetingId: "" },
+ layout: "Leaderboard",
+ layoutLabel: "Leaderboard",
+ type: "ad",
+ },
+ ],
+ "card-updated-at": 1599070341951,
+ "content-version-id": "7160cfea-8a20-48a6-a67c-c5c1bf26a062",
+ "card-added-at": 1599069777782,
+ status: "draft",
+ id: "aaf2ca9d-af7d-4c26-9caf-a16f1e104c21",
+ "content-id": "aaf2ca9d-af7d-4c26-9caf-a16f1e104c21",
+ version: 11,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Karnataka tourism",
+ message: null,
+ image: {
+ key: "ace/2018-12/fcdb3782-886c-47ce-a84f-0ef5e051d639/Images_9.jpg",
+ url: null,
+ attribution: "Supreme Court Justice Clarence Thomas in 2007. ",
+ caption: null,
+ metadata: {
+ width: 1920,
+ height: 1080,
+ "focus-point": [964, 507],
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://s.rfi.fr/media/display/ddb13a2c-e9b1-11ea-9cf1-005056bf87d6/w:1280/p:16x9/f4da51e521694ef3b14820c2fd32709e691f058e.jpg",
+ width: 1280,
+ height: 720,
+ },
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/d33f9a51-4bc6-4205-8784-86cb11027115",
+ type: "image",
+ "family-id": "f7ff58e5-dbba-485c-bbcf-9119a764afd5",
+ "image-attribution": "Credits: Quintype",
+ title: "Supreme Court Justice Clarence Thomas in 2007. ",
+ id: "d33f9a51-4bc6-4205-8784-86cb11027115",
+ "image-s3-key":
+ "ace/2020-08/04562266-d044-4cf3-8ff3-58ab0b084d33/f4da51e521694ef3b14820c2fd32709e691f058e.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/530154d6-f1eb-4e12-8c87-6a49e8269cc6",
+ type: "text",
+ "family-id": "34bccdf3-115a-4179-b76e-e38742093c03",
+ title: "",
+ id: "530154d6-f1eb-4e12-8c87-6a49e8269cc6",
+ metadata: {},
+ subtype: "summary",
+ text: "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/19d75234-ba0a-47d1-b9d3-b79c936b5802",
+ type: "text",
+ "family-id": "25db509b-047b-44ae-adc4-608037504f9f",
+ title: "",
+ id: "19d75234-ba0a-47d1-b9d3-b79c936b5802",
+ metadata: {
+ content:
+ "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
+ attribution: "Quintype",
+ },
+ subtype: "quote",
+ text: 'Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Quintype ',
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/624c8857-5f74-4494-a494-f9b6345e90d1",
+ type: "text",
+ "family-id": "518c37ee-41e7-4a33-b7ba-e22f8bdebf02",
+ title: "",
+ id: "624c8857-5f74-4494-a494-f9b6345e90d1",
+ metadata: {
+ content:
+ "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
+ attribution: "Quintype",
+ },
+ subtype: "blockquote",
+ text: 'Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Quintype ',
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/8b61bb6b-695b-4022-aa41-ecfa42aa3b06",
+ type: "file",
+ "family-id": "debea3c3-2737-4e90-ae7a-a33ef3bbdc83",
+ title: "",
+ id: "8b61bb6b-695b-4022-aa41-ecfa42aa3b06",
+ "file-name": "Doker.pdf",
+ url: "https://thumbor-stg.assettype.com/ace/2020-09/d33ccb5b-a3c4-4376-8767-bedebe3e6f7c/Doker.pdf",
+ "s3-key": "ace/2020-09/d33ccb5b-a3c4-4376-8767-bedebe3e6f7c/Doker.pdf",
+ "content-type": "application/pdf",
+ metadata: {
+ "file-size": 28746,
+ },
+ subtype: "attachment",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/85b7b83e-d1ac-4775-8c3c-5c48107db183",
+ type: "text",
+ "family-id": "4dcb50c4-19da-4d2d-830c-110e01dd0b24",
+ title: "",
+ id: "85b7b83e-d1ac-4775-8c3c-5c48107db183",
+ metadata: {
+ question: "Grand Central Terminal, Park Avenue, New York is the world's
",
+ answer: "largest railway station
",
+ },
+ subtype: "q-and-a",
+ text: 'Grand Central Terminal, Park Avenue, New York is the world\'s
',
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/bb010ef2-8e67-4b72-8d4a-a525c7c4818c",
+ type: "text",
+ "family-id": "278b4f10-60de-483c-9035-4f9826bcad4c",
+ title: "",
+ id: "bb010ef2-8e67-4b72-8d4a-a525c7c4818c",
+ metadata: {
+ question: "For which of the following disciplines is Nobel Prize awarded?
",
+ answer: "Physics and Chemistry
Physiology or Medicine
Literature, Peace and Economics
",
+ },
+ subtype: "q-and-a",
+ text: 'For which of the following disciplines is Nobel Prize awarded?
Physics and Chemistry
Physiology or Medicine
Literature, Peace and Economics
',
+ },
+ ],
+ "card-updated-at": 1599070341951,
+ "content-version-id": "4a3303f2-250d-4e57-9411-4e6b6ff30cd3",
+ "card-added-at": 1599070000242,
+ status: "draft",
+ id: "d100ec7c-7fed-4cf9-b376-d887371f15e6",
+ "content-id": "d100ec7c-7fed-4cf9-b376-d887371f15e6",
+ version: 9,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Karnataka tourism",
+ message: null,
+ image: {
+ key: "ace/2020-08/04562266-d044-4cf3-8ff3-58ab0b084d33/f4da51e521694ef3b14820c2fd32709e691f058e.jpg",
+ url: null,
+ attribution: "Credits: Quintype",
+ caption: null,
+ metadata: {
+ "original-url":
+ "https://s.rfi.fr/media/display/ddb13a2c-e9b1-11ea-9cf1-005056bf87d6/w:1280/p:16x9/f4da51e521694ef3b14820c2fd32709e691f058e.jpg",
+ width: 1280,
+ height: 720,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/a22e71ed-d2e6-442c-b30c-4eaae392241e",
+ type: "text",
+ "family-id": "d5e20362-1fc4-46a9-9e5f-cfd3f399b0e8",
+ title: "",
+ id: "a22e71ed-d2e6-442c-b30c-4eaae392241e",
+ metadata: {},
+ subtype: null,
+ text: "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ "original-url":
+ "https://s.rfi.fr/media/display/e21e72c0-ec66-11ea-9857-005056a964fe/w:1280/p:16x9/8bfca0efa60a7b897690bea687d9cb3b991611ff_0.jpg",
+ width: 1280,
+ height: 720,
+ },
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/613fcdb4-c551-4deb-be2f-ff50428235ec",
+ type: "image",
+ "family-id": "587b768d-9169-45c7-be30-98caa96b5c27",
+ "image-attribution": "",
+ title: "",
+ id: "613fcdb4-c551-4deb-be2f-ff50428235ec",
+ "image-s3-key":
+ "ace/2020-09/12d48d3c-dac1-4b99-b961-a724c92c6cc2/8bfca0efa60a7b897690bea687d9cb3b991611ff_0.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "page-url": "/story/231f55c0-ba92-4da6-a2c0-697c520f860f/element/3f57c4cb-0736-4c89-94a2-3891e6cf1dce",
+ type: "text",
+ "family-id": "28688b87-0abc-4703-b3be-6ad78de90f72",
+ title: "",
+ id: "3f57c4cb-0736-4c89-94a2-3891e6cf1dce",
+ metadata: {},
+ subtype: "summary",
+ text: "Dolor sit amet lorem ipsum, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: ' text promotional message
',
+ metadata: {
+ "promotional-message": true,
+ },
+ },
+ ],
+ "card-updated-at": 1599072759773,
+ "content-version-id": "d92aae5f-7d65-4260-853f-c440373905c4",
+ "card-added-at": 1599072125631,
+ status: "draft",
+ id: "92cea38b-f39a-4e1d-b510-9342cc6e792d",
+ "content-id": "92cea38b-f39a-4e1d-b510-9342cc6e792d",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Karnataka tourism",
+ message: null,
+ image: {
+ key: "ace/2020-09/12d48d3c-dac1-4b99-b961-a724c92c6cc2/8bfca0efa60a7b897690bea687d9cb3b991611ff_0.jpg",
+ url: null,
+ attribution: "",
+ caption: null,
+ metadata: {
+ "original-url":
+ "https://s.rfi.fr/media/display/e21e72c0-ec66-11ea-9857-005056a964fe/w:1280/p:16x9/8bfca0efa60a7b897690bea687d9cb3b991611ff_0.jpg",
+ width: 1280,
+ height: 720,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/child-sec/2020/09/02/karnataka-tourism",
+ "story-version-id": "fb04a248-7855-484c-ab2f-9b71e45fe7e7",
+ "content-type": "story",
+ "content-updated-at": 1599115422655,
+ "author-id": 187245,
+ "owner-id": 187245,
+ "linked-story-ids": [],
+ "promotional-message": ' text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1599070346878,
+ "hero-image-caption": "Supreme Court Justice Clarence Thomas in 2007. ",
+ version: 19,
+ "story-template": "photo",
+ "sequence-no": null,
+ "created-at": 1599115401203,
+ authors: [
+ {
+ slug: "shreya-shukla-2",
+ social: {
+ twitter: {
+ handle: "shuklashreya801",
+ },
+ },
+ name: "Shreya Shukla",
+ "contributor-role": {
+ id: 873,
+ name: "Author",
+ },
+ "avatar-url":
+ "https://lh6.googleusercontent.com/-pBdJGAfN81c/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfmrjS-gWdYzH3Gtmuib3KbVLdixw/photo.jpg",
+ bio: "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi ",
+ id: 187245,
+ "avatar-s3-key": null,
+ "twitter-handle": "shuklashreya801",
+ },
+ ],
+ metadata: {
+ "card-share": {
+ shareable: false,
+ },
+ },
+ "publish-at": null,
+ "assignee-name": "Shreya Shukla",
+};
+
+export const dummyLiveBlogStory = {
+ access: "subscription",
+ "updated-at": 1601277247375,
+ seo: { "meta-keywords": [], "claim-reviews": { story: null } },
+ "assignee-id": 425675,
+ "author-name": "Bindiya H",
+ "hero-image-hyperlink": "https://www.google.com",
+ tags: [
+ {
+ id: 163463,
+ name: "entertainment",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "entertainment",
+ "tag-type": "Tag",
+ },
+ {
+ id: 1209598,
+ name: "government",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "government",
+ "tag-type": "Tag",
+ },
+ {
+ id: 1409326,
+ name: "liveblog",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "liveblog",
+ "tag-type": "Tag",
+ },
+ ],
+ customSlotAfterStory: {
+ config: { targetingId: "" },
+ layout: "Leaderboard",
+ layoutLabel: "Leaderboard",
+ type: "ad",
+ },
+ headline: "Live blog story ",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "4270e6db-f238-46d4-8b6d-788ede41760e",
+ slug: "entertainment/2020/09/28/live-blog-story-3",
+ "last-published-at": 1601277247484,
+ subheadline: "At vero eos et accusamus iusto odio dignissimos ducimus qui blanditiis pre.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "entertainment",
+ name: "Entertainment",
+ "section-url": "https://ace-web.qtstage.io/news/entertainment",
+ id: 6447,
+ "parent-id": 38586,
+ "display-name": "Entertainment",
+ collection: { slug: "entertainment", name: "Entertainment", id: 3946 },
+ data: null,
+ },
+ ],
+ "story-audio": { "s3-key": null },
+ "read-time": 0,
+ "access-level-value": null,
+ "content-created-at": 1601276754377,
+ "owner-name": "Bindiya H",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ comments: null,
+ "word-count": 0,
+ entities: {},
+ "published-at": 1601277247484,
+ "is-live-blog": true,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ "is-amp-supported": true,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "Google",
+ "bullet-type": "123",
+ id: "4270e6db-f238-46d4-8b6d-788ede41760e",
+ "hero-image-s3-key": "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ contributors: [],
+ "associated-series-collection-ids": [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/3a569347-2bed-4763-bf3b-b58df01ad6cc",
+ type: "title",
+ "family-id": "844074a7-2b25-423a-b3c8-88461db91a26",
+ title: "",
+ id: "3a569347-2bed-4763-bf3b-b58df01ad6cc",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error",
+ },
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/739cf8a1-26d8-48fb-8cbf-771b1161ed68",
+ type: "text",
+ "family-id": "6b3e107b-02d4-4d72-b523-64bbf809246b",
+ title: "",
+ id: "739cf8a1-26d8-48fb-8cbf-771b1161ed68",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ },
+ ],
+ "card-updated-at": 1601431229891,
+ "content-version-id": "254fdb13-fa12-426f-a50b-8846847158a5",
+ "card-added-at": 1601431063377,
+ status: "draft",
+ id: "5c8900ed-4676-45f4-a7b6-aa28746370fc",
+ "content-id": "5c8900ed-4676-45f4-a7b6-aa28746370fc",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/8ee1c6ed-57d8-4891-8a77-e789b2d33c29",
+ type: "title",
+ "family-id": "fab4dbe9-acce-43cb-8d3e-3b7d83afe04a",
+ title: "",
+ id: "8ee1c6ed-57d8-4891-8a77-e789b2d33c29",
+ metadata: {},
+ subtype: null,
+ text: "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium",
+ },
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/78142993-d358-4c17-b610-9c80101f32b1",
+ type: "text",
+ "family-id": "ec1e356a-d274-4f71-9ef9-4f070c907894",
+ title: "",
+ id: "78142993-d358-4c17-b610-9c80101f32b1",
+ metadata: {},
+ subtype: null,
+ text: "Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem.
",
+ },
+ {
+ description: "",
+ "image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-09/55df83b2-3181-49f3-b1d9-31cc91dda1be/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ "member-id": 934395,
+ "image-metadata": {
+ "original-url":
+ "https://s.rfi.fr/media/display/a1b6b938-01c2-11eb-ba87-005056a964fe/w:1280/p:16x9/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ width: 1280,
+ height: 720,
+ },
+ "uploaded-at": 1601327722596,
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/bf739a18-6878-485a-adc7-d608d767f123",
+ type: "image",
+ "family-id": "98f289da-3a92-48e0-ac66-2c0381b9fbc8",
+ "image-attribution": "",
+ title: "Premier League",
+ id: "bf739a18-6878-485a-adc7-d608d767f123",
+ "image-s3-key":
+ "ace/2020-09/55df83b2-3181-49f3-b1d9-31cc91dda1be/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ ],
+ "card-updated-at": 1601431229891,
+ "content-version-id": "2640b2cd-e57e-47b8-9a09-0c9e61bec4a9",
+ "card-added-at": 1601372749401,
+ status: "draft",
+ id: "42c89975-2f45-472e-ae09-73fec6f020b7",
+ "content-id": "42c89975-2f45-472e-ae09-73fec6f020b7",
+ version: 3,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2020-09/55df83b2-3181-49f3-b1d9-31cc91dda1be/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2020-09/55df83b2-3181-49f3-b1d9-31cc91dda1be/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ attribution: "",
+ caption: null,
+ metadata: {
+ "original-url":
+ "https://s.rfi.fr/media/display/a1b6b938-01c2-11eb-ba87-005056a964fe/w:1280/p:16x9/3aae2ba50b3667e9f02e3674a37581fae1ebaf74_3.jpg",
+ width: 1280,
+ height: 720,
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ type: "title",
+ "family-id": "f73118f0-74b8-4b9f-b2f6-c3b26852e110",
+ title: "",
+ id: "1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error ",
+ },
+ ],
+ "card-updated-at": 1601431229891,
+ "content-version-id": "89935f86-dba4-4f90-b7ea-f59db58554f7",
+ "card-added-at": 1601289122939,
+ status: "draft",
+ id: "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ "content-id": "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ version: 6,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: { "key-event": true },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ type: "title",
+ "family-id": "f73118f0-74b8-4b9f-b2f6-c3b26852e110",
+ title: "",
+ id: "1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error ",
+ },
+ ],
+ "card-updated-at": 1601431229891,
+ "content-version-id": "89935f86-dba4-4f90-b7ea-f59db58554f7",
+ "card-added-at": 1601289122939,
+ status: "draft",
+ id: "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ "content-id": "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ version: 6,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: { "key-event": true },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ type: "title",
+ "family-id": "f73118f0-74b8-4b9f-b2f6-c3b26852e110",
+ title: "",
+ id: "1cef89d6-c934-4f3c-b4d1-56460eb076e4",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error ",
+ },
+ ],
+ "card-updated-at": 1601431229891,
+ "content-version-id": "89935f86-dba4-4f90-b7ea-f59db58554f7",
+ "card-added-at": 1601289122939,
+ status: "draft",
+ id: "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ "content-id": "69fb8bd4-77e8-4b17-bca4-c229b92ce57f",
+ version: 6,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: { "key-event": true },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/b62c0458-b8eb-4f3d-9f3b-86d04447a909",
+ type: "title",
+ "family-id": "b716edab-a036-4db6-8ca1-9d119afcac3d",
+ title: "",
+ id: "b62c0458-b8eb-4f3d-9f3b-86d04447a909",
+ metadata: {},
+ subtype: null,
+ text: "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium",
+ },
+ ],
+ "card-updated-at": 1601277247407,
+ "content-version-id": "49686375-3b2c-4f02-93a8-e47d1a063e1f",
+ "card-added-at": 1601277247407,
+ status: "draft",
+ id: "ea05dbab-e2d7-41f2-9055-5287bb659c6f",
+ "content-id": "ea05dbab-e2d7-41f2-9055-5287bb659c6f",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: { "key-event": true },
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/4270e6db-f238-46d4-8b6d-788ede41760e/element/3ca72d69-09d3-47be-8921-a993c111fd9e",
+ type: "title",
+ "family-id": "6ddbc420-4f89-488e-9332-d16ed4e4c481",
+ title: "",
+ id: "3ca72d69-09d3-47be-8921-a993c111fd9e",
+ metadata: {},
+ subtype: null,
+ text: "Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: ' text promotional message
',
+ metadata: { "promotional-message": true },
+ },
+ ],
+ "card-updated-at": 1601277247407,
+ "content-version-id": "0acfbcb5-c770-498e-8439-a137e031dede",
+ "card-added-at": 1601277247407,
+ status: "draft",
+ id: "e4bf3f6e-cb5f-4a3e-b1cd-965b523da0e3",
+ "content-id": "e4bf3f6e-cb5f-4a3e-b1cd-965b523da0e3",
+ version: 1,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "Live Updates",
+ message: null,
+ image: {
+ key: "ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ url: "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ attribution: "Google",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: { "mime-type": "image/jpeg", "focus-point": [975, 375], width: 1682, height: 820 },
+ },
+ },
+ attributes: { "key-event": true },
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/entertainment/2020/09/28/live-blog-story-3",
+ "story-version-id": "dbc68251-e21d-4b1f-b8d2-e6ec972249ce",
+ "content-type": "story",
+ "content-updated-at": 1601431232290,
+ "author-id": 425675,
+ "owner-id": 425675,
+ "linked-story-ids": [],
+ "promotional-message": ' text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1601276764461,
+ "hero-image-caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ version: 10,
+ "story-template": "live-blog",
+ "sequence-no": null,
+ "created-at": 1601431229862,
+ authors: [
+ {
+ slug: "bindiya-h",
+ social: { twitter: { handle: "h_bindiya" } },
+ name: "Bindiya H",
+ "contributor-role": { id: 873, name: "Author" },
+ "avatar-url":
+ "https://lh6.googleusercontent.com/-T9_sTsD4Qco/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rc0KarItXiZkh68r6sgS4QwAApcbg/photo.jpg",
+ bio: "Biooo ?",
+ id: 425675,
+ "avatar-s3-key": null,
+ "twitter-handle": "h_bindiya",
+ },
+ ],
+ metadata: { "is-closed": false, "card-share": { shareable: false } },
+ "hero-image-url": "https://thumbor-stg.assettype.com/ace/2019-03/f25cc9c1-917c-4a19-9c9a-fa498f84f17b/test.jpg",
+ "publish-at": null,
+ "assignee-name": "Bindiya H",
+};
+
+export const dummyListicleStory = {
+ access: "subscription",
+ "updated-at": 1602234193104,
+ seo: { "claim-reviews": { story: null } },
+ "assignee-id": 927927,
+ "author-name": "Pravin Atigre",
+ "hero-image-hyperlink": "https://www.google.com",
+ tags: [
+ { id: 1421524, name: "health", "meta-description": null, "meta-title": null, slug: "health", "tag-type": "Tag" },
+ { id: 1421525, name: "culture", "meta-description": null, "meta-title": null, slug: "culture", "tag-type": "Tag" },
+ {
+ id: 1421526,
+ name: "workplace",
+ "meta-description": null,
+ "meta-title": null,
+ slug: "workplace",
+ "tag-type": "Tag",
+ },
+ ],
+ customSlotAfterStory: {
+ config: { targetingId: "" },
+ layout: "Leaderboard",
+ layoutLabel: "Leaderboard",
+ type: "ad",
+ },
+ headline: "It pays to be kind: improving workplace culture through kindness",
+ "storyline-id": null,
+ votes: {},
+ "story-content-id": "307e5a1b-5cf0-4321-8193-827b2add174f",
+ slug: "health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "last-published-at": 1602234199516,
+ subheadline: "Neque porro quisquam est, qui dolorem ipsum quia dolor sit.",
+ alternative: {},
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "health",
+ name: "Health",
+ "section-url": "https://ace-web.qtstage.io/news/health",
+ id: 11181,
+ "parent-id": 38586,
+ "display-name": "Health",
+ collection: { slug: "health", name: "Health", id: 15603 },
+ data: null,
+ },
+ ],
+ "story-audio": {
+ "s3-key": "ace/story-audio/2020-10/307e5a1b-5cf0-4321-8193-827b2add174f/.0e6d9a89-3bb7-47b0-8bee-a34b5f75f5e9.mp3",
+ },
+ "read-time": 2,
+ "access-level-value": null,
+ "content-created-at": 1602154991378,
+ "owner-name": "Pravin Atigre",
+ "custom-slug": null,
+ "push-notification": null,
+ "publisher-id": 97,
+ "hero-image-metadata": {
+ width: 1600,
+ height: 900,
+ "mime-type": "image/jpeg",
+ "file-size": 228062,
+ "file-name": "seo.jpg",
+ },
+ comments: null,
+ "word-count": 232,
+ entities: {},
+ "published-at": 1602234199516,
+ "is-live-blog": false,
+ "breaking-news-linked-story-id": null,
+ "storyline-title": null,
+ summary: null,
+ "push-notification-title": null,
+ "external-id": null,
+ "canonical-url": null,
+ "is-amp-supported": true,
+ autotags: [],
+ "linked-entities": [],
+ status: "published",
+ "hero-image-attribution": "Credits: Asif Asharaf",
+ "bullet-type": "123",
+ id: "307e5a1b-5cf0-4321-8193-827b2add174f",
+ "hero-image-s3-key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ contributors: [],
+ "associated-series-collection-ids": [],
+ cards: [
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/5ffad09b-7db1-489a-8308-f7d63c745c70",
+ type: "title",
+ "family-id": "d3bf6239-6723-4291-a7d1-6164c808d07d",
+ title: "",
+ id: "5ffad09b-7db1-489a-8308-f7d63c745c70",
+ metadata: {},
+ subtype: null,
+ text: "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ },
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/f24c83a4-dd3d-47fe-8d05-90f6ada10632",
+ type: "text",
+ "family-id": "0a4af70b-2434-4038-8134-87cdc60fc379",
+ title: "",
+ id: "f24c83a4-dd3d-47fe-8d05-90f6ada10632",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ },
+ ],
+ "card-updated-at": 1602234185925,
+ "content-version-id": "9378abd6-2cec-4bdd-88b2-005cc6d1775f",
+ "card-added-at": 1602155475842,
+ status: "draft",
+ id: "aeb377da-eb9f-4013-ae19-fb6fb821e926",
+ "content-id": "aeb377da-eb9f-4013-ae19-fb6fb821e926",
+ version: 9,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "It pays to be kind: improving workplace culture through kindness",
+ message: null,
+ image: {
+ key: "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ url: null,
+ attribution: "Credits: Asif Asharaf",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: {
+ width: 1600,
+ height: 900,
+ "mime-type": "image/jpeg",
+ "file-size": 228062,
+ "file-name": "seo.jpg",
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/82244bcd-c453-41c4-a215-bb038f569375",
+ type: "title",
+ "family-id": "11f060e0-a1aa-4963-9778-24f6b6eca401",
+ title: "",
+ id: "82244bcd-c453-41c4-a215-bb038f569375",
+ metadata: {},
+ subtype: null,
+ text: "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ },
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/ae7131f6-450a-4589-9995-812ba5832c58",
+ type: "text",
+ "family-id": "a970d458-c7d8-4512-8420-4bf3a4046caf",
+ title: "",
+ id: "ae7131f6-450a-4589-9995-812ba5832c58",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ },
+ ],
+ "card-updated-at": 1602155725655,
+ "content-version-id": "43ff8777-4176-4294-95a3-5aaee8e34537",
+ "card-added-at": 1602155520864,
+ status: "draft",
+ id: "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ "content-id": "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ version: 7,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "It pays to be kind: improving workplace culture through kindness",
+ message: null,
+ image: {
+ key: "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ url: null,
+ attribution: "Credits: Asif Asharaf",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: {
+ width: 1600,
+ height: 900,
+ "mime-type": "image/jpeg",
+ "file-size": 228062,
+ "file-name": "seo.jpg",
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ type: "title",
+ "family-id": "1b8bcf8e-19cb-4d16-ac0e-bb4f0939dbbf",
+ title: "",
+ id: "d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ metadata: {},
+ subtype: null,
+ text: "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ },
+ {
+ description: "",
+ "image-metadata": {
+ width: 960,
+ height: 540,
+ "mime-type": "image/jpeg",
+ "file-size": 26544,
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ },
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/a7f79ffa-4be0-4207-a495-a035a020cac3",
+ type: "image",
+ "family-id": "6d5d82b8-f879-476d-84a2-1f62f74acdd5",
+ "image-attribution": "Jon Doe",
+ title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
+ id: "a7f79ffa-4be0-4207-a495-a035a020cac3",
+ "image-s3-key":
+ "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ metadata: {},
+ subtype: null,
+ hyperlink: "https://www.google.com/",
+ },
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/2a248f86-3181-4456-9f96-f7e56668fbc1",
+ type: "text",
+ "family-id": "b9a1fdd8-5727-4e5a-86a6-467922aa155f",
+ title: "",
+ id: "2a248f86-3181-4456-9f96-f7e56668fbc1",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ },
+ ],
+ "card-updated-at": 1602156067786,
+ "content-version-id": "dbfeb570-f9aa-4875-9c3d-f48bf551dd91",
+ "card-added-at": 1602155541845,
+ status: "draft",
+ id: "d6876a49-9319-4c24-9bf9-881ea454b967",
+ "content-id": "d6876a49-9319-4c24-9bf9-881ea454b967",
+ version: 10,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "It pays to be kind: improving workplace culture through kindness",
+ message: null,
+ image: {
+ key: "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ url: null,
+ attribution: "Jon Doe",
+ caption: null,
+ metadata: {
+ width: 960,
+ height: 540,
+ "mime-type": "image/jpeg",
+ "file-size": 26544,
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ {
+ "story-elements": [
+ {
+ description: "",
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/20db973e-4612-4933-9acd-5a6d4c3d281d",
+ type: "text",
+ "family-id": "b8f84eb7-4a91-45ba-a5d3-95d5f48c7772",
+ title: "",
+ id: "20db973e-4612-4933-9acd-5a6d4c3d281d",
+ metadata: {},
+ subtype: null,
+ text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ },
+ {
+ id: "00000000-0000-0000-0000-000000000000",
+ description: "",
+ title: "",
+ subtype: null,
+ type: "text",
+ text: ' text promotional message
',
+ metadata: { "promotional-message": true },
+ },
+ ],
+ "card-updated-at": 1602156067786,
+ "content-version-id": "dc523cfa-dcf2-41b5-ab7a-b5c3577e11f7",
+ "card-added-at": 1602156035607,
+ status: "draft",
+ id: "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ "content-id": "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ version: 2,
+ metadata: {
+ "social-share": {
+ shareable: false,
+ title: "It pays to be kind: improving workplace culture through kindness",
+ message: null,
+ image: {
+ key: "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ url: null,
+ attribution: "Credits: Asif Asharaf",
+ caption: "Saque ipsa quae ab illo inventore veritatis et quip",
+ metadata: {
+ width: 1600,
+ height: 900,
+ "mime-type": "image/jpeg",
+ "file-size": 228062,
+ "file-name": "seo.jpg",
+ },
+ },
+ },
+ attributes: {},
+ },
+ },
+ ],
+ url: "https://ace-web.qtstage.io/health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "story-version-id": "8768d161-6433-4f1e-b8ef-e632d180ede9",
+ "content-type": "story",
+ "content-updated-at": 1602234199729,
+ "author-id": 927927,
+ "owner-id": 927927,
+ "linked-story-ids": [],
+ "promotional-message": ' text promotional message
',
+ "asana-project-id": null,
+ "first-published-at": 1602155728566,
+ "hero-image-caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ version: 18,
+ "story-template": "listicle",
+ "sequence-no": null,
+ "created-at": 1602234185901,
+ authors: [
+ {
+ slug: "pravin-atigre",
+ social: null,
+ name: "Pravin Atigre",
+ "contributor-role": { id: 873, name: "Author" },
+ "avatar-url":
+ "https://lh3.googleusercontent.com/-I9kNTMFkn3E/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rdZeM4LMMdylmULvtrAvmeVF9DmAQ/photo.jpg",
+ bio: null,
+ id: 927927,
+ "avatar-s3-key": null,
+ "twitter-handle": null,
+ },
+ ],
+ metadata: { "card-share": { shareable: false } },
+ "publish-at": null,
+ "assignee-name": "Pravin Atigre",
+};
diff --git a/app/isomorphic/arrow/components/Fixture/index.js b/app/isomorphic/arrow/components/Fixture/index.js
new file mode 100644
index 000000000..27305cbd7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Fixture/index.js
@@ -0,0 +1,420 @@
+/* eslint-disable no-undef */
+import configJSON from "./config.json";
+import { dummyStory, dummyPhotoStory, dummyLiveBlogStory, dummyListicleStory } from "./dummyStory";
+import get from "lodash/get";
+import { createStore } from "redux";
+
+export const generateConfig = () => configJSON;
+
+export const generateStory = (storyType, data = {}) => {
+ switch (storyType) {
+ case "photo-story":
+ return Object.assign(dummyPhotoStory, data);
+ case "live-blog":
+ return Object.assign(dummyLiveBlogStory, data);
+ case "listicle-story":
+ return Object.assign(dummyListicleStory, data);
+ default:
+ return Object.assign(dummyStory, data);
+ }
+};
+
+export const generateCollection = ({ stories = 0, subCollections = [], itemMeta = {} } = {}) => {
+ const items = subCollections;
+ Array(stories)
+ .fill({ type: "story", ...itemMeta })
+ .forEach((item, index) => {
+ const story = generateStory();
+ const itm = Object.assign({}, item, { id: story.id, story });
+ items.push(itm);
+ });
+ return {
+ type: "collection",
+ name: "Politics",
+ id: parseInt(Math.random() * 10 ** 10),
+ slug: "collection-slug",
+ template: "section",
+ "created-at": 1597163351395,
+ metadata: {
+ "cover-image": {
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-08/f71dd527-cbc6-4cc9-94c2-8feee917f7e9/fortune.jpg",
+ metadata: {
+ width: 914,
+ height: 1200,
+ "mime-type": "image/jpeg",
+ "file-size": 179037,
+ "file-name": "fortune.jpg",
+ },
+ "cover-image-s3-key": "ace/2020-08/f71dd527-cbc6-4cc9-94c2-8feee917f7e9/fortune.jpg",
+ caption: "Some Politician",
+ },
+ section: [{ id: 11181, name: "Health", "parent-id": null, "tree-ids": [11181] }],
+ },
+ summary:
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer aliquam felis sed tellus mattis ultricies. Maecenas accumsan hendrerit turpis, a sollicitudin velit viverra nec. Praesent pretium, orci ac sodales volutpat, tellus orci rutrum metus.",
+ items: items,
+ };
+};
+
+export const generateCollections = (count = 0) => {
+ const collections = [];
+ Array(count)
+ .fill(0)
+ .forEach(() => {
+ collections.push(generateCollection({ stories: 10 }));
+ });
+
+ return {
+ type: "collection",
+ name: "News",
+ id: parseInt(Math.random() * 10 ** 10),
+ slug: "collection-slug",
+ template: "section",
+ metadata: {
+ "cover-image": {
+ "cover-image-url":
+ "http://d9zv4zsfqrm9s.cloudfront.net/ace/2017-12/c811b029-962f-4364-b376-ec1548d67494/earth.jpg",
+ "cover-image-metadata": {
+ width: 600,
+ height: 600,
+ "mime-type": "image/jpeg",
+ "focus-point": [355, 204],
+ },
+ "cover-image-s3-key": "vikatan/2019-01/f0b702ac-bfd1-4dd6-8e33-52cbb0bb3153/143203_thumb.jpg",
+ caption: "Some Politician",
+ },
+ section: [{ id: 11181, name: "Health", "parent-id": null, "tree-ids": [11181] }],
+ },
+ summary: "This is a collection concerned with politics",
+ items: collections,
+ };
+};
+
+export const generateCollectionsWithStories = (count = 0) => {
+ const items = generateCollection({ stories: 12 });
+ const collec = generateCollections(2);
+ const collections = [];
+ const merged = items.items.concat(collec.items);
+
+ Array(count)
+ .fill(0)
+ .forEach(() => {
+ collections.push(generateCollection({ stories: 12 }));
+ });
+
+ return {
+ type: "collection",
+ name: "News",
+ id: parseInt(Math.random() * 10 ** 10),
+ slug: "collection-slug",
+ template: "section",
+ metadata: {
+ "cover-image": {
+ "cover-image-url":
+ "http://d9zv4zsfqrm9s.cloudfront.net/ace/2017-12/c811b029-962f-4364-b376-ec1548d67494/earth.jpg",
+ "cover-image-metadata": {
+ width: 600,
+ height: 600,
+ "mime-type": "image/jpeg",
+ "focus-point": [355, 204],
+ },
+ "cover-image-s3-key": "vikatan/2019-01/f0b702ac-bfd1-4dd6-8e33-52cbb0bb3153/143203_thumb.jpg",
+ caption: "Some Politician",
+ },
+ section: [{ id: 11181, name: "Health", "parent-id": null, "tree-ids": [11181] }],
+ },
+ summary: "This is a collection concerned with politics",
+ items: merged,
+ };
+};
+
+export const generateSubMenu = () => {
+ return {
+ items: [
+ {
+ "tag-name": null,
+ "item-id": 22564,
+ rank: 5493,
+ title: "Link 1",
+ "item-type": "section",
+ "section-slug": "gurupeyarchi",
+ "tag-slug": null,
+ id: 5493,
+ "parent-id": null,
+ url: "https://vikatan-web.qtstage.io/section/gurupeyarchi",
+ "section-name": "அமெரிக்காவில்",
+ data: {
+ color: "#c7c7c7",
+ },
+ items: [
+ {
+ "tag-name": null,
+ "item-id": 22384,
+ rank: 5494,
+ title: "Link 2",
+ "item-type": "section",
+ "section-slug": "cinema",
+ "tag-slug": null,
+ id: 5494,
+ "parent-id": 5493,
+ url: "https://vikatan-web.qtstage.io/section/cinema",
+ "section-name": "அமெரிக்காவில்",
+ data: {
+ color: "#f7f7f7",
+ },
+ },
+ {
+ "tag-name": null,
+ "item-id": 23089,
+ rank: 5504,
+ title: "Child 1",
+ "item-type": "section",
+ "section-slug": "dry6feb2019",
+ "tag-slug": null,
+ id: 5504,
+ "parent-id": 5493,
+ url: "https://vikatan-web.qtstage.io/section/dry6feb2019",
+ "section-name": "அமெரிக்காவில்",
+ data: {
+ color: "#000000",
+ },
+ },
+ ],
+ },
+ ],
+ };
+};
+export const authorData = {
+ slug: "parvathi-mohan-2",
+ name: "Lucy Douglas",
+ social: {
+ twitter: {
+ url: "https://twitter.com/Reena00659364",
+ handle: "Twitter",
+ },
+ youtube: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Youtube",
+ },
+ facebook: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Facebook",
+ },
+ linkedin: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "LinkedIn",
+ },
+ whatsapp: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Whatsapp",
+ },
+ instagram: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Instagram",
+ },
+ pinterest: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Pinterest",
+ },
+ },
+ bio: "Lucy Douglas is a freelance journalist based in London. She has a particular interest in small business, startups and the founders behind them and, in a previous life, was an assistant-editor at Professional Beauty magazine, where she road-tested spas up and down the country and checked out the latest advances.",
+ id: 94985,
+ "avatar-url": "https://thumbor-stg.assettype.com/ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "avatar-s3-key": "ace/2019-08/145ab200-429f-44ac-8618-00c6e4643e31/cat.jpeg",
+ "twitter-handle": "quintype_inc",
+ stats: {
+ contributions: null,
+ },
+ metadata: {},
+};
+
+export const generateStoryElementData = (elementType) => {
+ const story = generateStory();
+ const storyElements = get(story, ["cards", "0", "story-elements"]);
+ return storyElements.find((element) => {
+ const { subtype, type } = element;
+ if (subtype) return subtype === elementType;
+ return type === elementType;
+ });
+};
+
+export const generateMagazineIssues = () => {
+ return [
+ {
+ collection: {
+ id: 106138,
+ name: "Manifique",
+ metadata: {
+ "cover-image": {
+ success: true,
+ url: "https://stg-quintype-dropbox.s3-accelerate.amazonaws.com/ace.staging.quintype.com/2020-09-09/874/aleksandra_tanasienko_oQi_UdQDkzw_unsplash.jpg",
+ metadata: {
+ width: 4000,
+ height: 6000,
+ "mime-type": "image/jpeg",
+ "file-size": 2574353,
+ "file-name": "aleksandra-tanasienko-oQi_UdQDkzw-unsplash.jpg",
+ },
+ "extracted-data": {
+ artist: "",
+ },
+ attribution: "",
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-09/f20cecc2-2466-4e3d-8b04-f013d880a9d0/aleksandra_tanasienko_oQi_UdQDkzw_unsplash.jpg",
+ "cover-image-s3-key":
+ "ace/2020-09/f20cecc2-2466-4e3d-8b04-f013d880a9d0/aleksandra_tanasienko_oQi_UdQDkzw_unsplash.jpg",
+ },
+ entities: {
+ collectionEntities: {
+ magazine: [
+ {
+ id: 199563,
+ name: "New Magazine",
+ type: "magazine",
+ "entity-type-id": 382,
+ },
+ ],
+ },
+ },
+ },
+ "collection-date": null,
+ slug: "manifique",
+ template: "collection",
+ "data-source": "manual",
+ "created-at": 1599639879798,
+ },
+ },
+ {
+ collection: {
+ id: 106137,
+ name: "High Times",
+ metadata: {
+ "cover-image": {
+ success: true,
+ url: "https://stg-quintype-dropbox.s3-accelerate.amazonaws.com/ace.staging.quintype.com/2020-09-28/294/da90b29ce035b5a3a134da39e94d654c.jpg",
+ metadata: {
+ width: 474,
+ height: 581,
+ "mime-type": "image/jpeg",
+ "file-size": 49921,
+ "file-name": "da90b29ce035b5a3a134da39e94d654c.jpg",
+ },
+ "extracted-data": {
+ artist: "",
+ },
+ attribution: "",
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-09/92f5453c-4477-40d3-9a44-3cf0c245b268/da90b29ce035b5a3a134da39e94d654c.jpg",
+ "cover-image-s3-key":
+ "ace/2020-09/92f5453c-4477-40d3-9a44-3cf0c245b268/da90b29ce035b5a3a134da39e94d654c.jpg",
+ },
+ entities: {
+ collectionEntities: {
+ magazine: [
+ {
+ id: 199563,
+ name: "New Magazine",
+ type: "magazine",
+ "entity-type-id": 382,
+ },
+ ],
+ },
+ },
+ },
+ "collection-date": null,
+ slug: "high-times",
+ template: "collection",
+ "data-source": "manual",
+ "created-at": 1599639711992,
+ },
+ },
+ {
+ collection: {
+ id: 106131,
+ name: "New Magazine Issue",
+ metadata: {
+ "cover-image": {
+ success: true,
+ url: "https://stg-quintype-dropbox.s3-accelerate.amazonaws.com/ace.staging.quintype.com/2020-09-09/1200/alexis_zacharko_07rXq3YroVM_unsplash.jpg",
+ metadata: {
+ width: 3456,
+ height: 5184,
+ "mime-type": "image/jpeg",
+ "file-size": 3084612,
+ "file-name": "alexis-zacharko-07rXq3YroVM-unsplash.jpg",
+ "focus-point": [2154, 2269],
+ },
+ "extracted-data": {
+ artist: "",
+ },
+ attribution: "",
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-09/ed79b1ed-5d63-4ec1-8a4b-da87d8ed7edc/alexis_zacharko_07rXq3YroVM_unsplash.jpg",
+ "cover-image-s3-key":
+ "ace/2020-09/ed79b1ed-5d63-4ec1-8a4b-da87d8ed7edc/alexis_zacharko_07rXq3YroVM_unsplash.jpg",
+ },
+ entities: {
+ collectionEntities: {
+ magazine: [
+ {
+ id: 199563,
+ name: "New Magazine",
+ type: "magazine",
+ "entity-type-id": 382,
+ },
+ ],
+ },
+ },
+ },
+ "collection-date": null,
+ slug: "new-magazine-issue",
+ template: "collection",
+ "data-source": "manual",
+ "created-at": 1599616208142,
+ },
+ },
+ {
+ collection: {
+ id: 106089,
+ name: "First Magazine Collection",
+ metadata: {
+ "cover-image": {
+ "cover-image-s3-key": "ace/2020-07/3e3dc7df-8a1c-462d-a236-265d45c28651/evil_caat.jpg",
+ "cover-image-metadata": {
+ "mime-type": "image/jpeg",
+ "focus-point": [3444, 3222],
+ width: 6000,
+ "file-size": 10319729,
+ "file-name": "evil caat.jpg",
+ height: 4000,
+ },
+ caption: null,
+ "cover-image-url":
+ "https://thumbor-stg.assettype.com/ace/2020-07/3e3dc7df-8a1c-462d-a236-265d45c28651/evil_caat.jpg",
+ },
+ entities: {
+ collectionEntities: {
+ magazine: [
+ {
+ id: 199563,
+ name: "New Magazine",
+ type: "magazine",
+ "entity-type-id": 382,
+ },
+ ],
+ },
+ },
+ },
+ "collection-date": null,
+ slug: "first-magazine-collection",
+ template: "collection",
+ "data-source": "automated",
+ "created-at": 1599571645414,
+ },
+ },
+ ];
+};
+
+const reducer = () => ({ qt: { config: { "cdn-image": "thumbor-stg.assettype.com" } } });
+export const generateStore = createStore(reducer);
diff --git a/app/isomorphic/arrow/components/Fixture/slot-config.js b/app/isomorphic/arrow/components/Fixture/slot-config.js
new file mode 100644
index 000000000..36c973fb2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Fixture/slot-config.js
@@ -0,0 +1,29 @@
+export const slotConfig = [{ type: "ad" }];
+
+export const slotData = [
+ {
+ type: "widget",
+ layout: "widget",
+ layoutLabel: "Custom Widget",
+ pageType: "story",
+ widgetCode: "V2lkZ2V0JTIwMQ==",
+ config: {
+ customCode: "V2lkZ2V0JTIwMQ==",
+ },
+ },
+ {
+ type: "collection",
+ collectionSlug: "",
+ title: "",
+ },
+ {
+ type: "widget",
+ layout: "widget",
+ layoutLabel: "Custom Widget",
+ pageType: "story",
+ widgetCode: "V2lkZ2V0JTIwMg==",
+ config: {
+ customCode: "V2lkZ2V0JTIwMg==",
+ },
+ },
+];
diff --git a/app/isomorphic/arrow/components/Molecules/FullScreenImages/fullscreenimages.scss b/app/isomorphic/arrow/components/Molecules/FullScreenImages/fullscreenimages.scss
new file mode 100644
index 000000000..38188c25c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/FullScreenImages/fullscreenimages.scss
@@ -0,0 +1,56 @@
+@import "../../../../../../node_modules/react-image-lightbox/style.css";
+
+.arr--image-lightbox {
+ .icon.loader {
+ color: var(--arrow-c-brand1);
+ }
+
+ .ril__toolbarRightSide {
+ flex-grow: 1;
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ .ril__toolbar {
+ bottom: 0;
+ top: auto;
+ }
+
+ img.ril__image {
+ width: unset;
+ max-width: 100%;
+ }
+
+ .ril__zoomInButton {
+ position: relative;
+ border-right: 1px solid var(--arrow-c-mono7);
+ background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNyIgaGVpZ2h0PSIxNyIgdmlld0JveD0iMCAwIDE3IDE3Ij4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+CiAgICAgICAgPHBhdGggZD0iTS0zLTRoMjR2MjRILTN6IiBvcGFjaXR5PSIuMzg0Ii8+CiAgICAgICAgPGcgZmlsbD0iI0ZGRiI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik04IDBoMXYxN0g4eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJNMTcgOHYxSDBWOHoiLz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPgo=")
+ no-repeat center;
+ }
+
+ .ril__zoomOutButton {
+ position: relative;
+ border-right: 1px solid var(--arrow-c-mono7);
+ background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNyIgaGVpZ2h0PSIxIiB2aWV3Qm94PSIwIDAgMTcgMSI+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9Im5vbnplcm8iPgogICAgICAgIDxwYXRoIGQ9Ik0tMy0xMmgyNHYyNEgtM3oiIG9wYWNpdHk9Ii4zODQiLz4KICAgICAgICA8cGF0aCBmaWxsPSIjRkZGIiBkPSJNMTcgMHYxSDBWMHoiLz4KICAgIDwvZz4KPC9zdmc+Cg==")
+ no-repeat center;
+ }
+
+ .ril__zoomInButton,
+ .ril__zoomOutButton,
+ .ril__closeButton {
+ width: 48px;
+ height: 48px;
+ }
+ .ril__navButtons {
+ outline: none;
+ border-radius: 0;
+ }
+
+ .ril__toolbarItem .arr--hyperlink-button {
+ position: unset;
+ svg {
+ position: relative;
+ top: 10px;
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Molecules/FullScreenImages/index.js b/app/isomorphic/arrow/components/Molecules/FullScreenImages/index.js
new file mode 100644
index 000000000..1a7b0c516
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/FullScreenImages/index.js
@@ -0,0 +1,98 @@
+import React, { useState, Suspense, lazy } from "react";
+import { useSelector } from "react-redux";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import "./fullscreenimages.scss";
+import { Loading } from "../../Svgs/Loading/loading";
+import { HyperLink } from "../../Atoms/Hyperlink";
+
+const Lightbox = lazy(() => import("react-image-lightbox"));
+
+export const FullScreenImages = ({ imageSlug = "", template, element = {}, props }) => {
+ const cdnImage = useSelector((state) => get(state, ["qt", "config", "cdn-image"]));
+ const [photoIndex, handleImage] = useState(0);
+ const [isOpen, handleOpen] = useState(false);
+ const deviceWidth = get(global, ["innerWidth"], 0);
+
+ const hyperlinks = get(element, ["story-elements"], []).map((image) => image.hyperlink);
+
+ let imageUrlList = [];
+ if (!imageSlug) {
+ imageUrlList = get(element, ["story-elements"], []).map(
+ (image) => `//${cdnImage}/${image["image-s3-key"]}?w=${deviceWidth}`
+ );
+ }
+
+ const onClickHandler = (index) => {
+ handleOpen(true);
+ handleImage(index);
+ };
+ const onClickKey = imageSlug ? "onClick" : "onClickHandler";
+
+ const imageUrlListLength = imageUrlList.length;
+ const onMoveNext = (imageUrlListLength) => {
+ const next = (photoIndex + 1) % imageUrlListLength;
+ handleImage(next);
+ };
+
+ const onMovePrev = (imageUrlListLength) => {
+ const prev = (photoIndex + imageUrlListLength - 1) % imageUrlListLength;
+ handleImage(prev);
+ };
+
+ const onClose = () => {
+ handleImage(0);
+ handleOpen(false);
+ };
+
+ const getHyperLink = () => {
+ if (element.hyperlink || hyperlinks[photoIndex]) {
+ return ;
+ }
+ return null;
+ };
+
+ const lightboxProps = imageSlug
+ ? {
+ mainSrc: `//${cdnImage}/${imageSlug}?w=${deviceWidth}`,
+ toolbarButtons: [getHyperLink()],
+ }
+ : {
+ mainSrc: imageUrlList[photoIndex],
+ nextSrc: imageUrlList[(photoIndex + 1) % imageUrlListLength],
+ prevSrc: imageUrlList[(photoIndex + imageUrlListLength - 1) % imageUrlListLength],
+ onMoveNextRequest: () => onMoveNext(imageUrlListLength),
+ onMovePrevRequest: () => onMovePrev(imageUrlListLength),
+ toolbarButtons: [getHyperLink()],
+ };
+
+ const LoadingScreen = () => {
+ return (
+
+
+
+ );
+ };
+
+ return (
+
+ {React.createElement(template, {
+ [onClickKey]: onClickHandler,
+ ...props,
+ })}
+ {isOpen ? (
+ }>
+
+
+ ) : null}
+
+ );
+};
+FullScreenImages.propTypes = {
+ imageSlug: PropTypes.string,
+ template: PropTypes.func,
+ element: PropTypes.shape({
+ "story-elements": PropTypes.array,
+ }),
+ props: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/KeyEvents/README.md b/app/isomorphic/arrow/components/Molecules/KeyEvents/README.md
new file mode 100644
index 000000000..ff89b8faf
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/KeyEvents/README.md
@@ -0,0 +1,21 @@
+# Key Events
+
+Key events is used to represent the event templates for a live blog story.
+
+## Usage
+
+```jsx
+import { KeyEvents } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+
+const config = {
+ theme: #333333;
+}
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Molecules/KeyEvents/index.js b/app/isomorphic/arrow/components/Molecules/KeyEvents/index.js
new file mode 100644
index 000000000..6184e5c80
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/KeyEvents/index.js
@@ -0,0 +1,112 @@
+import React, { useState } from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { ClockIcon } from "../../Svgs/clock-icon";
+import { getTextColor, getTimeStamp, timestampToFormat } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StateProvider } from "../../SharedContext";
+import "./key-events.m.css";
+import { useSelector } from "react-redux";
+
+const KeyEventCards = (props) => {
+ const { config, index, cardLength, card = {}, slug, loadCards, showLoadMore } = props;
+ const { "card-added-at": cardAddedAt, "story-elements": storyElements = [], id } = card;
+ const { theme, mountAt = "" } = config;
+ const textColor = getTextColor(theme);
+ const dark = "#333";
+ const light = "#fff";
+ const updateColor = textColor === "dark" ? dark : light;
+ const lastElement = index === cardLength - 1;
+ const borderLeft = lastElement ? "borderNone" : "";
+
+ return (
+ <>
+
+
+ {getTimeStamp(cardAddedAt, timestampToFormat, { showTime: true, isTimeFirst: true })}
+
+
+
+ {storyElements.map((element, index) => element.type === "title" &&
{element.text} )}
+ {showLoadMore && lastElement && loadCards && (
+
+ )}
+
+
+ >
+ );
+};
+
+KeyEventCards.propTypes = {
+ card: PropTypes.shape({
+ "card-added-at": PropTypes.number,
+ "story-elements": PropTypes.array,
+ id: PropTypes.string,
+ }),
+ config: PropTypes.object,
+ slug: PropTypes.string,
+ story: PropTypes.object,
+ elements: PropTypes.array,
+ index: PropTypes.number,
+ cardLength: PropTypes.number,
+ loadCards: PropTypes.bool,
+ showLoadMore: PropTypes.bool,
+};
+
+const KeyEvents = ({ story = {}, config = {}, showLoadMore = true, publishedDetails = {} }) => {
+ const keyEventsTitle = get(publishedDetails, ["localizedKeyEvents"], "Key Events") || "Key Events";
+ const cards = get(story, ["cards"], []);
+ const { theme, initialLoadCount = 4 } = config;
+ const textColor = getTextColor(theme);
+ const [initialCount, updatedCount] = useState(initialLoadCount);
+ const cardsWithKeyEvents = cards.filter((card) => get(card.metadata, ["attributes", "key-event"]));
+ const lastElement = cardsWithKeyEvents.length > initialCount;
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const onClick = () => {
+ updatedCount(initialCount + initialLoadCount);
+ };
+
+ if (cardsWithKeyEvents.length < 1) {
+ return null;
+ }
+
+ const showAllCards = !showLoadMore ? cardsWithKeyEvents.slice(0, 4) : cardsWithKeyEvents.slice(0, initialCount);
+
+ const keyEvents = showAllCards.map((card, index, source) => (
+
+ ));
+
+ return (
+
+
{keyEventsTitle}
+
{keyEvents}
+ {showLoadMore && lastElement && (
+
+ )}
+
+ );
+};
+
+KeyEvents.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.object,
+ showLoadMore: PropTypes.bool,
+ publishedDetails: PropTypes.object,
+};
+
+export default StateProvider(KeyEvents);
diff --git a/app/isomorphic/arrow/components/Molecules/KeyEvents/key-events.m.css b/app/isomorphic/arrow/components/Molecules/KeyEvents/key-events.m.css
new file mode 100644
index 000000000..332f5aef1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/KeyEvents/key-events.m.css
@@ -0,0 +1,89 @@
+.container {
+ border-bottom: 1px solid var(--arrow-c-mono5);
+}
+
+.event-title > h3 {
+ font-size: var(--arrow-fs-xs);
+}
+
+.container:global(.arrow-component) > h3 {
+ font-size: var(--arrow-fs-m);
+ padding: var(--arrow-spacing-xs);
+}
+
+.cards-wrapper {
+ margin-top: var(--arrow-spacing-s);
+}
+
+.time-wrapper {
+ display: flex;
+ align-items: center;
+ font-size: var(--arrow-fs-tiny);
+ svg {
+ margin-right: var(--arrow-spacing-xxs);
+ }
+}
+
+.event-title {
+ position: relative;
+ padding: 0 14px 28px;
+ margin-left: var(--arrow-spacing-s);
+}
+
+.event-title.dark {
+ border-left: 1px solid var(--arrow-c-mono5);
+}
+
+.event-title.light {
+ border-left: 1px solid var(--arrow-c-invert-mono5);
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+.light.borderNone,
+.dark.borderNone {
+ border-left: none;
+}
+
+.fade-out {
+ position: absolute;
+ top: -20px;
+ height: 100%;
+ width: 100%;
+ background-image: inherit;
+ pointer-events: none;
+}
+
+.container.light {
+ border-bottom: 1px solid var(--arrow-c-invert-mono5);
+}
+
+html[dir="rtl"] {
+ .time-wrapper {
+ direction: ltr;
+ flex-direction: row-reverse;
+ svg {
+ margin-right: 0;
+ margin-left: var(--arrow-spacing-xxs);
+ }
+ }
+
+ .event-title.dark,
+ .event-title.light {
+ border-right: 1px solid var(--arrow-c-mono5);
+ border-left: none;
+ margin-left: 0;
+ margin-right: var(--arrow-spacing-s);
+ }
+
+ .light.borderNone,
+ .dark.borderNone {
+ border-right: none;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Molecules/KeyEvents/stories.js b/app/isomorphic/arrow/components/Molecules/KeyEvents/stories.js
new file mode 100644
index 000000000..b147e84dc
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/KeyEvents/stories.js
@@ -0,0 +1,28 @@
+import React from "react";
+import Readme from "./README.md";
+import { withStore } from "../../../../storybook";
+import { generateStory } from "../../Fixture";
+import KeyEvents from "./index";
+import { color, text } from "@storybook/addon-knobs";
+
+const story = generateStory("live-blog");
+
+withStore(
+ "Molecules/Key Events",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Key Events", () => {
+ const config = {
+ theme: color("Color", "#ffffff"),
+ initialLoadCount: text("Initial Load Count", 4),
+ buttonText: text("Button text", "Load More"),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/README.md b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/README.md
new file mode 100644
index 000000000..dbbc5859f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/README.md
@@ -0,0 +1,25 @@
+# Page Introduction Card
+
+Page Introduction card is used to display the page information which consists of Page Title and Page Description.
+
+## Usage
+
+```jsx
+import { PageIntroductionCard } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+
+const config = {
+ pageTitle: "Page Title",
+ pageDescription: "This is the description of the page",
+ theme: #333333;,
+ showButton = false,
+ enableBorder = false,
+}
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/index.js b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/index.js
new file mode 100644
index 000000000..4c7723054
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/index.js
@@ -0,0 +1,51 @@
+import React from "react";
+import PropTypes from "prop-types";
+import upperCase from "lodash/upperCase";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import { getTextColor, navigateTo, generateNavigateSlug } from "../../../utils/utils";
+import "./page-intro-card.m.css";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+export const PageIntroductionCard = ({ config = {} }) => {
+ const {
+ pageTitle = "",
+ pageDescription = "",
+ theme = "#131922",
+ showButton = false,
+ enableBorder = false,
+ customUrlPath,
+ } = config;
+ const textColor = getTextColor(theme);
+ const borderBottom = enableBorder ? "borderBottom" : "";
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug({}, { ...qtConfig, config }, customUrlPath);
+
+ return (
+
+
+ {upperCase(pageTitle)}
+
+
+ {pageDescription}
+
+ {showButton && (
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ )}
+
+ );
+};
+
+PageIntroductionCard.propTypes = {
+ config: PropTypes.shape({ theme: PropTypes.string, pageTitle: PropTypes.string, pageDescription: PropTypes.string }),
+};
diff --git a/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/page-intro-card.m.css b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/page-intro-card.m.css
new file mode 100644
index 000000000..08aa5015d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/page-intro-card.m.css
@@ -0,0 +1,62 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value tablet from viewports;
+
+.author-page-wrapper:global(.full-width-with-padding) {
+ text-align: center;
+ padding-top: 40px;
+ padding-bottom: 32px;
+ @media (min-width: mobile) {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ @media (min-width: tablet) {
+ padding-right: 302px;
+ padding-left: 301px;
+ padding-top: 60px;
+ }
+ }
+}
+
+.pageTitle {
+ font-size: var(--arrow-fs-l);
+ color: var(--arrow-c-mono2);
+ padding-bottom: var(--arrow-spacing-xs);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-huge);
+ }
+}
+
+.borderBottom {
+ position: relative;
+ text-align: center;
+ padding-bottom: 34px;
+}
+
+.borderBottom:after {
+ content: "";
+ position: absolute;
+ left: 50%;
+ width: 52px;
+ margin: 0 0 0 -26px;
+ bottom: 24px;
+ border-bottom: 5px solid;
+}
+
+.pageDescription {
+ margin: 0 auto var(--arrow-spacing-l) 0;
+ font-size: var(--arrow-fs-xs);
+ line-height: var(--arrow-lh-5);
+ color: var(--arrow-c-mono2);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-s);
+ }
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
diff --git a/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/stories.js b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/stories.js
new file mode 100644
index 000000000..5505d2af0
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PageIntroductionCard/stories.js
@@ -0,0 +1,16 @@
+import React from "react";
+import { withStore } from "../../../../storybook";
+import { PageIntroductionCard } from "./index.js";
+import { color } from "@storybook/addon-knobs";
+import Readme from "./README.md";
+
+withStore("Molecules/Page Intro Card", Readme).add("Default", () => {
+ const config = {
+ pageTitle: "Page Title",
+ pageDescription: "This is the page description",
+ theme: color("Color", "#131922"),
+ enableBorder: true,
+ showButton: true,
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/README.md b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/README.md
new file mode 100644
index 000000000..f1c6e3cdf
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/README.md
@@ -0,0 +1,10 @@
+# Portrait Story Card
+
+The _PortraitStoryCard_ component is a basic [molecule](https://bradfrost.com/blog/post/atomic-web-design/#molecules) used to represent a story in portrait view within a Row.
+
+
+## Usage
+
+
+```jsx
+
diff --git a/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/index.js b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/index.js
new file mode 100644
index 000000000..a8f03f542
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/index.js
@@ -0,0 +1,49 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { StoryCard } from "../StoryCard";
+import { StorycardContent } from "../StorycardContent";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { isEmpty } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+
+import "./portrait-story-card.m.css";
+
+const PortraitStoryCard = ({
+ story,
+ border = "",
+ theme = "",
+ headerLevel = "6",
+ separator = "",
+ config = {},
+ borderColor = "",
+}) => {
+ if (isEmpty(story)) return null;
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+PortraitStoryCard.propTypes = {
+ border: PropTypes.string,
+ theme: PropTypes.string,
+ headerLevel: PropTypes.string,
+ separator: PropTypes.string,
+ borderColor: PropTypes.string,
+ config: PropTypes.object,
+ story: PropTypes.object,
+};
+
+export default StateProvider(PortraitStoryCard);
diff --git a/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/portrait-story-card.m.css b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/portrait-story-card.m.css
new file mode 100644
index 000000000..add11a0ff
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/portrait-story-card.m.css
@@ -0,0 +1,38 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ :global {
+ .arr--story-card > div:nth-child(2) {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: var(--arrow-spacing-xs);
+
+ @media (--viewport-medium) {
+ padding: var(--arrow-spacing-m);
+ }
+ }
+ .arr--headline > a > h6,
+ .author-name,
+ .arr--content-wrapper time,
+ .arr--read-time,
+ .section-tag {
+ color: var(--arrow-c-light);
+ }
+
+ .arr--hero-image {
+ margin-bottom: 0;
+ }
+
+ .arr--hero-image figure::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: linear-gradient(0deg, var(--arrow-c-mono3), transparent 70%, transparent);
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/stories.js b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/stories.js
new file mode 100644
index 000000000..4bbbe9e9f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/PortraitStoryCard/stories.js
@@ -0,0 +1,24 @@
+import React from "react";
+
+import Readme from "./README.md";
+import { generateStory } from "../../Fixture";
+import PortraitStoryCard from "./index";
+import { withStore } from "../../../../storybook";
+
+const story = generateStory();
+
+withStore(
+ "molecules/Portrait Story Card ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+
+ .add("Default", () => );
diff --git a/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/README.md b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/README.md
new file mode 100644
index 000000000..65ecf3102
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/README.md
@@ -0,0 +1,14 @@
+# SliderHorizontalCard(Row/Organisms)
+
+The _SliderHorizontalCard_ component is a basic [molecule](https://bradfrost.com/blog/post/atomic-web-design/#organisms) used to represent a molecule with atoms.
+
+Horizontal Slider Card is a horozontal card with same width for image and content.
+## Usage
+
+### Default SliderHorizontalCard
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/index.js b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/index.js
new file mode 100644
index 000000000..c8cd61dde
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/index.js
@@ -0,0 +1,52 @@
+import React from "react";
+import { Headline } from "../../Atoms/Headline/index";
+import { Subheadline } from "../../Atoms/Subheadline/index";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { HeroImage } from "../../Atoms/HeroImage/index";
+import { SectionTag } from "../../Atoms/SectionTag/index";
+import { getTextColor, isEmpty } from "../../../utils/utils";
+
+import PropTypes from "prop-types";
+
+import "./slider-horizontal-card.m.css";
+
+export const SliderHorizontalCard = ({
+ story,
+ border = "",
+ theme = "",
+ headerLevel,
+ borderColor = "",
+ config = {},
+}) => {
+ if (!story || isEmpty(story)) return
;
+ const borderStyle = border === "full" ? "border" : "";
+ const textColor = getTextColor(theme);
+ const { localizationConfig = {} } = config;
+ return (
+
+ );
+};
+
+SliderHorizontalCard.propTypes = {
+ story: PropTypes.object.isRequired,
+ border: PropTypes.string,
+ theme: PropTypes.string,
+ headerLevel: PropTypes.string,
+ borderColor: PropTypes.string,
+ config: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/slider-horizontal-card.m.css b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/slider-horizontal-card.m.css
new file mode 100644
index 000000000..c8623d3e2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/slider-horizontal-card.m.css
@@ -0,0 +1,42 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.horizontal {
+ display: flex;
+ flex-direction: column;
+ @media (--viewport-medium) {
+ display: grid;
+ flex-direction: column;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: 24px;
+ }
+}
+.content {
+ flex-basis: 50%;
+ @media (--viewport-medium) {
+ margin: var(--arrow-spacing-l) 40px var(--arrow-spacing-l) 0;
+ }
+}
+
+.border .content {
+ @media (--viewport-medium) {
+ margin: var(--arrow-spacing-l) 40px var(--arrow-spacing-l) 0;
+ }
+}
+
+html[dir="rtl"] {
+ .content,
+ .border .content {
+ @media (--viewport-medium) {
+ margin: var(--arrow-spacing-l) 0 var(--arrow-spacing-l) 40px;
+ }
+ }
+}
+
+.horizontal > :first-child {
+ @media (--viewport-medium) {
+ margin-bottom: 0;
+ }
+}
+.border {
+ border: 1px solid var(--arrow-c-mono5);
+}
diff --git a/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/stories.js b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/stories.js
new file mode 100644
index 000000000..e8374fd30
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SliderHorizontalCard/stories.js
@@ -0,0 +1,30 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { SliderHorizontalCard } from "./index";
+import Readme from "./README.md";
+import { color } from "@storybook/addon-knobs";
+import { withStore } from "../../../../storybook";
+
+const story = generateStory();
+
+const defaultvalue = "#3a9fdd";
+const sectionTagBorderColor = "Section Tag Border Color";
+
+withStore(
+ "molecules/Horizontal Slider Card ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .add("Default horizontal card for slider", () => (
+
+ ))
+ .add("Horizontal card for slider with empty story", () => (
+
+ ));
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/README.md b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/README.md
new file mode 100644
index 000000000..c08f42147
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/README.md
@@ -0,0 +1,118 @@
+# Social Share Template
+
+A template for social share on story page
+
+## Usage
+
+#### Social share template with horizontal share
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg"
+};
+
+ ;
+```
+
+#### Social share template with vertical share
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "plain-svg"
+};
+
+ ;
+```
+
+#### Social share template with plain svg
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "plain-svg"
+};
+
+ ;
+```
+
+#### Social share template with plain color svg
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "plain-color-svg"
+};
+
+ ;
+```
+
+#### Social share template with square svg
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "square-svg"
+};
+
+ ;
+```
+
+#### Social share template with circular svg
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "circular-svg"
+};
+
+ ;
+```
+
+#### Social share template with circular color svg
+
+```jsx
+const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ vertical: true
+ iconType: "circular-color-svg"
+};
+
+ ;
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/__snapshots__/social-share-template.test.js.snap b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/__snapshots__/social-share-template.test.js.snap
new file mode 100644
index 000000000..8c212afd6
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/__snapshots__/social-share-template.test.js.snap
@@ -0,0 +1,738 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Social Share Template Should not render facebook icon if url not passed 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url=""
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should not render linkedin icon if url not passed 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url=""
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should not render social share template if url is not passed 1`] = `""`;
+
+exports[`Social Share Template Should not render twitter icon if url not passed 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url=""
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should not render whatsapp icon if url not passed 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url=""
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should render horizontal social share template 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://www.facebook.com"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should render template if theme and icon is not passed 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
+
+exports[`Social Share Template Should render vertical social share template 1`] = `
+
+
+
+
+
+
+
+
+
+ }
+ key="facebook"
+ name="facebook"
+ text="Facebook"
+ url="https://www.facebook.com"
+ />
+
+ }
+ key="twitter"
+ name="twitter"
+ text="Twitter"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="linkedin"
+ name="linkedin"
+ text="LinkedIn"
+ url="https://twitter.com/"
+ />
+
+ }
+ key="whatsapp"
+ name="whatsapp"
+ text="Whatsapp"
+ url="https://twitter.com/"
+ />
+
+
+
+
+`;
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/index.js b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/index.js
new file mode 100644
index 000000000..6f0316f25
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/index.js
@@ -0,0 +1,120 @@
+import React, { useEffect, useState } from "react";
+import PropTypes from "prop-types";
+import { socialShareData } from "./social-share-data";
+import { SocialSharePopup } from "../../Atoms/SocialSharePopup";
+import "./social-share-template.m.css";
+import { CloseIcon } from "../../Svgs/close-icon";
+import { ShareIcon } from "../../Svgs/share-icon";
+import { getTextColor } from "../../../utils/utils";
+import camelcase from "lodash/camelCase";
+
+const ShareItem = ({ name, url, icon, dataTestId }) => {
+ return url ? (
+
+
+ {icon}
+
+
+ ) : null;
+};
+ShareItem.propTypes = {
+ name: PropTypes.string,
+ url: PropTypes.string,
+ icon: PropTypes.node,
+ dataTestId: PropTypes.string,
+};
+
+function getActionIcon(open, color) {
+ if (!open) return ;
+ return ;
+}
+
+export const SocialShareTemplate = (props) => {
+ const {
+ fbUrl = "",
+ twitterUrl = "",
+ linkedinUrl = "",
+ whatsappUrl = "",
+ vertical = false,
+ theme,
+ iconType = "plain-color-svg",
+ open = false,
+ } = props;
+ if (!fbUrl && !twitterUrl && !linkedinUrl && !whatsappUrl) return null;
+ const iconShade = getTextColor(theme);
+ const iconColor = iconShade === "dark" ? "#000000" : "#ffffff";
+ const options = { fbUrl, twitterUrl, linkedinUrl, whatsappUrl, iconColor, iconType };
+ const iconsList = socialShareData(options);
+ const [isOpen, setIsOpen] = useState(open);
+ const verticalShare = vertical ? "vertical" : "horizontal";
+
+ useEffect(() => setIsOpen(open), [open]);
+
+ const shareIcon = () => {
+ return (
+
+ {getActionIcon(isOpen, iconColor)}
+
+ );
+ };
+
+ const clickHandler = () => {
+ setIsOpen(!isOpen);
+ };
+
+ const popUpWrapper = () => {
+ return (
+
+ {!isOpen &&
}
+ {isOpen && (
+ <>
+
+
+ {
+ setIsOpen(false);
+ }}
+ />
+
+
+ >
+ )}
+
+ );
+ };
+ const popupStyle = !isOpen && vertical ? "tablet-view-popup" : "";
+ return (
+
+
+
+ {shareIcon()}
+ {iconsList.map((iconData, index) => (
+
+ ))}
+
+
+ {popUpWrapper()}
+
+ );
+};
+
+SocialShareTemplate.propTypes = {
+ fbUrl: PropTypes.string,
+ twitterUrl: PropTypes.string,
+ linkedinUrl: PropTypes.string,
+ whatsappUrl: PropTypes.string,
+ vertical: PropTypes.bool,
+ theme: PropTypes.string,
+ iconType: PropTypes.string,
+ open: PropTypes.boolean,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-data.js b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-data.js
new file mode 100644
index 000000000..d269c3c16
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-data.js
@@ -0,0 +1,66 @@
+import React from "react";
+import { Twitter } from "../../Svgs/SocialIcons/twitter";
+import { Facebook } from "../../Svgs/SocialIcons/facebook";
+import { LinkedIn } from "../../Svgs/SocialIcons/linkedin";
+import { WhatsApp } from "../../Svgs/SocialIcons/whatsapp";
+
+const getSocialIcons = (color, iconType) => {
+ switch (iconType) {
+ case "plain-color-svg":
+ return {
+ facebook: ,
+ twitter: ,
+ linkedin: ,
+ whatsapp: ,
+ };
+
+ default:
+ return {
+ facebook: ,
+ twitter: ,
+ linkedin: ,
+ whatsapp: ,
+ };
+ }
+};
+
+export const socialShareData = ({ fbUrl, twitterUrl, linkedinUrl, whatsappUrl, iconColor, iconType = "plain-svg" }) => {
+ const socialIcons = getSocialIcons(iconColor, iconType);
+ const getOptions = () => {
+ return [
+ {
+ name: "facebook",
+ url: fbUrl,
+ icon: socialIcons.facebook,
+ text: "Facebook",
+ bgColor: "#4e71a8",
+ alt: "fb icon",
+ },
+ {
+ name: "twitter",
+ url: twitterUrl,
+ icon: socialIcons.twitter,
+ text: "Twitter",
+ bgColor: "#1db7eb",
+ alt: "twitter icon",
+ },
+ {
+ name: "linkedin",
+ url: linkedinUrl,
+ icon: socialIcons.linkedin,
+ text: "LinkedIn",
+ bgColor: "#0077B5",
+ alt: "linkedin icon",
+ },
+ {
+ name: "whatsapp",
+ url: whatsappUrl,
+ icon: socialIcons.whatsapp,
+ text: "Whatsapp",
+ bgColor: "#25d366",
+ alt: "whatsapp share",
+ },
+ ];
+ };
+ return getOptions();
+};
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.m.css b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.m.css
new file mode 100644
index 000000000..4d4b62bf9
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.m.css
@@ -0,0 +1,174 @@
+@import "../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.desktop-view {
+ display: none;
+ @include desktop {
+ display: flex;
+ }
+}
+
+.tablet-view {
+ @include desktop {
+ display: none;
+ }
+}
+.wrapper {
+ list-style: none;
+ align-items: center;
+ transition: all 0.3s;
+ z-index: var(--z-index-1);
+ flex-direction: row-reverse;
+ overflow: hidden;
+}
+
+.horizontal .wrapper {
+ display: flex;
+ max-width: var(--arrow-fs-big);
+ @include desktop {
+ max-width: 120px;
+ }
+}
+
+.vertical .wrapper {
+ flex-direction: column;
+ border-radius: 50%;
+ max-height: var(--arrow-fs-big);
+ @include desktop {
+ position: inherit;
+ right: var(--arrow-spacing-l);
+ border-radius: unset;
+ max-height: 132px;
+ }
+}
+
+.light.vertical .wrapper {
+ background-color: var(--arrow-c-dark);
+ box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.17);
+ @include desktop {
+ background-color: inherit;
+ box-shadow: unset;
+ }
+}
+
+.dark.vertical .wrapper {
+ background-color: var(--arrow-c-light);
+ box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.17);
+ @include desktop {
+ background-color: inherit;
+ box-shadow: unset;
+ }
+}
+
+.icon {
+ padding: var(--arrow-spacing-xs);
+}
+
+.wrapper.open {
+ @include desktop {
+ border-radius: var(--arrow-spacing-xxs);
+ }
+}
+
+.light .wrapper.open {
+ @include desktop {
+ border: solid 0.6px var(--arrow-c-invert-mono7);
+ }
+}
+
+.dark .wrapper.open {
+ @include desktop {
+ border: solid 0.6px var(--arrow-c-mono7);
+ }
+}
+
+.horizontal .wrapper.open {
+ @include desktop {
+ max-width: 240px;
+ }
+}
+
+.vertical .wrapper.open {
+ @include desktop {
+ max-height: 240px;
+ }
+}
+
+.icon:hover {
+ @include tablet {
+ background-color: var(--arrow-c-mono7);
+ border-radius: 50%;
+ }
+}
+
+.popup-wrapper {
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ width: 100%;
+ @include tablet {
+ position: absolute;
+ width: 328px;
+ }
+}
+
+.popup-wrapper.popup-open {
+ @include mobile {
+ transition: transform 0.6s;
+ }
+ @include tablet {
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ -ms-transform: translateX(-50%) translateY(-50%);
+ -webkit-transform: translate(-50%, -50%);
+ }
+}
+
+html[dir="rtl"] {
+ .popup-wrapper.popup-open {
+ @include tablet {
+ right: 50%;
+ transform: translate(50%, -50%);
+ -ms-transform: translateX(50%) translateY(-50%);
+ -webkit-transform: translate(50%, -50%);
+ }
+ }
+}
+
+.overlay {
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1100;
+ overflow-y: scroll;
+ position: fixed;
+ @include desktop {
+ position: initial;
+ }
+}
+
+.light .overlay {
+ background-color: rgba(13, 13, 13, 0.77);
+}
+
+.dark .overlay {
+ background-color: rgba(255, 255, 255, 0.77);
+}
+
+.tablet-view-popup:global(.arr--share) {
+ position: sticky;
+ position: -webkit-sticky;
+ bottom: 56px;
+ height: 0;
+ text-align: center;
+ @include tablet {
+ right: 24px;
+ @include desktop {
+ bottom: unset;
+ height: unset;
+ text-align: unset;
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.test.js b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.test.js
new file mode 100644
index 000000000..97356efbe
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/social-share-template.test.js
@@ -0,0 +1,116 @@
+import * as React from "react";
+import { shallow } from "enzyme";
+import { SocialShareTemplate } from "./index";
+
+describe("Social Share Template", () => {
+ it("Should render horizontal social share template", () => {
+ const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should render vertical social share template", () => {
+ const data = {
+ fbUrl: "https://www.facebook.com",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should not render social share template if url is not passed", () => {
+ const data = {
+ fbUrl: "",
+ twitterUrl: "",
+ linkedinUrl: "",
+ whatsappUrl: "",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should not render facebook icon if url not passed", () => {
+ const data = {
+ fbUrl: "",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should not render twitter icon if url not passed", () => {
+ const data = {
+ fbUrl: "https://twitter.com/",
+ twitterUrl: "",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should not render whatsapp icon if url not passed", () => {
+ const data = {
+ fbUrl: "https://twitter.com/",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should not render linkedin icon if url not passed", () => {
+ const data = {
+ fbUrl: "https://twitter.com/",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "",
+ whatsappUrl: "https://twitter.com/",
+ theme: "#ffff",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should render template if theme and icon is not passed", () => {
+ const data = {
+ fbUrl: "https://twitter.com/",
+ twitterUrl: "https://twitter.com/",
+ linkedinUrl: "https://twitter.com/",
+ whatsappUrl: "https://twitter.com/",
+ theme: "",
+ iconType: "plain-svg",
+ vertical: true,
+ };
+ const wrapper = shallow( );
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/stories.js b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/stories.js
new file mode 100644
index 000000000..9d4f3b675
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/SocialShareTemplate/stories.js
@@ -0,0 +1,40 @@
+import React from "react";
+import { withStore } from "../../../../storybook";
+import { SocialShareTemplate } from "./index";
+import Readme from "./README.md";
+import { boolean } from "@storybook/addon-knobs";
+
+withStore("Molecules/Social Share Template", {}, Readme)
+ .add("Horizontal", () => {
+ const props = {
+ fbUrl: "https://www.facebook.com",
+ fullUrl:
+ "https://ace-web.qtstage.io/anything/recent-stories/newsready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ iconType: "plain-svg",
+ linkedinUrl: "https://www.linkedin.com",
+ mailtoUrl:
+ "mailto:?subject=Ready%20Player%20One%20review%20%E2%80%93%20Spielberg%C2%A0&body=https%3A%2F%2Face-web.qtstage.io%2Fanything%2Frecent-stories%2Fnews%2Fready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ publisherUrl: undefined,
+ theme: "#ffffff",
+ title: "Ready Player One review – Spielberg ",
+ twitterUrl: "https://twitter.com",
+ };
+ return ;
+ })
+ .add("Vertical", () => {
+ const props = {
+ fbUrl: "https://www.facebook.com",
+ fullUrl:
+ "https://ace-web.qtstage.io/anything/recent-stories/newsready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ iconType: "plain-svg",
+ linkedinUrl: "https://www.linkedin.com",
+ mailtoUrl:
+ "mailto:?subject=Ready%20Player%20One%20review%20%E2%80%93%20Spielberg%C2%A0&body=https%3A%2F%2Face-web.qtstage.io%2Fanything%2Frecent-stories%2Fnews%2Fready-player-one-review-spielberg-spins-a-dizzying-vr-yarn",
+ publisherUrl: undefined,
+ theme: "#ffffff",
+ title: "Ready Player One review – Spielberg ",
+ twitterUrl: "https://twitter.com",
+ vertical: true,
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/README.md b/app/isomorphic/arrow/components/Molecules/StoryCard/README.md
new file mode 100644
index 000000000..6ff2e77c4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/README.md
@@ -0,0 +1,75 @@
+# StoryCard
+
+The _StoryCard_ component is a basic [molecule](https://bradfrost.com/blog/post/atomic-web-design/#molecules) used to represent a story within a Row.
+
+The _StoryCard_ can be composible based on it's children(Atoms) or using StoryCardContent component(molecule).
+
+## Usage
+
+### Default Story Card
+
+```jsx
+
+```
+
+### Horizontal Story Card
+
+```jsx
+
+```
+
+### Story Card w/ Image & Headline
+
+```jsx
+
+
+
+
+```
+
+### Story Card w/ Image & Timestamp
+
+```jsx
+
+
+
+
+```
+
+### Story Card w/ Headline, Author & Subheadline
+
+```jsx
+
+
+
+
+
+```
+
+### Story Card w/ Image, Headline, Author+Timestamp & Subheadline
+
+```jsx
+
+
+
+
+
+
+```
+
+### Story Card using Story content
+
+```jsx
+
+
+
+
+```
+
+### Story Card using aspect ratio
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBGIMAGE.md b/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBGIMAGE.md
new file mode 100644
index 000000000..857bc79c1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBGIMAGE.md
@@ -0,0 +1,25 @@
+# StoryCard with background image
+
+The _StoryCard_ component with background image used to represent a story within a Row.
+
+The _StoryCard_ can be composible based on it's children(Atoms, molecule).
+
+### Story Card with background image
+
+```jsx
+
+
+
+
+```
+
+### Story Card with background image and overlap with content
+
+```jsx
+
+
+
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBORDER.md b/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBORDER.md
new file mode 100644
index 000000000..a853a14bb
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/READMEBORDER.md
@@ -0,0 +1,61 @@
+# StoryCard
+
+The _StoryCard_ component with different kinds of borders and box shadow modules used to represent a story within a Row with borders.
+
+The _StoryCard_ can be composible based on it's children(Atoms).
+
+### Default Story Card with Full Border
+
+```jsx
+
+```
+
+### Default Story Card with Border Bottom
+
+```jsx
+
+```
+
+### Default Story Card with Box Shadow
+
+```jsx
+
+```
+
+### StoryCard using StoryCardContent
+
+```jsx
+
+
+
+
+
+
+
+```
+
+### StoryCard using StoryCardContent border Box and fullBleed False
+
+```jsx
+
+
+
+
+
+
+
+```
+
+### Horizontal StoryCard using StoryCardContent border full
+
+```jsx
+
+
+
+
+
+
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/index.js b/app/isomorphic/arrow/components/Molecules/StoryCard/index.js
new file mode 100644
index 000000000..d539ed78c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/index.js
@@ -0,0 +1,185 @@
+import React from "react";
+import { Headline } from "../../Atoms/Headline/index";
+import { Subheadline } from "../../Atoms/Subheadline/index";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { HeroImage } from "../../Atoms/HeroImage/index";
+import { SectionTag } from "../../Atoms/SectionTag/index";
+import { getTextColor, isEmpty, rgbToHex } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+
+import PropTypes from "prop-types";
+
+import "./storycard.m.css";
+
+const StoryCardBase = ({
+ story,
+ children,
+ isHorizontal,
+ aspectRatio,
+ border,
+ useImageAsBackground,
+ bgImgContentOverlap,
+ theme,
+ borderColor,
+ isHorizontalMobile,
+ centerAlign,
+ headerLevel,
+ hideAuthorImage,
+ prefix,
+ config,
+ isHorizontalWithImageLast,
+}) => {
+ if (!story || isEmpty(story)) return
;
+ const borderOptions = ["default", "full", "bottom", "boxShadow"];
+ const borderTemplate = borderOptions.includes(border) ? `border-${border}` : "";
+ // TODO optimize conditions
+
+ let horizontalCardStyle = "default-card";
+ if (isHorizontal) horizontalCardStyle = "horizontal-card";
+ else if (isHorizontalWithImageLast) horizontalCardStyle = "horizontal-image-last";
+
+ const bgImageClasses = useImageAsBackground ? "backgroung-img" : "";
+ const bgImgContentOverlapClass = bgImgContentOverlap ? "content-overlap" : "";
+ const textColor = getTextColor(theme);
+ const horizontalMobileClasses = isHorizontalMobile ? "horizontal-mob" : "";
+
+ const defaultStorycard = (
+
+ );
+ return (
+
+ {children || defaultStorycard}
+
+ );
+};
+
+const DefaultStoryCard = ({
+ story,
+ border,
+ isHorizontal,
+ aspectRatio,
+ headerLevel,
+ isHorizontalMobile = false,
+ centerAlign = false,
+ borderColor,
+ hideAuthorImage,
+ prefix,
+ isHorizontalWithImageLast,
+ config,
+}) => {
+ const alignment = centerAlign ? "center-align" : "";
+ const SectionTagborderColor = rgbToHex(borderColor);
+ const { localizationConfig = {} } = config;
+
+ return (
+ <>
+ {isHorizontal ? (
+
+ ) : isHorizontalWithImageLast ? (
+
+ ) : (
+
+ )}
+
+ >
+ );
+};
+
+DefaultStoryCard.propTypes = {
+ story: PropTypes.object.isRequired,
+ border: PropTypes.oneOfType([PropTypes.string]),
+ isBottom: PropTypes.bool,
+ isHorizontal: PropTypes.bool,
+ aspectRatio: PropTypes.oneOfType([PropTypes.array]),
+ isHorizontalMobile: PropTypes.bool,
+ centerAlign: PropTypes.bool,
+ headerLevel: PropTypes.string,
+ borderColor: PropTypes.string,
+ hideAuthorImage: PropTypes.bool,
+ prefix: PropTypes.string,
+ config: PropTypes.object,
+ isHorizontalWithImageLast: PropTypes.bool,
+};
+
+StoryCardBase.propTypes = {
+ /** The Story Object from the API response */
+ story: PropTypes.object.isRequired,
+ children: PropTypes.node,
+ /** Flip the Story Card to Horizonal mode */
+ isHorizontal: PropTypes.bool,
+ /** Add border to the Story Card */
+ border: PropTypes.oneOfType([PropTypes.string]),
+ // story card with background image
+ useImageAsBackground: PropTypes.bool,
+ // story card content overlap with image
+ bgImgContentOverlap: PropTypes.bool,
+ /** add aspect ratio for hero image */
+ aspectRatio: PropTypes.oneOfType([PropTypes.array]),
+ /** add theme(dark /light/custom color) for storycard */
+ theme: PropTypes.string,
+ /** flip the story card mobile design horizontal */
+ isHorizontalMobile: PropTypes.bool,
+ // make content center align in desktop and left align in mobile
+ centerAlign: PropTypes.bool,
+ headerLevel: PropTypes.string,
+ borderColor: PropTypes.string,
+ // handle author image through storycard
+ hideAuthorImage: PropTypes.bool,
+ // handle author prefix through storycard
+ prefix: PropTypes.string,
+ config: PropTypes.object,
+ isHorizontalWithImageLast: PropTypes.bool,
+};
+
+StoryCardBase.defaultProps = {
+ isHorizontal: false,
+ isHorizontalMobile: false,
+ border: "default",
+ theme: "",
+ centerAlign: false,
+ borderColor: "",
+ isHorizontalWithImageLast: false,
+};
+
+export const StoryCard = StateProvider(StoryCardBase);
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/stories.js b/app/isomorphic/arrow/components/Molecules/StoryCard/stories.js
new file mode 100644
index 000000000..910faa68f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/stories.js
@@ -0,0 +1,223 @@
+import { StoryCard } from "./index";
+
+import { Headline } from "../../Atoms/Headline/index";
+import { HeroImage } from "../../Atoms/HeroImage/index";
+import { TimeStamp } from "../../Atoms/TimeStamp/index";
+import { Author } from "../../Atoms/Author/index";
+import { Subheadline } from "../../Atoms/Subheadline/index";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { StorycardContent } from "../StorycardContent/index";
+
+import React from "react";
+import { generateStory } from "../../Fixture";
+
+import { optionalSelect, withStore } from "../../../../storybook";
+import Readme from "./README.md";
+import ReadmeForBorder from "./READMEBORDER.md";
+import ReadmeForBgImage from "./READMEBGIMAGE.md";
+import { color } from "@storybook/addon-knobs";
+import "./storycard.m.css";
+
+const story = generateStory();
+
+const aspectRatioOptions = {
+ "[16,9]": [16, 9],
+ "[8,3]": [8, 3],
+ "[1,1]": [1, 1],
+ "[4, 3]": [4, 3],
+ "[2, 3]": [2, 3],
+};
+const label = "color";
+const defaultvalue = "#929292";
+const sectionTagBorderColor = "Section Tag Border Color";
+const defaultBorderColor = "#3a9fdd";
+const config = {};
+withStore(
+ "Molecules/StoryCard",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default Story Card", () => (
+
+ ))
+ .add("Default with theme", () => (
+
+ ))
+ .add("Default with mobile horizontal design", () => (
+
+ ))
+ .add("Horizontal Story Card", () => (
+
+ ))
+ .add("Story Card w/ Image & Headline", () => (
+
+
+
+
+ ))
+ .add("Story Card w/ Image & Timestamp", () => (
+
+
+
+
+ ))
+ .add("Story Card w/ Headline, Author & Subheadline", () => (
+
+ ))
+
+ .add("Story Card w/ Image, Headline & Subheadline Author+Timestamp ", () => (
+
+
+
+
+
+
+ ))
+ .add("Story Card w/o Image, Headline, Author+Timestamp & Subheadline", () => (
+
+
+
+
+ ))
+ .add("Story Card using Story content", () => (
+
+
+
+
+ ))
+ .add("Story card with aspect ratio", () => (
+
+ ))
+ .add("Story card with story object as null", () => )
+ .add("Story card with an empty story object", () => );
+
+withStore(
+ "Molecules/StoryCard(with Border)",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ ReadmeForBorder
+)
+ .addDecorator((story) => {story()}
)
+ .add("without border", () => (
+
+ ))
+ .add("Default Story Card with Full Border", () => (
+
+ ))
+ .add("Default Story Card with Border Bottom", () => (
+
+ ))
+ .add("Default Story Card with Box Shadow", () => (
+
+ ))
+ .add("StoryCard using StoryCardContent ", () => (
+
+
+
+
+
+
+
+ ))
+ .add("StoryCard using StoryCardContent border Box and fullBleed False", () => (
+
+
+
+
+
+
+
+ ))
+ .add("Horizontal StoryCard using StoryCardContent border full ", () => (
+
+
+
+
+
+
+
+ ));
+
+withStore(
+ "Molecules/StoryCard(with background image)",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ ReadmeForBgImage
+)
+ .addDecorator((story) => {story()}
)
+ .add("Story Card with background image", () => (
+
+
+
+
+ ))
+ .add("Story Card with background image and overlap with content", () => (
+
+
+
+
+ ));
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCard/storycard.m.css b/app/isomorphic/arrow/components/Molecules/StoryCard/storycard.m.css
new file mode 100644
index 000000000..f75ac4d98
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCard/storycard.m.css
@@ -0,0 +1,158 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.author-timestamp-wrapper {
+ display: flex;
+ align-items: center;
+}
+.default-card {
+ display: flex;
+ flex-direction: column;
+}
+.horizontal-card {
+ display: flex;
+ flex-direction: row;
+}
+
+.backgroung-img > a > figure::after {
+ @media (--viewport-medium) {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: linear-gradient(0deg, rgba(0, 0, 0, 0.7), transparent 50%, transparent);
+ }
+}
+.backgroung-img > div:nth-child(2) {
+ @media (--viewport-medium) {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-m) 48px;
+ position: absolute;
+ bottom: 0;
+ left: 15%;
+ right: 15%;
+ }
+}
+.backgroung-img img {
+ position: absolute;
+ bottom: 0;
+}
+
+.content-overlap > div:nth-child(2) {
+ @media (--viewport-medium) {
+ margin: 0 var(--arrow-spacing-m);
+ width: calc(100% - 32px);
+ background-color: var(--arrow-c-light);
+ padding: var(--arrow-spacing-m);
+ position: absolute;
+ bottom: 0;
+ }
+}
+.content-overlap img {
+ @media (--viewport-medium) {
+ position: absolute;
+ bottom: 0;
+ }
+}
+
+/* TODO: Refactor targetting of child */
+.horizontal-card > div:first-child {
+ flex-basis: 30%;
+}
+
+.horizontal-card > div:nth-child(2) {
+ flex-basis: 70%;
+}
+
+.border-full.light {
+ border: 1px solid var(--light-border);
+}
+
+.border-full.dark {
+ border: 1px solid var(--dark-border);
+}
+
+.border-bottom.light {
+ border-bottom: 1px solid var(--light-border);
+ padding-bottom: var(--arrow-spacing-m);
+}
+
+.border-bottom.dark {
+ border-bottom: 1px solid var(--dark-border);
+ padding-bottom: var(--arrow-spacing-m);
+}
+
+.border-boxShadow {
+ box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
+}
+.center-align {
+ display: flex;
+ flex-direction: column;
+ align-items: left;
+ @media (--viewport-medium) {
+ align-items: center;
+ text-align: center;
+ }
+}
+
+.content-bottom {
+ /* padding: 0 0 0 8px; */
+}
+.content-full,
+.content-boxShadow {
+ padding: var(--arrow-spacing-xs) var(--arrow-spacing-m);
+}
+.backgroung-img,
+.content-overlap {
+ position: relative;
+}
+
+.hero-image {
+ margin-bottom: var(--arrow-spacing-xs);
+ padding-right: 0;
+}
+.right-padding {
+ padding-bottom: 0;
+ padding-right: var(--arrow-spacing-xs);
+}
+
+.horizontal-mob {
+ display: flex;
+ flex-direction: row;
+ @media (--viewport-medium) {
+ flex-direction: column;
+ }
+}
+.horizontal-mob .hero-image {
+ flex-basis: 30%;
+ @media (--viewport-medium) {
+ flex-basis: 100%;
+ }
+}
+.horizontal-mob .content {
+ flex-basis: 70%;
+ @media (--viewport-medium) {
+ flex-basis: 100%;
+ }
+}
+
+.border-default {
+ border: none;
+ height: 100%;
+}
+.card {
+ height: 100%;
+}
+
+.horizontal-image-last {
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.horizontal-image-last > div:first-child {
+ flex-basis: 30%;
+}
+
+.horizontal-image-last > div:nth-child(2) {
+ flex-basis: 70%;
+}
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/README.md b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/README.md
new file mode 100644
index 000000000..c029a9b08
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/README.md
@@ -0,0 +1,9 @@
+# Story Card With Bullet Point
+
+Modified Story Card, with numbered bullet point
+
+## Usage
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/index.js b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/index.js
new file mode 100644
index 000000000..8282247c5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/index.js
@@ -0,0 +1,32 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { Headline } from "../../Atoms/Headline";
+import { BulletPoint } from "../../Atoms/BulletPoint";
+import "./story-card-with-bullet-point.m.css";
+
+export const StoryCardWithBulletPoint = ({ story, config, bulletValue }) => {
+ const { theme = "", localizationConfig = {} } = config;
+ return (
+
+ );
+};
+
+StoryCardWithBulletPoint.propTypes = {
+ story: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ localizationConfig: PropTypes.object,
+ }),
+ bulletValue: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/stories.js b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/stories.js
new file mode 100644
index 000000000..06fa0ec92
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/stories.js
@@ -0,0 +1,31 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { StoryCardWithBulletPoint } from "./index";
+import Readme from "./README.md";
+import { color, boolean } from "@storybook/addon-knobs";
+import { withStore } from "../../../../storybook";
+
+const story = generateStory();
+
+withStore(
+ "molecules/Story Card With Bullet Point ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const contextConfig = {
+ theme: color("color", "#ffffff"),
+ showSection: boolean("Show Section", true),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Show Time", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/story-card-with-bullet-point.m.css b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/story-card-with-bullet-point.m.css
new file mode 100644
index 000000000..a9c410ad8
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryCardWithBulletPoint/story-card-with-bullet-point.m.css
@@ -0,0 +1,7 @@
+.card-with-bullet-wrapper {
+ display: flex;
+}
+
+.card-with-bullet-wrapper :global(.arrow-component.arr--headline h6) {
+ font-weight: normal;
+}
diff --git a/app/isomorphic/arrow/components/Molecules/StoryElementCard/README.md b/app/isomorphic/arrow/components/Molecules/StoryElementCard/README.md
new file mode 100644
index 000000000..8b00d5c2d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryElementCard/README.md
@@ -0,0 +1,9 @@
+# Story Element Card
+
+Story Element Card is used to render story elements of a story card.
+
+## Usage
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Molecules/StoryElementCard/index.js b/app/isomorphic/arrow/components/Molecules/StoryElementCard/index.js
new file mode 100644
index 000000000..5404be499
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryElementCard/index.js
@@ -0,0 +1,361 @@
+/* eslint-disable no-case-declarations */
+import React from "react";
+import get from "lodash/get";
+import { Text } from "../../Atoms/StoryElements/Text";
+import { Blurb } from "../../Atoms/StoryElements/Blurb";
+import "./story-element-card.m.css";
+import { Summary } from "../../Atoms/StoryElements/Summary";
+import { BlockQuote } from "../../Atoms/StoryElements/BlockQuote";
+import { BigFact } from "../../Atoms/StoryElements/BigFact";
+import { AlsoRead } from "../../Atoms/StoryElements/AlsoRead";
+import { Image } from "../../Atoms/StoryElements/Image";
+import PropTypes from "prop-types";
+import { QuestionAnswer } from "../../Atoms/StoryElements/QuestionAnswer";
+import { ImageGallery } from "../../Atoms/StoryElements/imageGallery";
+import { StoryElement } from "../../Atoms/StoryElements/StoryElement";
+import { Video } from "../../Atoms/StoryElements/Video";
+import { Quote } from "../../Atoms/StoryElements/Quote";
+import { Reference } from "../../Atoms/StoryElements/Reference";
+import { CaptionAttribution } from "../../Atoms/CaptionAttribution";
+import { Attachment } from "../../Atoms/StoryElements/Attachment";
+import { SocialShare } from "@quintype/components";
+import { SocialShareTemplate } from "../../Molecules/SocialShareTemplate";
+import { facebookMobileVideoResizeFix, getTextColor } from "../../../utils/utils";
+import { ImageSlideshow } from "../../Atoms/StoryElements/ImageSlideshow";
+
+const getElementType = (element) => element.subtype || element.type || "";
+const getElement = (story, element, config = {}, AdComponent, WidgetComp, index, cardId) => {
+ const {
+ text = {},
+ summary = {},
+ blurb = {},
+ blockquote = {},
+ quote = {},
+ "also-read": alsoRead = {},
+ "q-and-a": qaElement = {},
+ question: questionElement = {},
+ answer: answerElement = {},
+ references = {},
+ } = config;
+
+ let elementType = getElementType(element);
+
+ if (elementType === "image-gallery") {
+ elementType = element.metadata.type;
+ }
+
+ const storyElementId = get(element, ["id"]);
+
+ switch (elementType) {
+ case "ad":
+ return ;
+
+ case "widget":
+ return ;
+
+ case "text":
+ case "cta":
+ const { css: { textColor: textElementColor = "#000", hyperlinkColor = "#2f81cd" } = {} } = text;
+ return ;
+
+ case "summary":
+ const {
+ template: summaryTemplate = "",
+ css: { headerBgColor = "" } = {},
+ opts: { headline = "", hideHeadline = false } = {},
+ } = summary;
+ return (
+
+ );
+
+ case "blurb":
+ const { template: blurbTemplate = "default", css: { borderColor = "#2f81cd" } = {} } = blurb;
+ return ;
+
+ case "blockquote":
+ const {
+ template: blockQuoteTemplate = "default",
+ css: { iconType = "edgeIcon", blockQuoteColor = "#2f81cd", backgroundShade = "" } = {},
+ } = blockquote;
+ return (
+
+ );
+
+ case "quote":
+ const { template: quoteTemplate = "borderNone", css: { borderColor: quoteBorderColor = "#2f81cd" } = {} } = quote;
+ return
;
+
+ case "bigfact":
+ return ;
+
+ case "also-read":
+ const {
+ template: alsoReadTemplate = "default",
+ css: { textColor = "#000" } = {},
+ opts: { title = "" } = {},
+ } = alsoRead;
+ return (
+
+ );
+
+ case "image":
+ return ;
+
+ case "q-and-a":
+ const {
+ template: qaTemplate = "default",
+ css: { iconColor = "" } = {},
+ opts: { defaultIconType = "edge" } = {},
+ } = qaElement;
+ return (
+
+ );
+
+ case "question":
+ const {
+ template: questionTemplate = "default",
+ css: { questionIconColor = "" } = {},
+ opts: { defaultQuestionIconType = "edge" } = {},
+ } = questionElement;
+ return (
+
+ );
+
+ case "answer":
+ const {
+ template: answerTemplate = "default",
+ css: { answerIconColor = "" } = {},
+ opts: { defaultAnswerIconType = "edge" } = {},
+ } = answerElement;
+ return (
+
+ );
+
+ case "gallery":
+ return (
+
+
+
+ );
+
+ case "slideshow":
+ return (
+
+
+
+ );
+
+ case "youtube-video":
+ case "dailymotion-video":
+ return (
+
+
+
+ );
+ case "attachment":
+ return ;
+ case "tweet":
+ case "jsembed":
+ return (
+
+
+
+ );
+ case "references":
+ const { opts: { showHeadline = true, headlineText = "" } = {} } = references;
+ return (
+
+ );
+ default:
+ return (
+
+
+
+ );
+ }
+};
+
+const MainImageWrapper = ({ imageElement, story, children, config, card }) => {
+ return (
+ <>
+
+ >
+ );
+};
+
+MainImageWrapper.propTypes = {
+ story: PropTypes.object,
+ imageElement: PropTypes.object,
+ config: PropTypes.object,
+ children: PropTypes.node,
+ card: PropTypes.shape({
+ id: PropTypes.string,
+ }),
+};
+
+export const StoryElementCard = ({
+ story,
+ card,
+ heroVideoElementId = -1,
+ config,
+ isLive,
+ theme,
+ adComponent,
+ widgetComp,
+}) => {
+ const textColor = getTextColor(theme);
+ const isLiveBlog = isLive ? "live-blog" : "";
+ let shouldRunFBMobileVideoFix = true;
+
+ return (
+
+ {get(card, ["story-elements"], []).map((element, index) => {
+ if (element.id === heroVideoElementId) return false;
+ if (element.subtype && element.subtype === "facebook-post" && shouldRunFBMobileVideoFix) {
+ facebookMobileVideoResizeFix();
+ shouldRunFBMobileVideoFix = false;
+ }
+ const tableStyle = element && element.subtype === "table" ? "table" : "";
+ const cardId = get(card, ["id"], "");
+ return (
+
+ {getElement(story, element, config, adComponent, widgetComp, index, cardId)}
+
+ );
+ })}
+
+ );
+};
+StoryElementCard.propTypes = {
+ story: PropTypes.object,
+ card: PropTypes.array,
+ heroVideoElementId: PropTypes.number,
+ config: PropTypes.object,
+ isLive: PropTypes.bool,
+ theme: PropTypes.string,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export const PhotoStoryElement = ({ card = {}, config, story, adComponent, widgetComp }) => {
+ let shouldRunFBMobileVideoFix = true;
+ return (
+
+ {get(card, ["story-elements"], []).map((element, index) => {
+ const cardId = get(card, ["id"], "");
+ if (element.type === "image") {
+ return
;
+ } else {
+ if (element.subtype && element.subtype === "facebook-post" && shouldRunFBMobileVideoFix) {
+ facebookMobileVideoResizeFix();
+ shouldRunFBMobileVideoFix = false;
+ }
+ return (
+
+ {getElement(story, element, config, adComponent, widgetComp, index, cardId)}
+
+ );
+ }
+ })}
+
+ );
+};
+
+PhotoStoryElement.propTypes = {
+ story: PropTypes.object,
+ card: PropTypes.object,
+ config: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export const SlotAfterStory = ({ id = "", element = {}, AdComponent, WidgetComp }) => {
+ switch (element.type) {
+ case "ad":
+ return ;
+ case "widget":
+ return ;
+ default:
+ return null;
+ }
+};
+
+SlotAfterStory.propTypes = {
+ id: PropTypes.string,
+ element: PropTypes.object,
+ AdComponent: PropTypes.func,
+ WidgetComp: PropTypes.func,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/StoryElementCard/stories.js b/app/isomorphic/arrow/components/Molecules/StoryElementCard/stories.js
new file mode 100644
index 000000000..c260bbedd
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryElementCard/stories.js
@@ -0,0 +1,27 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { withStore } from "../../../../storybook";
+import Readme from "./README.md";
+import { StoryElementCard } from "./index";
+import { color } from "@storybook/addon-knobs";
+
+const story = generateStory();
+const { cards } = story;
+
+withStore(
+ "Molecules/Story Element Card",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const config = {
+ theme: color("Background Color", "#fff"),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Molecules/StoryElementCard/story-element-card.m.css b/app/isomorphic/arrow/components/Molecules/StoryElementCard/story-element-card.m.css
new file mode 100644
index 000000000..2d7c639fd
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StoryElementCard/story-element-card.m.css
@@ -0,0 +1,78 @@
+@import "../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.element-container {
+ margin: var(--arrow-spacing-l) 0;
+ @include tablet {
+ margin: var(--arrow-spacing-xl) 0;
+ }
+}
+
+.live-blog {
+ margin: var(--arrow-spacing-m) 0 var(--arrow-spacing-l);
+ @include tablet {
+ margin: var(--arrow-spacing-m) 0 var(--arrow-spacing-xl);
+ }
+}
+
+.image-share {
+ display: flex;
+ justify-content: space-between;
+ padding: var(--arrow-spacing-xs) 0 var(--arrow-spacing-l);
+}
+.image-share :global(.arr--caption-attribution) {
+ margin: unset;
+}
+
+.image-share :global(.desktop-share-wrapper) {
+ @include desktop {
+ max-width: 42px;
+ }
+}
+
+.story-card {
+ border-bottom: 1px solid var(--arrow-c-mono5);
+ margin-bottom: var(--arrow-spacing-xl);
+}
+
+.element-container :global(.story-element-jsembed) iframe {
+ max-width: 100%;
+}
+
+.element-container :global(.story-element-jsembed) img {
+ width: fit-content;
+}
+
+.element-container :global(.story-element-jsembed-dailymotion-video) iframe {
+ width: 100%;
+ height: 100%;
+}
+
+:global(.story-element-jsembed.story-element-jsembed-facebook-post *) {
+ @include mobile {
+ max-width: 100%;
+ position: relative;
+ }
+}
+
+:global(.story-element-jsembed.story-element-jsembed-facebook-post) {
+ @include mobile {
+ max-width: calc(100vw - var(--arrow-spacing-l));
+ }
+}
+
+.table :global(.story-element-data-table) {
+ max-width: calc(100vw - 24px);
+ @include tablet {
+ max-width: calc(calc(calc(1024px / 12) * 8) - 24px);
+ @include desktop {
+ max-width: calc(calc(calc(1025px / 12) * 8) - 24px);
+ }
+ }
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
diff --git a/app/isomorphic/arrow/components/Molecules/StorycardContent/README.md b/app/isomorphic/arrow/components/Molecules/StorycardContent/README.md
new file mode 100644
index 000000000..8b1625835
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StorycardContent/README.md
@@ -0,0 +1,34 @@
+# StoryCard Content
+
+The _StoryCardContent_ component is a basic [molecule] used to represent a storyCard content within a Row.
+
+The _StoryCardContent_ can be composible based on it's children(Atoms).
+
+## Usage
+
+### Default Story Card
+
+```jsx
+
+```
+
+### Story Card with Headline and Timestamp
+
+```jsx
+
+
+
+
+```
+
+### Story Card with Headline, Author with Timestamp and Subheadline
+
+```jsx
+
+
+
+
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Molecules/StorycardContent/index.js b/app/isomorphic/arrow/components/Molecules/StorycardContent/index.js
new file mode 100644
index 000000000..3f0a2cd4e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StorycardContent/index.js
@@ -0,0 +1,77 @@
+import React from "react";
+import { Headline } from "../../Atoms/Headline";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { getTextColor, rgbToHex } from "../../../utils/utils";
+
+import PropTypes from "prop-types";
+
+import "./storycardContent.m.css";
+
+export const StorycardContent = ({
+ story,
+ children,
+ theme,
+ border,
+ isHorizontalMobile,
+ headerLevel,
+ borderColor,
+ config,
+}) => {
+ const textColor = getTextColor(theme);
+ const borderOptions = ["full"];
+ const SectionTagborderColor = rgbToHex(borderColor);
+ const borderTemplate = borderOptions.includes(border) ? `border-${border}` : "";
+ const horizontalCard = isHorizontalMobile ? "horizontal-content-wrapper" : "";
+ return (
+
+ {children || (
+
+ )}
+
+ );
+};
+
+const DefaultStoryCardContent = ({ story, headerLevel, borderColor, config = {} }) => {
+ const SectionTagborderColor = rgbToHex(borderColor);
+ const { localizationConfig = {} } = config;
+ return (
+
+ );
+};
+
+DefaultStoryCardContent.propTypes = {
+ story: PropTypes.object.isRequired,
+ border: PropTypes.string,
+ theme: PropTypes.string,
+ isHorizontalMobile: PropTypes.bool,
+ headerLevel: PropTypes.string,
+ borderColor: PropTypes.string,
+ config: PropTypes.object,
+};
+
+StorycardContent.propTypes = {
+ story: PropTypes.object.isRequired,
+ children: PropTypes.node,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ isHorizontalMobile: PropTypes.bool,
+ headerLevel: PropTypes.string,
+ borderColor: PropTypes.string,
+ config: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Molecules/StorycardContent/stories.js b/app/isomorphic/arrow/components/Molecules/StorycardContent/stories.js
new file mode 100644
index 000000000..862b436e4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StorycardContent/stories.js
@@ -0,0 +1,39 @@
+import React from "react";
+import { generateStory } from "../../Fixture";
+import { withStore } from "../../../../storybook";
+import { StorycardContent } from "./index";
+import Readme from "./README.md";
+
+import { Headline } from "../../Atoms/Headline/index";
+import { Subheadline } from "../../Atoms/Subheadline/index";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+
+const story = generateStory();
+
+withStore(
+ "Molecules/StoryCard Content",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("default", () => )
+ .add("Story Card with Headline and Timestamp", () => (
+
+
+
+
+ ))
+ .add("Story Card with Headline, Author with Timestamp and Subheadline ", () => (
+
+
+
+
+
+ ));
diff --git a/app/isomorphic/arrow/components/Molecules/StorycardContent/storycardContent.m.css b/app/isomorphic/arrow/components/Molecules/StorycardContent/storycardContent.m.css
new file mode 100644
index 000000000..790c9cc36
--- /dev/null
+++ b/app/isomorphic/arrow/components/Molecules/StorycardContent/storycardContent.m.css
@@ -0,0 +1,21 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ flex-basis: 70%;
+ @media (--viewport-medium) {
+ flex-basis: 100%;
+ }
+}
+.horizontal-content-wrapper.border-full {
+ padding: 16px 16px 16px 0;
+ @media (--viewport-medium) {
+ padding: 0 16px 16px 16px;
+ }
+}
+
+.border-full {
+ padding: 12px 0 0 0;
+ @media (--viewport-medium) {
+ padding: 0 16px 16px 16px;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/README.md b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/README.md
new file mode 100644
index 000000000..dd69b048f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/README.md
@@ -0,0 +1,38 @@
+# Alternate Collection Filter
+
+The _AlternateCollectionFilter_ component is a collection of collections in a tabbed interface.
+
+## Usage
+
+### Import
+
+```jsx
+import { AlternateCollectionFilter } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "solid",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/alternate-collection-filter.m.css b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/alternate-collection-filter.m.css
new file mode 100644
index 000000000..28ba3813c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/alternate-collection-filter.m.css
@@ -0,0 +1,254 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-desktop (width > 1024px);
+
+.navigator {
+ visibility: hidden;
+ position: relative;
+}
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+
+ :global {
+ .arr--headline h6 {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+
+ .arr--read-time {
+ position: relative;
+ top: -4px;
+ }
+
+ .read-time-indicator {
+ position: relative;
+ top: -2px;
+ padding-bottom: 0;
+ }
+
+ .arr--publish-time {
+ position: relative;
+ top: -1px;
+ }
+
+ .arr-separator {
+ top: -7px;
+ }
+
+ .arr--button {
+ margin: var(--arrow-spacing-l) auto;
+ }
+ }
+
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
+
+.light-wrapper {
+ :global {
+ .arr--collection-name h3,
+ .arr--headline h6 {
+ color: var(--arrow-c-invert-mono1);
+ }
+
+ .section-tag,
+ .author-name,
+ .arr--publish-time,
+ .arr--read-time {
+ color: var(--arrow-c-invert-mono3);
+ }
+ }
+}
+
+.collection-filter {
+ @media (--viewport-desktop) {
+ display: grid;
+ grid-template-columns: 3fr 1px 9fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+
+.divider.dark {
+ @media (--viewport-desktop) {
+ border-left: 1px solid var(--dark-border);
+ }
+}
+
+.divider.light {
+ @media (--viewport-desktop) {
+ border-left: 1px solid var(--light-border);
+ }
+}
+
+.subCollection-wrapper {
+ position: relative;
+ min-height: 100px;
+}
+
+.cards {
+ display: block;
+
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-gap: var(--arrow-spacing-l);
+ }
+
+ @media (--viewport-desktop) {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+.cards .card {
+ margin-bottom: var(--arrow-spacing-l);
+
+ @media (--viewport-medium) {
+ margin-bottom: unset;
+ }
+}
+
+.card-wrapper {
+ @media (--viewport-medium) {
+ display: flex;
+ align-items: flex-start;
+ }
+}
+
+.child-collections {
+ display: flex;
+ font-size: var(--arrow-fs-xs);
+ margin-bottom: var(--arrow-spacing-m);
+ scroll-snap-type: x mandatory;
+ overflow: scroll;
+ scrollbar-width: none;
+
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+
+ @media (--viewport-desktop) {
+ display: block;
+ font-size: var(--arrow-fs-s);
+ line-height: var(--arrow-lh-2);
+ overflow: none;
+ }
+}
+
+.child-collections::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+.child-collection.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.child-collection.light {
+ color: var(--arrow-c-invert-mono3);
+}
+
+.child-collection {
+ margin: 0 var(--arrow-spacing-s) var(--arrow-spacing-s);
+ cursor: pointer;
+ font-size: var(--arrow-fs-xs);
+ font-weight: var(--arrow-fw-bold);
+ width: max-content;
+
+ @media (--viewport-desktop) {
+ font-size: var(--arrow-fs-s);
+ margin: 0 0 var(--arrow-spacing-l) 0;
+ }
+}
+
+.child-collection-wrapper {
+ display: flex;
+ scroll-snap-align: start;
+}
+
+.child-collection.dark:hover,
+.open-subchild > .child-collection.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.child-collection.light:hover,
+.open-subchild > .child-collection.light {
+ color: var(--arrow-c-invert-mono1);
+}
+
+.open-subchild {
+ border-bottom: var(--arrow-spacing-xxs) solid var(--arrow-c-brand1);
+
+ @media (--viewport-desktop) {
+ border-bottom: unset;
+ }
+}
+
+.open-subchild > .navigator {
+ @media (--viewport-desktop) {
+ visibility: visible;
+ width: var(--arrow-spacing-l);
+ transition: width 0.35s ease-in-out;
+ animation: fadein 2s;
+ }
+}
+
+/* tringle design */
+.navigator:before {
+ @media (--viewport-medium) {
+ content: "";
+ border-style: solid;
+ border-width: var(--arrow-spacing-xxs) var(--arrow-spacing-xxs) var(--arrow-spacing-xxs) var(--arrow-spacing-xs);
+ border-color: transparent transparent transparent var(--arrow-c-mono2);
+ position: absolute;
+ right: 0;
+ top: var(--arrow-spacing-xs);
+ }
+}
+
+.navigator.dark:before {
+ @media (--viewport-medium) {
+ border-color: transparent transparent transparent var(--arrow-c-mono2);
+ }
+}
+
+.navigator.light:before {
+ @media (--viewport-medium) {
+ border-color: transparent transparent transparent var(--arrow-c-invert-mono2);
+ }
+}
+
+.subCollection-stories {
+ :global {
+ .section-tag {
+ font-weight: normal;
+ }
+ }
+}
+
+.collection-filter .subCollection-stories {
+ animation-name: fadeIn;
+ animation-fill-mode: both;
+ animation-duration: 1s;
+ animation-delay: 1s;
+}
+
+html[dir="rtl"] {
+ .open-subchild > .navigator {
+ height: var(--arrow-spacing-l);
+ transform: rotate(-180deg);
+ }
+}
+
+.no-button-wrapper {
+ margin-top: var(--arrow-spacing-l);
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/index.js b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/index.js
new file mode 100644
index 000000000..f87922a64
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/index.js
@@ -0,0 +1,182 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { useDispatch, useSelector } from "react-redux";
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { getTextColor, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { Loading } from "../../Svgs/Loading/loading";
+
+import "./alternate-collection-filter.m.css";
+
+const AlternateCollectionFilter = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ showButton = true,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+
+ const [subCollectionStories, handleApiData] = useState([]);
+ const [cachedStories, updateCachedStories] = useState({});
+ const [active, handleActive] = useState(0);
+ const [loading, handleLoading] = useState(true);
+
+ const getFilteredCollection = get(collection, ["items"], []).filter(
+ (collections) => collections.type === "collection"
+ );
+
+ if (getFilteredCollection.length < 1) {
+ return null;
+ }
+
+ const filteredCollectionids = getFilteredCollection.map((subCol) => subCol.id);
+
+ const memoizeStories = (id, data) => {
+ updateCachedStories((cachedStories) => ({ ...cachedStories, [id]: data }));
+ };
+
+ async function fetchSubCollectionData(id = filteredCollectionids[0]) {
+ handleLoading(true);
+ try {
+ let data = {};
+ if (!(id in cachedStories)) {
+ const res = await axios.get(`/api/v1/collections/${id}?limit=7`);
+ data = res.data;
+ } else data = cachedStories[id];
+ memoizeStories(id, data);
+
+ const childCollectionItems = collectionToStories(data);
+ handleLoading(false);
+ handleApiData(childCollectionItems);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("API call failed", error);
+ }
+ }
+
+ useEffect((id) => {
+ fetchSubCollectionData(id);
+ }, []);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(getFilteredCollection[active], qtConfig);
+
+ const items = subCollectionStories.slice(0, 9);
+ const textColor = getTextColor(theme);
+
+ const openChildCollectionItems = (event, index, slug) => {
+ event.stopPropagation();
+ fetchSubCollectionData(slug);
+ const activeIndex = active === index ? 0 : index;
+ handleActive(activeIndex);
+ };
+
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ const getCustomStyleName = textColor === "light" ? "light-wrapper" : "";
+
+ return (
+
+
+
+
+
+ {getFilteredCollection.map((subCollections, index) => {
+ return (
+
+
openChildCollectionItems(event, index, subCollections.id)}
+ >
+ {subCollections.name}
+
+
+
+ );
+ })}
+
+
+
+ {loading ? (
+
+ ) : (
+
+
+ {items.slice(0, 6).map((story, index) => (
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+
+ {items.length > 6 && (
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ )}
+ {footerSlotComp}
+
+
+ );
+};
+
+export default StateProvider(AlternateCollectionFilter);
+
+AlternateCollectionFilter.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // section tag border color
+ borderColor: PropTypes.string,
+ // background color of the row
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ footerSlotConfig: PropTypes.object,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+AlternateCollectionFilter.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
diff --git a/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/stories.js b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/stories.js
new file mode 100644
index 000000000..1a532a277
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AlternateCollectionFilter/stories.js
@@ -0,0 +1,75 @@
+/* eslint-disable prefer-regex-literals */
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import AlternateCollectionFilter from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+
+import { generateCollections, generateCollection } from "../../Fixture";
+
+const mock = new MockAdapter(axios);
+
+const API_REQUEST = new RegExp(`/api/v1/collections/*`);
+
+const nestedCollection = generateCollection({ stories: 10 });
+
+const collection = generateCollections(15); // will have to reduce this number post testing
+
+const defaultvalue = "#ffffff";
+const collectionNameDefaultValue = "#3a9fdd";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Alternate Collection Filter",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ borderColor: color("Section Tag Border Color", sectionTagDefaultvalue),
+ theme: color("color", defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ mock.onGet(API_REQUEST).reply(200, nestedCollection);
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/AsideCollection/README.md b/app/isomorphic/arrow/components/Rows/AsideCollection/README.md
new file mode 100644
index 000000000..437a23d01
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AsideCollection/README.md
@@ -0,0 +1,27 @@
+### Aside Collection
+
+Aside collection is a template which is used to represent a collection of stories or an array of stories.
+
+It also accepts stories from a custom collection if a collection slug is passed.
+
+By default it'll render stories from related stories API.
+
+## Usage
+
+## To pass a collection with default collection name
+
+```jsx
+
+```
+
+## To pass stories with customisable heading and horizontal style
+
+```jsx
+
+```
+
+## To make aside's last child sticky in tablet and desktop
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.m.css b/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.m.css
new file mode 100644
index 000000000..3225af365
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.m.css
@@ -0,0 +1,103 @@
+@import "../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.stories {
+ margin-top: var(--arrow-spacing-m);
+}
+.wrapper :global(.arr--collection-name) {
+ margin-bottom: var(--arrow-spacing-m);
+}
+.story {
+ display: flex;
+ margin-bottom: var(--arrow-spacing-m);
+}
+
+.story-hero-image {
+ min-width: 82px;
+ max-width: 82px;
+ margin-right: var(--arrow-spacing-xs);
+}
+
+.story-byline {
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: var(--arrow-spacing-xs);
+}
+
+.info {
+ text-align: center;
+}
+
+.dark {
+ color: var(--arrow-c-mono4);
+}
+.light {
+ color: var(--arrow-c-mono4);
+}
+.wrapper h3 {
+ font-size: var(--arrow-fs-m);
+}
+
+.ad-slot {
+ padding: var(--arrow-spacing-l) 0;
+ @include tablet {
+ padding: var(--arrow-spacing-xl) 0;
+ }
+}
+
+.horizontal-wrapper {
+ .stories {
+ @include tablet {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ grid-gap: var(--arrow-spacing-l);
+ }
+ }
+
+ :global .arr--story-card {
+ @include tablet {
+ flex-direction: column;
+ }
+ }
+
+ .story-hero-image {
+ @include tablet {
+ min-width: 100%;
+ margin-bottom: 8px;
+ margin-right: var(--arrow-spacing-xs);
+ }
+ }
+
+ .story-byline {
+ @include tablet {
+ margin-top: 0;
+ }
+ }
+
+ .story {
+ @include tablet {
+ display: block;
+ }
+ }
+}
+
+html[dir="rtl"] {
+ .story-hero-image {
+ margin-right: 0;
+ margin-left: var(--arrow-spacing-xs);
+ }
+}
+
+.wrapper {
+ @include tablet {
+ height: 100%;
+ }
+}
+
+.sticky > :last-child {
+ @include tablet {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 125px;
+ margin-bottom: 64px;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.test.js b/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.test.js
new file mode 100644
index 000000000..db8550f73
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AsideCollection/aside-collection.test.js
@@ -0,0 +1,34 @@
+import React from "react";
+import { mount, shallow } from "enzyme";
+import AsideCollection from ".";
+import { generateCollection, generateStore, generateStory } from "../../Fixture";
+import { Provider } from "react-redux";
+
+const collection = generateCollection();
+const story = generateStory();
+const stories = [story, story, story];
+
+describe("Aside collection component", () => {
+ it("Should render default template with collection", () => {
+ const wrapper = mount(
+
+
+ );
+
+ );
+ expect(wrapper.find(AsideCollection)).toHaveLength(1);
+ });
+ it("Should render template with array of stories", () => {
+ const wrapper = mount(
+
+
+ );
+
+ );
+ expect(wrapper.find({ "data-test-id": "aside-collection" }).prop("className")).toMatch(/aside-collection/);
+ });
+ it("Should render `no stories message` if there are no stories", () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(AsideCollection)).toHaveLength(0);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/AsideCollection/index.js b/app/isomorphic/arrow/components/Rows/AsideCollection/index.js
new file mode 100644
index 000000000..400189eae
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AsideCollection/index.js
@@ -0,0 +1,179 @@
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import React, { useEffect, useState } from "react";
+import { isEmpty, getCollectionData } from "../../../utils/utils";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import KeyEvents from "../../Molecules/KeyEvents";
+import "./aside-collection.m.css";
+
+const StoryCollection = ({ data, slotData, horizontal, config, opts }) => {
+ const { theme = "", title = "", collectionSlug = "" } = slotData || config;
+ const heading = title || "Related Stories";
+ const isHorizontal = horizontal ? "horizontal-wrapper" : "";
+ const mountAt = get(config, ["mountAtPrefix"], "");
+ const [storyList, updateStoryList] = useState([]);
+ const [collectionData, updateCollectionData] = useState([]);
+ const updateStoryListItems = async (slug) => {
+ const collectionDataResponse = await getCollectionData(slug, mountAt);
+ updateCollectionData(collectionDataResponse);
+ const storyItems = collectionDataResponse.items;
+ updateStoryList(storyItems);
+ };
+
+ useEffect(() => {
+ if (collectionSlug) {
+ updateStoryListItems(collectionSlug);
+ }
+ }, [collectionSlug]);
+
+ useEffect(() => {
+ if (!collectionSlug && data) {
+ const storyItems = data.items || data;
+ updateStoryList(storyItems);
+ }
+ }, [data, collectionSlug]);
+
+ return (
+ <>
+
+
+ {storyList && storyList.length > 0 ? (
+ storyList.slice(0, 4).map((item) => {
+ const story = item.story || item;
+ return (
+
+
+
+
+
+
+ {isHorizontal &&
}
+
+
+
+
+
+ );
+ })
+ ) : (
+
+ No stories found.
+
+ )}
+
+ >
+ );
+};
+
+StoryCollection.propTypes = {
+ data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
+ config: PropTypes.object,
+ horizontal: PropTypes.bool,
+ slotData: PropTypes.object,
+ opts: PropTypes.object,
+};
+
+const AsideCollection = ({
+ data = {},
+ config = {},
+ horizontal = false,
+ slots = [],
+ sticky = false,
+ keyEventsData = {},
+ enableKeyEvents = false,
+ adComponent,
+ widgetComp,
+ storyId,
+ opts = {},
+}) => {
+ if (isEmpty(data)) return null;
+
+ const { theme } = config;
+ const isHorizontal = horizontal ? "horizontal-wrapper" : "";
+
+ if (horizontal) {
+ return (
+
+
+
+ );
+ }
+ const isSticky = sticky ? "sticky" : "";
+ const asideSlot = (type, slot, index) => {
+ switch (type) {
+ case "ad":
+ return (
+
+ {adComponent({ ...slot, id: `ad-${index}_${storyId}` })}
+
+ );
+ case "widget":
+ return (
+
+ {widgetComp({ ...slot, id: `widget-${index}_${storyId}` })}
+
+ );
+
+ case "collection":
+ return (
+
+
+
+ );
+ }
+ };
+
+ return (
+
+ {enableKeyEvents && (
+
+ )}
+ {slots.map((slot, index) => asideSlot(slot.type, slot, index))}
+
+ );
+};
+
+AsideCollection.propTypes = {
+ data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
+ config: PropTypes.object,
+ opts: PropTypes.object,
+ horizontal: PropTypes.bool,
+ slots: PropTypes.array,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+ sticky: PropTypes.bool,
+ keyEventsData: PropTypes.shape({
+ story: PropTypes.object,
+ config: PropTypes.object,
+ showLoadMore: PropTypes.boolean,
+ }),
+ enableKeyEvents: PropTypes.boolean,
+ storyId: PropTypes.string,
+};
+
+export default StateProvider(AsideCollection);
+export { StoryCollection };
diff --git a/app/isomorphic/arrow/components/Rows/AsideCollection/stories.js b/app/isomorphic/arrow/components/Rows/AsideCollection/stories.js
new file mode 100644
index 000000000..fde8357c4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AsideCollection/stories.js
@@ -0,0 +1,52 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import AsideCollection from ".";
+import { collectionNameTemplates, optionalSelect, withStore } from "../../../../storybook";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import { generateStory } from "../../Fixture";
+import { slotData } from "../../Fixture/slot-config";
+import Readme from "./README.md";
+
+const story = generateStory();
+const stories = [story, story, story];
+const label = "Background color";
+const defaultvalue = "#ffffff";
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Aside Collection",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+).add("Aside Collection template", () => {
+ const config = {
+ title: text("Title", "Recommended Stories"),
+ theme: color(label, defaultvalue),
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ showReadTime: boolean("Read time", true),
+ showRowTitle: boolean("Collection name", true),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ };
+ return (
+
+ );
+});
diff --git a/app/isomorphic/arrow/components/Rows/AstrologyCollection/README.md b/app/isomorphic/arrow/components/Rows/AstrologyCollection/README.md
new file mode 100644
index 000000000..2a250f314
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AstrologyCollection/README.md
@@ -0,0 +1,40 @@
+# Astrology Collection Filter
+
+The _AstrologyCollection_ component is a collection of collections in a tabbed interface.
+
+## Usage
+
+### Import
+
+```jsx
+import { AstrologyCollection } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ borderColor: color("Section Tag Border Color", sectionTagDefaultvalue),
+ theme: color("color", defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showButton: boolean("Show button", true),
+ slotConfig: [
+ {
+ type: "ad",
+ component: () =>
+ }
+ ]
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
diff --git a/app/isomorphic/arrow/components/Rows/AstrologyCollection/astrology-collection.m.css b/app/isomorphic/arrow/components/Rows/AstrologyCollection/astrology-collection.m.css
new file mode 100644
index 000000000..422f5921a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AstrologyCollection/astrology-collection.m.css
@@ -0,0 +1,165 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-large (width >= 1024px);
+.wrapper {
+ text-align: center;
+
+ :global {
+ .collection-borderBottom {
+ display: inline-block;
+ }
+
+ .collection-borderLeft {
+ display: inline-flex;
+ }
+ }
+}
+
+.collection-summary {
+ font-size: var(--arrow-fs-xs);
+ line-height: var(--arrow-lh-3);
+ margin-bottom: var(--arrow-spacing-xl);
+ color: var(--arrow-c-mono4);
+ @media (--viewport-medium) {
+ max-width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.tab {
+ display: flex;
+ justify-content: center;
+ margin-bottom: var(--arrow-spacing-xl);
+
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-48);
+ }
+}
+
+.child-collections {
+ display: flex;
+ font-size: var(--arrow-fs-xs);
+ scroll-snap-type: x mandatory;
+ overflow: scroll;
+ scrollbar-width: none;
+}
+
+.child-collections::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+.child-collection {
+ flex-shrink: 0;
+ list-style: none;
+ cursor: pointer;
+ font-size: var(--arrow-fs-xs);
+ font-weight: var(--arrow-fw-bold);
+ padding: var(--arrow-spacing-s);
+}
+
+.child-collection.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.child-collection.light {
+ color: var(--arrow-c-invert-mono3);
+}
+
+.child-collection.dark:hover,
+.open-subchild.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.child-collection.light:hover,
+.open-subchild.light,
+.collection-summary.light {
+ color: var(--arrow-c-invert-mono1);
+}
+
+.open-subchild {
+ border-bottom: var(--arrow-spacing-xxs) solid var(--arrow-c-brand1);
+}
+
+.main-wrapper {
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 9fr 3fr;
+ grid-gap: var(--arrow-spacing-s);
+ }
+}
+
+.main-wrapper-container {
+ max-width: 1000px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.subCollection-stories {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ animation-name: fadeIn;
+ animation-fill-mode: both;
+ animation-duration: 1s;
+ animation-delay: 1s;
+ justify-content: space-between;
+
+ @media (--viewport-large) {
+ grid-template-columns: repeat(6, 1fr);
+ }
+
+ :global .arr--fallback-image {
+ border-radius: 50%;
+ }
+}
+
+.card {
+ flex-basis: 32%;
+ margin-bottom: var(--arrow-spacing-l);
+ padding: 0 var(--arrow-spacing-xs);
+
+ @media (--viewport-medium) {
+ flex-basis: 16%;
+ }
+
+ :global .qt-image {
+ border-radius: 50%;
+ border: solid 1px var(--arrow-c-invert-mono7);
+ }
+
+ :global .arr--headline {
+ text-align: center;
+ }
+
+ :global .arr--hero-image {
+ flex-grow: 0;
+ width: 56px;
+ height: 56px;
+ margin-left: auto;
+ margin-right: auto;
+
+ @media (--viewport-medium) {
+ width: 88px;
+ height: 88px;
+ }
+ }
+}
+
+.nomore-stories {
+ margin: auto;
+}
+
+.ads {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AstrologyCollection/index.js b/app/isomorphic/arrow/components/Rows/AstrologyCollection/index.js
new file mode 100644
index 000000000..101c00f45
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AstrologyCollection/index.js
@@ -0,0 +1,178 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { useDispatch, useSelector } from "react-redux";
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { getTextColor, generateNavigateSlug, navigateTo, getSlot } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { Loading } from "../../Svgs/Loading/loading";
+
+import "./astrology-collection.m.css";
+
+const AstrologyCollection = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ footerButton = "",
+ slotConfig = [],
+ } = config;
+
+ const [subCollectionStories, handleApiData] = useState([]);
+ const [cachedStories, updateCachedStories] = useState({});
+ const [active, handleActive] = useState(0);
+ const [loading, handleLoading] = useState(true);
+
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const isAdWidgetEnabled = type === "ad" || type === "widget";
+ const adWidgetSlot = isAdWidgetEnabled ? getSlot(type, component) : null;
+
+ const getFilteredCollection = get(collection, ["items"], []).filter(
+ (collections) => collections.type === "collection"
+ );
+
+ if (getFilteredCollection.length < 1) {
+ return null;
+ }
+
+ const filteredCollectionids = getFilteredCollection.map((subCol) => subCol.id);
+
+ const memoizeStories = (id, data) => {
+ updateCachedStories((cachedStories) => ({ ...cachedStories, [id]: data }));
+ };
+
+ async function fetchSubCollectionData(id = filteredCollectionids[0]) {
+ handleLoading(true);
+ try {
+ let data = {};
+ if (!(id in cachedStories)) {
+ const res = await axios.get(`/api/v1/collections/${id}?limit=7`);
+
+ data = res.data;
+ } else data = cachedStories[id];
+ memoizeStories(id, data);
+
+ const childCollectionItems = collectionToStories(data);
+ handleLoading(false);
+ handleApiData(childCollectionItems);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("API call failed", error);
+ }
+ }
+
+ useEffect((id) => {
+ fetchSubCollectionData(id);
+ }, []);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(getFilteredCollection[active], qtConfig);
+
+ const items = subCollectionStories.slice(0, 12);
+ const textColor = getTextColor(theme);
+
+ const openChildCollectionItems = (event, index, slug) => {
+ event.stopPropagation();
+ fetchSubCollectionData(slug);
+ const activeIndex = active === index ? 0 : index;
+ handleActive(activeIndex);
+ };
+
+ return (
+
+
+
+ {collection.summary && (
+
+ {collection.summary}
+
+ )}
+
+
+ {getFilteredCollection.map((subCollections, index) => {
+ return (
+ openChildCollectionItems(event, index, subCollections.id)}
+ data-test-id={subCollections.name}
+ >
+ {subCollections.name}
+
+ );
+ })}
+
+
+
+
+ {loading ? (
+
+ ) : (
+
+ {items.length === 0 &&
No more stories to load.
}
+ {items.map((story, index) => (
+
+
+
+
+
+
+ ))}
+
+ )}
+
+ {isAdWidgetEnabled &&
{adWidgetSlot}
}
+
+
+ {items.length >= 12 && (
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ )}
+
+
+ );
+};
+
+export default StateProvider(AstrologyCollection);
+
+AstrologyCollection.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // background color of the row
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+AstrologyCollection.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
diff --git a/app/isomorphic/arrow/components/Rows/AstrologyCollection/stories.js b/app/isomorphic/arrow/components/Rows/AstrologyCollection/stories.js
new file mode 100644
index 000000000..6ca760856
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AstrologyCollection/stories.js
@@ -0,0 +1,61 @@
+/* eslint-disable prefer-regex-literals */
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, collectionNameTemplates, footerButton } from "../../../../storybook";
+import AstrologyCollection from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+
+import { generateCollections, generateCollection } from "../../Fixture";
+
+const mock = new MockAdapter(axios);
+
+const API_REQUEST = new RegExp(`/api/v1/collections/*`);
+
+const nestedCollection = generateCollection({ stories: 12 });
+
+const collection = generateCollections(3);
+
+const defaultvalue = "#ffffff";
+const collectionNameDefaultValue = "#3a9fdd";
+
+const CustomAdOrWidget = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Astrology Collection",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ theme: color("color", defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showButton: boolean("Show button", true),
+ slotConfig: [
+ {
+ type: "ad",
+ component: () => ,
+ },
+ ],
+ };
+
+ mock.onGet(API_REQUEST).reply(200, nestedCollection);
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/README.md b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/README.md
new file mode 100644
index 000000000..56b696040
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/README.md
@@ -0,0 +1,43 @@
+# Author Introduction Card
+
+Author Introduction card is used to display the author information which consists of name, bio with author image and social connect of the author.
+
+## Usage
+
+Import the AuthorIntroductionCard and pass authorDetails as props and config.
+
+```jsx
+import { AuthorIntroductionCard } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+ enableBio: false;
+ enableSocialLinks: true;
+};
+
+
+```
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+ enableBio: false;
+ enableSocialLinks: true;
+};
+
+
+```
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+ enableBio: false;
+ enableSocialLinks: true;
+};
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.m.css b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.m.css
new file mode 100644
index 000000000..d42a96e5b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.m.css
@@ -0,0 +1,279 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value mobile: 768px;
+@value tablet: 992px;
+@value desktop: 1025px;
+
+.connect {
+ font-size: var(--arrow-spacing-m);
+ line-height: var(--arrow-lh-5);
+ color: var(--arrow-c-mono4);
+ align-self: center;
+ margin-bottom: var(--arrow-spacing-xxs);
+}
+
+.social-share {
+ flex: 1;
+ padding: var(--arrow-spacing-xs);
+}
+
+.share-icon {
+ display: flex;
+ align-items: flex-end;
+ margin-right: var(--arrow-spacing-l);
+}
+
+.default .author-image {
+ align-self: center;
+ @media (min-width: mobile) {
+ grid-column: 8/12;
+ }
+}
+
+.default .author-name {
+ color: var(--arrow-c-mono2);
+ font-weight: var(--arrow-fw-bold);
+ line-height: var(--arrow-lh-5);
+ align-self: center;
+ margin: var(--arrow-spacing-xs) 0 var(--arrow-spacing-xxs);
+ @media (min-width: mobile) {
+ line-height: var(--arrow-lh-2);
+ align-self: flex-start;
+ }
+}
+
+.default .author-description {
+ margin: var(--arrow-spacing-s) 0;
+ color: var(--arrow-c-mono4);
+ font-size: var(--arrow-spacing-m);
+ line-height: var(--arrow-lh-5);
+ white-space: pre-line;
+}
+
+.default .wrapper {
+ display: flex;
+ flex-direction: column-reverse;
+ padding: 0 var(--arrow-spacing-s) var(--arrow-spacing-m) var(--arrow-spacing-s);
+ @media (min-width: mobile) {
+ display: grid;
+ grid-gap: var(--arrow-fs-l);
+ grid-template-columns: repeat(12, 1fr);
+ padding: 0 0 var(--arrow-fs-xxl) 0;
+ }
+}
+
+.default .introduction-card {
+ display: flex;
+ flex: 1;
+ justify-content: center;
+ flex-direction: column;
+ @media (min-width: mobile) {
+ grid-column: 2/8;
+ }
+ @media (min-width: tablet) {
+ grid-column: 2/7;
+ }
+}
+
+.default .social-wrapper {
+ margin-top: var(--arrow-spacing-xs);
+ display: flex;
+ justify-content: space-between;
+ @media (min-width: mobile) {
+ align-items: center;
+ }
+ @media (min-width: tablet) {
+ margin-top: unset;
+ position: relative;
+ align-items: unset;
+ }
+}
+
+.default .connect {
+ @media (min-width: tablet) {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.default .linkWrapper {
+ display: flex;
+}
+
+.default .social-connect-wrapper {
+ @media (min-width: mobile) {
+ display: flex;
+ }
+ @media (min-width: tablet) {
+ margin-top: var(--arrow-spacing-xs);
+ display: block;
+ }
+}
+
+.default :global(.desktop-share-wrapper) {
+ @media (min-width: tablet) {
+ position: absolute;
+ right: 0;
+ max-width: 42px;
+ }
+}
+
+.default .social-wrapper > :nth-child(2) {
+ margin-top: var(--arrow-spacing-xxs);
+ @media (min-width: mobile) {
+ margin-top: unset;
+ }
+}
+
+.default .social-connect-wrapper > :nth-child(2) {
+ margin-left: unset;
+ @media (min-width: mobile) {
+ margin-left: var(--arrow-spacing-xs);
+ }
+ @media (min-width: tablet) {
+ margin-left: unset;
+ }
+}
+
+.default .wrapper.dark {
+ border-bottom: 1px solid var(--dark-border);
+}
+
+.default .wrapper.light {
+ border-bottom: 1px solid var(--light-border);
+}
+
+.small-circle {
+ @media (min-width: mobile) {
+ padding: var(--arrow-spacing-l) var(--arrow-spacing-s);
+ height: 100%;
+ @media (min-width: tablet) {
+ padding: var(--arrow-spacing-xl);
+ }
+ }
+}
+
+.small-circle.border {
+ border-radius: 3px;
+ padding: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ padding: var(--arrow-spacing-l) var(--arrow-spacing-s);
+ }
+ @media (min-width: tablet) {
+ padding: var(--arrow-spacing-xl);
+ }
+}
+
+.small-circle.border.dark {
+ border: solid 1px var(--dark-border);
+}
+
+.small-circle.border.light {
+ border: solid 1px var(--light-border);
+}
+
+.small-circle .wrapper {
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+.small-circle .introduction-card {
+ @media (min-width: mobile) {
+ text-align: center;
+ }
+}
+
+.small-circle .author-image {
+ display: flex;
+ align-items: center;
+ @media (min-width: mobile) {
+ justify-content: center;
+ align-items: unset;
+ }
+}
+
+.small-circle .social-wrapper {
+ @media (min-width: mobile) {
+ justify-content: center;
+ }
+}
+
+.small-circle .author-name {
+ color: var(--arrow-c-mono2);
+ font-size: var(--arrow-fs-s);
+ line-height: var(--arrow-lh-3);
+ font-weight: var(--arrow-fw-bold);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-m);
+ margin-top: var(--arrow-spacing-m);
+ }
+}
+
+.small-circle .author-image > div {
+ margin-right: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ margin-right: unset;
+ }
+}
+
+.small-circle .author-description,
+.small-circle .connect {
+ font-size: var(--arrow-fs-tiny);
+ line-height: var(--arrow-lh-5);
+ color: var(--arrow-c-mono4);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-tiny);
+ }
+}
+
+.small-circle .author-description {
+ position: relative;
+ max-height: 63px;
+ overflow: hidden;
+ margin-top: var(--arrow-spacing-xs);
+}
+
+.small-circle .fade-out {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 90%;
+ height: 21px;
+ background-image: inherit;
+}
+
+.small-circle .social-connect-wrapper {
+ margin-top: var(--arrow-spacing-xxs);
+ @media (min-width: mobile) {
+ margin-top: var(--arrow-spacing-xs);
+ }
+}
+
+.small-circle.border .social-connect-wrapper {
+ margin-top: var(--arrow-spacing-xs);
+}
+
+.default .dark,
+.small-circle .dark {
+ color: var(--arrow-c-mono2);
+}
+
+.default .light,
+.small-circle .light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+.default .social-connect-wrapper > :nth-child(2),
+.small-circle .social-connect-wrapper > :nth-child(1) {
+ padding-left: unset;
+}
+
+.small-circle .social-share {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-xs) var(--arrow-spacing-xs);
+}
+
+html[dir="rtl"] {
+ .default :global(.desktop-share-wrapper) {
+ @media (min-width: tablet) {
+ position: static;
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.test.js b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.test.js
new file mode 100644
index 000000000..aeb35ea00
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/author-intro.test.js
@@ -0,0 +1,106 @@
+import React from "react";
+import { mount } from "enzyme";
+import AuthorIntroductionCard from ".";
+import { generateStore, authorData } from "../../Fixture";
+import { Provider } from "react-redux";
+
+describe("Author Intro component", () => {
+ it("Should render default template with border, author bio and social-links enabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: true,
+ enableSocialLinks: true,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/default/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(7);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(1);
+ });
+
+ it("Should render default template with border, author bio and social-links disabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: false,
+ enableSocialLinks: false,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/default/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(0);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(0);
+ });
+
+ it("Should render square template with border, author bio and social-links enabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: true,
+ enableSocialLinks: true,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/default/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(7);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(1);
+ });
+
+ it("Should render square template with border, author bio and social-links disabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: false,
+ enableSocialLinks: false,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/default/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(0);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(0);
+ });
+
+ it("Should render small cirlce template with border, author bio and social-links enabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: true,
+ enableSocialLinks: true,
+ borderSupport: true,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/small-circle/);
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/border/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(7);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(1);
+ });
+
+ it("Should render small cirlce template with border, author bio and social-links disabled", () => {
+ const contextConfig = {
+ theme: "#ffffff",
+ enableBio: false,
+ enableSocialLinks: false,
+ borderSupport: false,
+ };
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "author-intro" }).prop("className")).toMatch(/small-circle/);
+ expect(wrapper.find({ "data-test-id": "social-link" }).length).toBe(0);
+ expect(wrapper.find({ "data-test-id": "author-bio" }).length).toBe(0);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/index.js b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/index.js
new file mode 100644
index 000000000..4bc4a9c44
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/index.js
@@ -0,0 +1,123 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { getTextColor, sharePageUrl, clientWidth } from "../../../utils/utils";
+import AuthorImage from "../../Atoms/AuthorImage";
+import { SocialShareTemplate } from "../../Molecules/SocialShareTemplate/index";
+import { SocialShare } from "@quintype/components";
+import "./author-intro.m.css";
+import { Facebook } from "../../Svgs/SocialIcons/facebook";
+import { Youtube } from "../../Svgs/SocialIcons/youtube";
+import { Twitter } from "../../Svgs/SocialIcons/twitter";
+import { LinkedIn } from "../../Svgs/SocialIcons/linkedin";
+import { Instagram } from "../../Svgs/SocialIcons/instagram";
+import { WhatsApp } from "../../Svgs/SocialIcons/whatsapp";
+import { Pinterest } from "../../Svgs/SocialIcons/pinterest";
+
+const AuthorIntroductionCard = ({ data = {}, config = {}, template = "" }) => {
+ const { theme = "", enableBio = true, enableSocialLinks = true, borderSupport = true } = config;
+ const textColor = getTextColor(theme);
+ const { name, bio, social } = data;
+ const isSmallCircle = template === "smallCircle";
+ const authorCardStyle = isSmallCircle ? "small-circle" : "default";
+ const isMobile = clientWidth("mobile");
+ const isFullWidth = !isSmallCircle ? "full-width-with-padding" : "";
+ const supportBorder = isSmallCircle && borderSupport ? "border" : "";
+
+ const getIcon = (item) => {
+ switch (item) {
+ case "twitter":
+ return ;
+ case "facebook":
+ return ;
+ case "youtube":
+ return ;
+ case "linkedin":
+ return ;
+ case "whatsapp":
+ return ;
+ case "instagram":
+ return ;
+ case "pinterest":
+ return ;
+ }
+ };
+
+ return (
+
+
+
+ {isSmallCircle && isMobile ? null :
{name} }
+ {enableBio && bio && (
+
+ {bio}
+ {isSmallCircle && (
+
+ )}
+
+ )}
+
+
+ {enableSocialLinks && social && (
+ <>
+ {template !== "smallCircle" &&
Connect :
}
+ {Object.entries(social).map((item, index) =>
+ item.length > 0 && item[1] ? (
+ // can add Social Networks without the url so adding an additional check below
+ item[1].url ? (
+
+ {getIcon(item[0])}
+
+ ) : null
+ ) : null
+ )}
+ >
+ )}
+
+ {!isSmallCircle && (
+
+ )}
+
+
+
+
+ {isSmallCircle && isMobile &&
{name} }
+
+
+
+ );
+};
+
+AuthorIntroductionCard.propTypes = {
+ data: PropTypes.shape({ name: PropTypes.string, bio: PropTypes.string, social: PropTypes.object }),
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ enableBio: PropTypes.bool,
+ enableSocialLinks: PropTypes.bool,
+ borderSupport: PropTypes.bool,
+ }),
+ template: PropTypes.string,
+};
+export default AuthorIntroductionCard;
diff --git a/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/stories.js b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/stories.js
new file mode 100644
index 000000000..b2d0b9195
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorIntroductionCard/stories.js
@@ -0,0 +1,43 @@
+import React from "react";
+import { withStore } from "../../../../storybook";
+import AuthorIntroductionCard from "./index";
+import { color, boolean } from "@storybook/addon-knobs";
+import { authorData } from "../../Fixture";
+import Readme from "./README.md";
+
+withStore(
+ "Rows/Author Introduction Card ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .add("Default", () => {
+ const contextConfig = {
+ theme: color("Color", "#ffffff"),
+ enableBio: boolean("Enable Bio", true),
+ enableSocialLinks: boolean("Enable Social Links", true),
+ };
+ return ;
+ })
+ .add("Square", () => {
+ const contextConfig = {
+ theme: color("Color", "#ffffff"),
+ enableBio: boolean("Enable Bio", true),
+ enableSocialLinks: boolean("Enable Social Links", true),
+ };
+ return ;
+ })
+ .add("Small Circle", () => {
+ const contextConfig = {
+ theme: color("Color", "#ffffff"),
+ enableBio: boolean("Enable Bio", true),
+ enableSocialLinks: boolean("Enable Social Links", true),
+ borderSupport: boolean("With Border", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/AuthorsList/README.md b/app/isomorphic/arrow/components/Rows/AuthorsList/README.md
new file mode 100644
index 000000000..68d81e695
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorsList/README.md
@@ -0,0 +1,24 @@
+# Authors List
+
+Authors List is used to display the authors information which consists of name, bio with author image and social connect of the author.
+
+## Usage
+
+Import the AuthorsList and pass authorDetails as data and config.
+
+```jsx
+import { AuthorsList } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+const config = {
+ enableBio: boolean("Enable Bio", true),
+ buttonText: text("Footer text", "Load More"),
+ theme: color("color", "#ffffff")
+ enableSocialLinks: boolean("Enable Social Links", true)
+};
+limit = 9;
+ ;
+```
diff --git a/app/isomorphic/arrow/components/Rows/AuthorsList/author-list.test.js b/app/isomorphic/arrow/components/Rows/AuthorsList/author-list.test.js
new file mode 100644
index 000000000..b9f81b4be
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorsList/author-list.test.js
@@ -0,0 +1,50 @@
+import React from "react";
+import { mount } from "enzyme";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import AuthorsList from ".";
+import { generateStore, authorData } from "../../Fixture";
+import { Provider } from "react-redux";
+
+const getMoreData = () => {
+ console.log("load more data");
+};
+
+const AuthorsData = Array(8).fill(authorData);
+
+describe("Authors List component", () => {
+ it("Should render authors list component if data is present", () => {
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "authors-list" }).prop("className")).toMatch(/arr--authors-list/);
+ });
+
+ it("Should not render authors list component if data is absent", () => {
+ const wrapper = mount(
+
+ ;
+
+ );
+ expect(wrapper.find({ "data-test-id": "authors-list" }).length).toBe(0);
+ });
+
+ it("Should render Authors List component with Load More disabled", () => {
+ const wrapper = mount(
+
+ ; );
+
+ );
+ expect(wrapper.find(LoadmoreButton).length).toEqual(0);
+ });
+
+ it("Should render Authors List component with Load More enabled", () => {
+ const wrapper = mount(
+
+ ; );
+
+ );
+ expect(wrapper.find(LoadmoreButton).length).toEqual(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/AuthorsList/authors-list.m.css b/app/isomorphic/arrow/components/Rows/AuthorsList/authors-list.m.css
new file mode 100644
index 000000000..7f84de8c1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorsList/authors-list.m.css
@@ -0,0 +1,23 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+
+.wrapper {
+ margin: 0 var(--arrow-spacing-s);
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-gap: var(--arrow-spacing-l);
+ margin: 0 var(--arrow-spacing-l);
+ }
+ @media (min-width: tablet) {
+ margin: 0;
+ }
+}
+
+.linkWrapper :global(.arr--author-intro-card) {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ margin-bottom: unset;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/AuthorsList/index.js b/app/isomorphic/arrow/components/Rows/AuthorsList/index.js
new file mode 100644
index 000000000..0d9049906
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorsList/index.js
@@ -0,0 +1,52 @@
+import React from "react";
+import { Link } from "@quintype/components";
+import PropTypes from "prop-types";
+import { getTextColor } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import AuthorIntroductionCard from "../AuthorIntroductionCard";
+import { StateProvider } from "../../SharedContext";
+import "./authors-list.m.css";
+import { useSelector } from "react-redux";
+import get from "lodash/get";
+
+const AuthorsList = ({ data = [], config = {}, getMoreData, hideLoadmore = false, limit }) => {
+ if (data.length < 1) return null;
+ const { theme } = config;
+ const textColor = getTextColor(theme);
+ const mountAt = config.mountAt || "";
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ return (
+
+
+ {data.slice(0, limit).map((author, index) => (
+
+
+
+ ))}
+
+ {!hideLoadmore && (
+
+ )}
+
+ );
+};
+
+AuthorsList.propTypes = {
+ data: PropTypes.array,
+ config: PropTypes.shape({ enableBio: PropTypes.bool, theme: PropTypes.string }),
+ getMoreData: PropTypes.object.isRequired,
+ limit: PropTypes.number,
+ hideLoadmore: PropTypes.bool,
+};
+
+export default StateProvider(AuthorsList);
diff --git a/app/isomorphic/arrow/components/Rows/AuthorsList/stories.js b/app/isomorphic/arrow/components/Rows/AuthorsList/stories.js
new file mode 100644
index 000000000..dcc975f78
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/AuthorsList/stories.js
@@ -0,0 +1,61 @@
+import React from "react";
+import { withStore } from "../../../../storybook";
+import AuthorsList from "./index";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { authorData } from "../../Fixture";
+import Readme from "./README.md";
+
+const AuthorsData = Array(8).fill(authorData);
+AuthorsData.push({
+ slug: "parvathi-mohan-2",
+ name: "Lucy Douglas",
+ social: {
+ twitter: {
+ url: "https://twitter.com/Reena00659364",
+ handle: "Twitter",
+ },
+ youtube: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Youtube",
+ },
+ facebook: {
+ url: "https://www.facebook.com/people/Reena-Singh/100005438855189",
+ handle: "Facebook",
+ },
+ },
+ bio: "",
+ id: 94985,
+ "avatar-url": "",
+ "avatar-s3-key": "",
+ "twitter-handle": "quintype_inc",
+ stats: {
+ contributions: null,
+ },
+ metadata: {},
+});
+
+const getMoreData = () => {
+ console.log("load more data");
+};
+
+withStore(
+ "Rows/Author List",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const config = {
+ enableBio: boolean("Enable Bio", true),
+ buttonText: text("Footer text", "Load More"),
+ theme: color("color", "#ffffff"),
+ enableSocialLinks: boolean("Enable Social Links", true),
+ borderSupport: boolean("With Border", true),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Rows/CollectionFilter/README.md b/app/isomorphic/arrow/components/Rows/CollectionFilter/README.md
new file mode 100644
index 000000000..5460b3889
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/CollectionFilter/README.md
@@ -0,0 +1,39 @@
+# Collection Filter
+
+The _CollectionFilter_ component is a collection of collections in a tabbed interface.
+
+The last 3 stories can be replaced with an Ad/Widget based on the slotConfig.
+
+## Usage
+
+### Import
+```jsx
+import { CollectionFilter } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "solid",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/CollectionFilter/collection-filter.m.css b/app/isomorphic/arrow/components/Rows/CollectionFilter/collection-filter.m.css
new file mode 100644
index 000000000..6486b2614
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/CollectionFilter/collection-filter.m.css
@@ -0,0 +1,213 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.navigator {
+ visibility: hidden;
+ position: relative;
+ @media (--viewport-medium) {
+ width: 18px;
+ }
+}
+.wrapper {
+ padding: 0 12px;
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
+.collection-filter {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 2fr 1px 10fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.divider.dark {
+ @media (--viewport-medium) {
+ border-left: 1px solid var(--dark-border);
+ }
+}
+
+.divider.light {
+ @media (--viewport-medium) {
+ border-left: 1px solid var(--light-border);
+ }
+}
+
+.subCollection-wrapper {
+ position: relative;
+ min-height: 100px;
+}
+.subCollection-stories {
+ @media (--viewport-medium) {
+ display: flex;
+ align-items: flex-start;
+ }
+}
+.cards {
+ display: block;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: var(--arrow-spacing-l);
+ flex-basis: 66%;
+ }
+}
+.last-column {
+ display: block;
+ @media (--viewport-medium) {
+ display: flex;
+ flex-direction: column;
+ margin-left: var(--arrow-spacing-l);
+ flex-basis: 34%;
+ }
+}
+
+.last-column .card {
+ margin-bottom: var(--arrow-spacing-l);
+}
+.cards .card {
+ margin-bottom: var(--arrow-spacing-l);
+ @media (--viewport-medium) {
+ margin-bottom: unset;
+ }
+}
+.card-wrapper {
+ @media (--viewport-medium) {
+ display: flex;
+ align-items: flex-start;
+ }
+}
+.child-collections {
+ display: flex;
+ font-size: var(--arrow-fs-xs);
+ margin-bottom: 16px;
+ scroll-snap-type: x mandatory;
+ overflow: scroll;
+ scrollbar-width: none;
+ @media (--viewport-medium) {
+ display: block;
+ font-size: var(--arrow-fs-s);
+ line-height: var(--arrow-lh-2);
+ overflow: none;
+ }
+}
+
+.child-collections::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+.child-collection.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.child-collection.light {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.child-collection {
+ margin: 0 11px 12px 11px;
+ cursor: pointer;
+ font-size: var(--arrow-fs-xs);
+ font-weight: var(--arrow-fw-bold);
+ @media (--viewport-medium) {
+ font-size: var(--arrow-fs-s);
+ margin: 0 0 var(--arrow-spacing-l) 0;
+ }
+}
+
+.child-collection-wrapper {
+ display: flex;
+ scroll-snap-align: start;
+}
+
+.child-collection.dark:hover,
+.open-subchild > .child-collection.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.child-collection.light:hover,
+.open-subchild > .child-collection.light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+.open-subchild {
+ border-bottom: 2px solid var(--arrow-c-brand1);
+ @media (--viewport-medium) {
+ border-bottom: unset;
+ }
+}
+
+.open-subchild > .navigator {
+ @media (--viewport-medium) {
+ visibility: visible;
+ width: 24px;
+ transition: width 0.35s ease-in-out;
+ animation: fadein 2s;
+ }
+}
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+/* tringle design */
+.navigator:before {
+ @media (--viewport-medium) {
+ content: "";
+ border-style: solid;
+ border-width: 4px 4px 4px 8px;
+ border-color: transparent transparent transparent var(--arrow-c-mono2);
+ position: absolute;
+ right: 0;
+ top: 8px;
+ }
+}
+
+.navigator.dark:before {
+ @media (--viewport-medium) {
+ border-color: transparent transparent transparent var(--arrow-c-mono2);
+ }
+}
+
+.navigator.light:before {
+ @media (--viewport-medium) {
+ border-color: transparent transparent transparent var(--arrow-c-invert-mono2);
+ }
+}
+
+.collection-filter .subCollection-stories {
+ animation-name: fadeIn;
+ animation-fill-mode: both;
+ animation-duration: 1s;
+ animation-delay: 1s;
+}
+
+html[dir="rtl"] {
+ .last-column {
+ @media (--viewport-medium) {
+ margin-right: var(--arrow-spacing-l);
+ margin-left: 0;
+ }
+ }
+
+ .open-subchild > .navigator {
+ height: var(--arrow-spacing-l);
+ transform: rotate(-180deg);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/CollectionFilter/index.js b/app/isomorphic/arrow/components/Rows/CollectionFilter/index.js
new file mode 100644
index 000000000..1ee2fcc98
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/CollectionFilter/index.js
@@ -0,0 +1,236 @@
+import React, { Fragment, useEffect, useState } from "react";
+import axios from "axios";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { getTextColor, getSlot, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { Loading } from "../../Svgs/Loading/loading";
+import { collectionToStories } from "@quintype/components";
+
+import "./collection-filter.m.css";
+import { useDispatch, useSelector } from "react-redux";
+
+const CollectionFilter = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ slotConfig = [],
+ border = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const { footerSlot } = footerSlotConfig;
+
+ const [subCollectionStories, handleApiData] = useState([]);
+ const [cachedStories, updateCachedStories] = useState({});
+ const [active, handleActive] = useState(0);
+ const [loading, handleLoading] = useState(true);
+
+ const getFilteredCollection = get(collection, ["items"], []).filter(
+ (collections) => collections.type === "collection"
+ );
+
+ if (getFilteredCollection.length < 1) {
+ return null;
+ }
+
+ const filteredCollectionids = getFilteredCollection.map((subCol) => subCol.id);
+
+ const memoizeStories = (id, data) => {
+ updateCachedStories((cachedStories) => ({ ...cachedStories, [id]: data }));
+ };
+
+ async function fetchSubCollectionData(id = filteredCollectionids[0]) {
+ handleLoading(true);
+ try {
+ let data = {};
+ if (!(id in cachedStories)) {
+ const res = await axios.get(`/api/v1/collections/${id}`);
+ data = res.data;
+ } else data = cachedStories[id];
+ memoizeStories(id, data);
+
+ const childCollectionItems = collectionToStories(data);
+ handleLoading(false);
+ handleApiData(childCollectionItems);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("API call failed", error);
+ }
+ }
+
+ useEffect((id) => {
+ fetchSubCollectionData(id);
+ }, []);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(getFilteredCollection[active], qtConfig);
+
+ const items = subCollectionStories.slice(0, 9);
+ const textColor = getTextColor(theme);
+
+ const openChildCollectionItems = (event, index, slug) => {
+ event.stopPropagation();
+ fetchSubCollectionData(slug);
+ const activeIndex = active === index ? 0 : index;
+ handleActive(activeIndex);
+ };
+
+ const getVerticalCard = (story, index) => (
+
+
+
+
+
+
+ );
+
+ const getHorizontalCard = (story, index) => (
+
+
+
+
+
+
+ );
+
+ const storySlot = (items) => {
+ return (
+
+ {items.slice(6, 9).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })}
+
+ );
+ };
+
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ return (
+
+
+
+
+
+ {getFilteredCollection.map((subCollections, index) => {
+ return (
+
+
openChildCollectionItems(event, index, subCollections.id)}
+ >
+ {subCollections.name}
+
+
+
+ );
+ })}
+
+
+
+ {loading ? (
+
+ ) : (
+
+
+ {items.slice(0, 1).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })}
+ {items.slice(1, 4).map((story, index) => {
+ return
{getVerticalCard(story, index)} ;
+ })}
+
+
+
+ {items.slice(4, 6).map((story, index) => {
+ return {getHorizontalCard(story, index)} ;
+ })}
+ {getSlot(type, component, () => storySlot(items))}
+
+
+ )}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+export default StateProvider(CollectionFilter);
+
+CollectionFilter.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // section tag border color
+ borderColor: PropTypes.string,
+ // background color of the row
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ // configure ad slot widget and story
+ slotConfig: PropTypes.array,
+ collectionNameTemplate: PropTypes.string,
+ footerSlotConfig: PropTypes.object,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+CollectionFilter.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
diff --git a/app/isomorphic/arrow/components/Rows/CollectionFilter/stories.js b/app/isomorphic/arrow/components/Rows/CollectionFilter/stories.js
new file mode 100644
index 000000000..9e8aeead8
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/CollectionFilter/stories.js
@@ -0,0 +1,78 @@
+/* eslint-disable prefer-regex-literals */
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import CollectionFilter from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+
+import { generateCollections, generateCollection } from "../../Fixture";
+
+const mock = new MockAdapter(axios);
+
+const API_REQUEST = new RegExp(`/api/v1/collections/*`);
+
+const nestedCollection = generateCollection({ stories: 10 });
+
+const collection = generateCollections(5);
+
+const defaultvalue = "#ffffff";
+const collectionNameDefaultValue = "#3a9fdd";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+const configurableSlot = () => {
+ return ;
+};
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Collection Filter",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ borderColor: color("Section Tag Border Color", sectionTagDefaultvalue),
+ theme: color("color", defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ mock.onGet(API_REQUEST).reply(200, nestedCollection);
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ElevenStories/11-stories.m.css b/app/isomorphic/arrow/components/Rows/ElevenStories/11-stories.m.css
new file mode 100644
index 000000000..9dfe7e100
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ElevenStories/11-stories.m.css
@@ -0,0 +1,85 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-small (width < 768px);
+
+.eleven-stories {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 2fr 1fr;
+ grid-gap: var(--arrow-spacing-l);
+ }
+}
+.wrapper-with-divider {
+ display: flex;
+ flex-direction: column;
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 1px 2fr 1px 1fr;
+ grid-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+.divider.dark {
+ border-left: 1px solid var(--dark-border);
+}
+.divider.light {
+ border-left: 1px solid var(--light-border);
+}
+.column-one {
+ border-bottom: 1px solid var(--arrow-c-mono5);
+ margin-bottom: var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ border-bottom: unset;
+ margin-bottom: unset;
+ padding-bottom: unset;
+ }
+}
+
+.column-two {
+ order: -1;
+ border-bottom: 1px solid var(--arrow-c-mono5);
+ margin-bottom: var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ order: 0;
+ border-bottom: unset;
+ margin-bottom: unset;
+ padding-bottom: unset;
+ }
+}
+.wrapper .column-one {
+ border-bottom: unset;
+}
+.wrapper .column-two {
+ border-bottom: unset;
+}
+
+.story-card {
+ margin-bottom: var(--arrow-spacing-s);
+}
+.first-story-card {
+ margin-bottom: var(--arrow-spacing-s);
+}
+.eleven-stories .child-collection-wrapper :global(.arr-collection-name-border-left) {
+ font-size: var(--arrow-fs-s);
+}
+
+.eleven-stories .child-collection-wrapper :global(.arr--collection-name) {
+ margin-bottom: var(--arrow-spacing-xs);
+ font-size: var(--arrow-fs-s);
+}
+
+.column-one > :first-child :global(.arr--sub-headline),
+.column-two > :last-child :global(.arr--sub-headline),
+.column-three > :first-child :global(.arr--sub-headline) {
+ @media (--viewport-small) {
+ display: none;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/ElevenStories/README.md b/app/isomorphic/arrow/components/Rows/ElevenStories/README.md
new file mode 100644
index 000000000..06d13e3dd
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ElevenStories/README.md
@@ -0,0 +1,39 @@
+# Eleven Stories
+
+The _11 stories_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with storycards.
+
+Last column of the 11 stories row supports collection, ad/widget also.
+
+## Usage
+
+### Import
+```jsx
+import { ElevenColGrid } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "solid",
+ showSection: true,
+ showRowTitle: true,
+ showAuthor: true,
+ showTime: true,
+ showSubheadline: true,
+ withseparator: true,
+ showFooterButton: false,
+ buttonText: "Load More",
+ slotConfig: [{ type: "story" }],
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/ElevenStories/index.js b/app/isomorphic/arrow/components/Rows/ElevenStories/index.js
new file mode 100644
index 000000000..1315ff3ab
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ElevenStories/index.js
@@ -0,0 +1,339 @@
+import React, { Fragment } from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { SectionTag } from "../../Atoms/SectionTag/index";
+import { getTextColor, getSlot, rgbToHex, navigateTo, generateNavigateSlug } from "../../../utils/utils";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StateProvider } from "../../SharedContext";
+
+import "./11-stories.m.css";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { useDispatch, useSelector } from "react-redux";
+
+const ElevenStories = ({ collection, config = {} }) => {
+ const storyItems = collectionToStories(collection);
+ if (storyItems.length < 1) {
+ return null;
+ }
+
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ slotConfig = [],
+ withseparator,
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ localizationConfig = {},
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const { footerSlot } = footerSlotConfig;
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const textColor = getTextColor(theme);
+ const SectionTagborderColor = rgbToHex(borderColor);
+ const getChildCollectionData = () => {
+ const filterCollection = get(collection, ["items"], []).find((collections) => collections.type === "collection");
+ return (
+
+
+ {storyItems.slice(0, 1).map((story, index) => {
+ return (
+
+ );
+ })}
+ {storyItems.slice(1, 4).map((story, index) => {
+ return (
+
+ );
+ })}
+ {storyItems.slice(4, 5).map((story, index) => {
+ return (
+
+ );
+ })}
+
+ );
+ };
+
+ const getLastSlotStoryData = () => {
+ return (
+
+ {storyItems.slice(6, 7).map((story, index) => {
+ return (
+
+ );
+ })}
+ {storyItems.slice(7, 10).map((story, index) => {
+ return (
+
+ );
+ })}
+ {storyItems.slice(10, 11).map((story, index) => {
+ return (
+
+ );
+ })}
+
+ );
+ };
+
+ const slot = getSlot(type, component, getLastSlotStoryData, getChildCollectionData);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ const getColumn = () => {
+ if (withseparator) {
+ return (
+ <>
+
+
+ {storyItems.slice(2, 3).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+ {storyItems.slice(3, 5).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+ {storyItems.slice(5, 6).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+
+
+
+ {storyItems.slice(0, 1).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+ {storyItems.slice(1, 2).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+
+
+ {slot}
+
+ >
+ );
+ } else
+ return (
+ <>
+
+
+ {storyItems.slice(2, 3).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+ {storyItems.slice(3, 6).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+
+
+ {storyItems.slice(0, 1).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+ {storyItems.slice(1, 2).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+
+ {slot}
+
+ >
+ );
+ };
+ return (
+
+
+
+ {getColumn()}
+ navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+ElevenStories.propTypes = {
+ /** stories is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ footerButton: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ withseparator: PropTypes.bool,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+export default StateProvider(ElevenStories);
diff --git a/app/isomorphic/arrow/components/Rows/ElevenStories/stories.js b/app/isomorphic/arrow/components/Rows/ElevenStories/stories.js
new file mode 100644
index 000000000..f3350824d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ElevenStories/stories.js
@@ -0,0 +1,70 @@
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import Readme from "./README.md";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import ElevenStories from "./index";
+import React from "react";
+import { generateCollectionsWithStories } from "../../Fixture";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+const collection = generateCollectionsWithStories(2);
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const footerSlot = () => {
+ return ;
+};
+
+// TODO: Add Ad/Widget Slot Config Option
+const slotConfigOptions = {
+ Stories: [{ type: "story" }],
+ Collection: [{ type: "collection" }],
+};
+
+withStore(
+ "Rows/Eleven Stories Row",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showSubheadline: boolean("Subheadline disable", true),
+ withseparator: boolean("separator", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ slotConfig: optionalSelect("Slot Config", slotConfigOptions, [{ type: "story" }]),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourColFiveStories/README.md b/app/isomorphic/arrow/components/Rows/FourColFiveStories/README.md
new file mode 100644
index 000000000..1048ba9dd
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColFiveStories/README.md
@@ -0,0 +1,37 @@
+# Four Column Five Stories Grid
+
+The _FourColGrid_ component is a basic four column five stories storycard.
+
+Last story of the four column grid supports an ad/widget slot.
+
+## Usage
+
+### Import
+```jsx
+import { FourColFiveStories } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourColFiveStories/four-col-five-stories.m.css b/app/isomorphic/arrow/components/Rows/FourColFiveStories/four-col-five-stories.m.css
new file mode 100644
index 000000000..31a9d5619
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColFiveStories/four-col-five-stories.m.css
@@ -0,0 +1,113 @@
+/* eslint-disable scss/at-rule-no-unknown, no-descending-specificity, no-duplicate-selectors */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.four-col-five-stories {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+.wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (min-width: desktop) {
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.first-card {
+ display: flex;
+ flex-direction: column;
+ gap: var(--arrow-spacing-xs);
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ :global(.arrow-component) h6 {
+ font-size: var(--arrow-spacing-l);
+ }
+ margin-bottom: var(--arrow-spacing-l);
+ }
+ @media (min-width: desktop) {
+ flex-direction: row;
+ align-items: center;
+ gap: var(--arrow-spacing-l);
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+
+.first-card {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ @media (--viewport-small) {
+ height: 24px;
+ width: 24px;
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+ }
+}
+
+.other-cards {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: var(--arrow-spacing-xs);
+ left: var(--arrow-spacing-xs);
+ z-index: var(--z-index-1);
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+.first-card > * {
+ flex: 1;
+}
+
+.other-cards {
+ display: grid;
+ grid-row-gap: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ grid-row-gap: var(--arrow-spacing-l);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
+
+.card > div > :last-child {
+ margin-top: var(--arrow-spacing-xs);
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourColFiveStories/index.js b/app/isomorphic/arrow/components/Rows/FourColFiveStories/index.js
new file mode 100644
index 000000000..009238f85
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColFiveStories/index.js
@@ -0,0 +1,162 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StateProvider } from "../../SharedContext";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { generateNavigateSlug, getSlot, getTextColor, navigateTo, rgbToHex } from "../../../utils/utils";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+import "./four-col-five-stories.m.css";
+
+const loadMore = ({ isLoading, renderStories, getMoreStories, subsequentLoadCount = 4 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = renderStories.length + 1;
+ getMoreStories(offset, limit);
+};
+
+const FourColFiveStories = ({
+ collection,
+ config,
+ getMoreStories,
+ isLoadMoreVisible,
+ isLoading,
+ isolatedLoadMore,
+ hideFirstCard,
+}) => {
+ const stories = collectionToStories(collection);
+ if (!stories.length) return null;
+
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ footerButton = "",
+ borderColor = "",
+ subsequentLoadCount = 4,
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const textColor = getTextColor(theme);
+ const sectionTagBorderColor = rgbToHex(borderColor);
+ const adWidgetSlot = type === "ad" || type === "widget" ? getSlot(type, component) : null;
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const [firstStory, ...otherStories] = stories || [];
+
+ const storyCard = (story) => (
+ <>
+
+
+ >
+ );
+
+ const otherCards = (story, index) => (
+
+ {storyCard(story)}
+
+ );
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ const renderStories = hideFirstCard ? stories : otherStories;
+
+ return (
+
+
+
+
+ {!hideFirstCard && (
+
+ {storyCard(firstStory)}
+
+ )}
+
+ {renderStories.slice(0, 3).map((story, index) => otherCards(story, index))}
+ {adWidgetSlot}
+ {renderStories.slice(3).map((story, index) => otherCards(story, index))}
+
+
+ {getLoadMore({ isLoading, renderStories, getMoreStories, subsequentLoadCount })}
+
+
+ );
+};
+
+FourColFiveStories.propTypes = {
+ collection: PropTypes.object,
+ config: PropTypes.shape({
+ collectionNameBorderColor: PropTypes.string,
+ theme: PropTypes.string,
+ slotConfig: PropTypes.array,
+ collectionNameTemplate: PropTypes.string,
+ footerButton: PropTypes.string,
+ borderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoading: PropTypes.bool,
+ hideFirstCard: PropTypes.bool,
+ isLoadMoreVisible: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+FourColFiveStories.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+ collection: {},
+ config: {},
+ hideFirstCard: false,
+};
+
+export default StateProvider(FourColFiveStories);
diff --git a/app/isomorphic/arrow/components/Rows/FourColFiveStories/stories.js b/app/isomorphic/arrow/components/Rows/FourColFiveStories/stories.js
new file mode 100644
index 000000000..58b737f35
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColFiveStories/stories.js
@@ -0,0 +1,67 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import Readme from "./README.md";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import TwoColSixStories from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 5 });
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const configurableSlot = () => {
+ return ;
+};
+
+const border = {
+ default: "fullBorder",
+ noBorder: "noBorder",
+};
+
+withStore(
+ "Rows/Four Col Five Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showSection: boolean("Section tag", true),
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showReadTime: boolean("Read time", true),
+ border: optionalSelect("Border settings", border),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourColGrid/README.md b/app/isomorphic/arrow/components/Rows/FourColGrid/README.md
new file mode 100644
index 000000000..6a90d4b71
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColGrid/README.md
@@ -0,0 +1,39 @@
+# Four Column Grid
+
+The _FourColGrid_ component is a basic four column storycard [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms).
+
+Last story of the four column grid supports an ad/widget slot.
+
+## Usage
+
+### Import
+```jsx
+import { FourColGrid } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => },
+ subsequentLoadCount: 8
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourColGrid/four-col.m.css b/app/isomorphic/arrow/components/Rows/FourColGrid/four-col.m.css
new file mode 100644
index 000000000..34cb97583
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColGrid/four-col.m.css
@@ -0,0 +1,50 @@
+@custom-media --viewport-medium (width >= 768px);
+.four-col-grid {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ margin: var(--arrow-spacing-xs) 0 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ grid-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+.story-card-wrapper {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: 0;
+ }
+}
+.wrapper > :first-child,
+.ad {
+ order: -1;
+ @media (--viewport-medium) {
+ order: 0;
+ }
+}
+.ad {
+ margin-bottom: 16px;
+}
+
+.content-wrapper {
+ display: flex;
+ flex-direction: column;
+ flex-basis: 70%;
+ @media (--viewport-medium) {
+ flex-basis: 100%;
+ }
+}
+
+.four-col-grid :global(.arr--story-card .arr--hero-image) {
+ flex-basis: 40%;
+}
+.four-col-grid :global(.arr--story-card .arr--content-wrapper) {
+ flex-basis: 60%;
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourColGrid/index.js b/app/isomorphic/arrow/components/Rows/FourColGrid/index.js
new file mode 100644
index 000000000..1c61e82a7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColGrid/index.js
@@ -0,0 +1,145 @@
+import React, { Fragment } from "react";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { HeroImage } from "../../Atoms/HeroImage";
+import get from "lodash/get";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { generateNavigateSlug, getTextColor, navigateTo } from "../../../utils/utils";
+import { collectionToStories } from "@quintype/components";
+import PropTypes from "prop-types";
+import { StateProvider } from "../../SharedContext";
+import "./four-col.m.css";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { useDispatch, useSelector } from "react-redux";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+
+const loadMore = ({ isLoading, storyItems, getMoreStories, subsequentLoadCount = 4 }) => {
+ if (isLoading) return; // prevent multiple clicks/calls
+ const limit = subsequentLoadCount;
+ const offset = storyItems.length;
+ getMoreStories(offset, limit);
+};
+
+const FourColGrid = ({ collection, config = {}, getMoreStories, isLoadMoreVisible, isLoading, isolatedLoadMore }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ customCollectionName,
+ navigate = true,
+ subsequentLoadCount = 4,
+ } = config;
+ const storyItems = collectionToStories(collection);
+
+ if (!storyItems.length) return null;
+
+ const { footerSlot } = footerSlotConfig;
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const textColor = getTextColor(theme);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ return (
+
+
+
+
+ {storyItems.map((story, index) => {
+ return (
+
+
+
+
+
+
+
+
+ );
+ })}
+
+ {getLoadMore({ isLoading, storyItems, getMoreStories, subsequentLoadCount })}
+ {footerSlotComp}
+
+
+ );
+};
+
+FourColGrid.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.bool,
+ slotConfig: PropTypes.array,
+ collectionNameTemplate: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+FourColGrid.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+};
+
+export default StateProvider(FourColGrid);
diff --git a/app/isomorphic/arrow/components/Rows/FourColGrid/stories.js b/app/isomorphic/arrow/components/Rows/FourColGrid/stories.js
new file mode 100644
index 000000000..a9f13c9ed
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColGrid/stories.js
@@ -0,0 +1,66 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import FourColGrid from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+ NavigateToPage: "NavigateToPage",
+};
+
+const footerSlot = () => {
+ return ;
+};
+withStore(
+ "Rows/Four Col Grid ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ subsequentLoadCount: text("Subsequent Load", 8),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourColPortraitStories/README.md b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/README.md
new file mode 100644
index 000000000..4685c623d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/README.md
@@ -0,0 +1,38 @@
+# Four Col Portrait Stories
+
+The _FourColPortraitStories_ component is a basic four column portrait storycard [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms).
+
+## Usage
+
+### Import
+
+```jsx
+import { FourColPortraitStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => },
+ subsequentLoadCount: 8
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourColPortraitStories/four-col-portrait-stories.m.css b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/four-col-portrait-stories.m.css
new file mode 100644
index 000000000..7f27aff0a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/four-col-portrait-stories.m.css
@@ -0,0 +1,45 @@
+@custom-media --viewport-medium (width > 1024px);
+.four-col-portrait {
+ padding: 0 var(--arrow-spacing-s);
+
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+
+.wrapper {
+ display: grid;
+ margin: var(--arrow-spacing-xs) 0 0;
+ grid-template-columns: repeat(2, 1fr);
+ grid-gap: var(--arrow-spacing-s);
+
+ @media (--viewport-medium) {
+ grid-template-columns: repeat(4, 1fr);
+ margin: var(--arrow-spacing-m) 0;
+ grid-gap: var(--arrow-spacing-l);
+ }
+
+ :global {
+ .arr--headline > a > h6 {
+ font-size: var(--arrow-fs-tiny);
+ @media (--viewport-medium) {
+ font-size: var(--arrow-fs-xs);
+ }
+ }
+
+ .arr--content-wrapper time,
+ .arr--read-time,
+ .author-name {
+ font-size: 12px;
+ @media (--viewport-medium) {
+ font-size: var(--arrow-fs-tiny);
+ }
+ }
+ }
+
+ @media (max-width: 1024px) and (min-width: 768px) {
+ :global .arr--headline > a > h6 {
+ font-size: var(--arrow-fs-l);
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourColPortraitStories/index.js b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/index.js
new file mode 100644
index 000000000..7bc62f0af
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/index.js
@@ -0,0 +1,146 @@
+import React from "react";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { generateNavigateSlug, getTextColor, navigateTo } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+import PortraitStoryCard from "../../Molecules/PortraitStoryCard";
+
+import "./four-col-portrait-stories.m.css";
+
+const loadMore = ({ isLoading, storyItems, getMoreStories, subsequentLoadCount = 4 }) => {
+ if (isLoading) return; // prevent multiple clicks/calls
+ const limit = subsequentLoadCount;
+ const offset = storyItems.length;
+ getMoreStories(offset, limit);
+};
+
+const FourColPortraitStories = ({
+ collection,
+ config = {},
+ getMoreStories,
+ isLoadMoreVisible,
+ isLoading,
+ isolatedLoadMore,
+}) => {
+ const storyItems = collectionToStories(collection);
+
+ if (!storyItems.length) return null;
+
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ customCollectionName,
+ navigate = true,
+ subsequentLoadCount = 4,
+ } = config;
+
+ const { footerSlot } = footerSlotConfig;
+ const footerSlotComp = typeof footerSlot === "function" ? footerSlot() : null;
+ const textColor = getTextColor(theme);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ return (
+
+
+
+
+ {storyItems.map((story, index) => {
+ return (
+
+ );
+ })}
+
+ {getLoadMore({ isLoading, storyItems, getMoreStories, subsequentLoadCount })}
+ {footerSlotComp}
+
+
+ );
+};
+
+FourColPortraitStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.bool,
+ collectionNameTemplate: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+FourColPortraitStories.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+};
+
+export default StateProvider(FourColPortraitStories);
diff --git a/app/isomorphic/arrow/components/Rows/FourColPortraitStories/stories.js b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/stories.js
new file mode 100644
index 000000000..28b500cd2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColPortraitStories/stories.js
@@ -0,0 +1,66 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import FourColPortraitStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+ NavigateToPage: "NavigateToPage",
+};
+
+const footerSlot = () => {
+ return ;
+};
+withStore(
+ "Rows/Four Col Portrait Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", false),
+ showAuthor: boolean("Author disable", false),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", false),
+ subsequentLoadCount: text("Subsequent Load", 8),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourColSixteenStories/README.md b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/README.md
new file mode 100644
index 000000000..8e06dd843
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/README.md
@@ -0,0 +1,35 @@
+# Four Col Sixteen Stories
+
+The _FourColSixteenStories_ component accepts collection of collections.
+
+## Usage
+
+### Import
+```jsx
+import { FourColSixteenStories } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "solid",
+ showSection: true,
+ showRowTitle: true,
+ showAuthor: true,
+ showTime: true,
+ showSubheadline: true,
+ withseparator: true,
+ showFooterButton: false,
+ buttonText: "Read More",
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourColSixteenStories/four-col-sixteen-stories.m.css b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/four-col-sixteen-stories.m.css
new file mode 100644
index 000000000..772dea752
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/four-col-sixteen-stories.m.css
@@ -0,0 +1,142 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.componentWrapper {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-column-gap: 25px;
+ grid-template-columns: repeat(2, 1fr);
+ @media (min-width: desktop) {
+ grid-template-columns: repeat(4, 1fr);
+ }
+ }
+}
+
+.cardWrapper {
+ @media (min-width: mobile) {
+ flex: auto;
+ }
+}
+
+.storyCardWrapper :global(.arr--story-card) {
+ padding: var(--arrow-spacing-xs) 0;
+}
+
+.storyCardWrapper > :first-child {
+ padding: 0 0 var(--arrow-spacing-xs);
+}
+
+.storyCardWrapper.storyCardWrapper > :last-child {
+ border-bottom: unset;
+}
+
+.separator {
+ @media (min-width: mobile) {
+ position: relative;
+ }
+}
+
+.separator::after {
+ @media (min-width: mobile) {
+ content: "";
+ width: 1px;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ right: -13px;
+ }
+}
+
+.light .separator::after {
+ @media (min-width: mobile) {
+ background-color: var(--light-border);
+ }
+}
+
+.dark .separator::after {
+ @media (min-width: mobile) {
+ background-color: var(--dark-border);
+ }
+}
+
+.componentWrapper > :last-child .separator::after {
+ @media (min-width: mobile) {
+ content: none;
+ }
+}
+
+.componentWrapper > :nth-child(2) .separator::after {
+ @media (min-width: mobile) {
+ content: none;
+ @media (min-width: desktop) {
+ content: "";
+ }
+ }
+}
+
+.contentWrapper {
+ @media (min-width: mobile) {
+ display: flex;
+ flex-direction: column;
+ }
+}
+
+.componentWrapper:global(.arrow-component.full-width-with-padding) {
+ @media (max-width: desktop) {
+ padding-left: var(--arrow-spacing-s);
+ padding-right: var(--arrow-spacing-s);
+ }
+}
+
+.componentWrapper > :nth-child(1) :global(.arr--button),
+.componentWrapper > :nth-child(2) :global(.arr--button) {
+ @media (max-width: desktop) {
+ margin-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.componentWrapper > :nth-child(3) :global(.arr--button) {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.storyCardWrapper {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ @media (--viewport-small) {
+ height: 24px;
+ width: 24px;
+ }
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourColSixteenStories/index.js b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/index.js
new file mode 100644
index 000000000..113ad0c6d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/index.js
@@ -0,0 +1,124 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import {
+ getTextColor,
+ generateNavigateSlug,
+ navigateTo,
+ rgbToHex,
+ getNumberOfStoriesToShow,
+} from "../../../utils/utils";
+import { collectionToStories } from "@quintype/components";
+import { StateProvider } from "../../SharedContext";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage/index";
+import { StoryCard } from "../../Molecules/StoryCard/index";
+import { useDispatch, useSelector } from "react-redux";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { SectionTag } from "../../Atoms/SectionTag/index";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import "./four-col-sixteen-stories.m.css";
+
+const getChildCollectionData = (collection, config, index) => {
+ const stories = collectionToStories(collection);
+ if (!stories.length) return null;
+
+ const [firstStory, ...otherStories] = stories;
+ const {
+ collectionNameTemplate,
+ borderColor = "",
+ withSeparator = true,
+ theme,
+ footerButton = "",
+ localizationConfig = {},
+ collectionNameBorderColor = "",
+ numberOfStoriesToShowInEachColumn = 4,
+ } = config;
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const SectionTagBorderColor = rgbToHex(borderColor);
+ const showNumberOfStoriesInEachColumn = getNumberOfStoriesToShow(numberOfStoriesToShowInEachColumn) - 1;
+
+ if (numberOfStoriesToShowInEachColumn < 1) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {otherStories.slice(0, showNumberOfStoriesInEachColumn).map((story, index) => {
+ return (
+
+
+
+
+
+ );
+ })}
+
+
+
navigateTo(dispatch, generateNavigateSlug(collection, qtConfig))}
+ qtConfig={qtConfig}
+ />
+
+ );
+};
+
+const FourColSixteenStories = ({ collection, config = {} }) => {
+ const childCollections = get(collection, ["items"], []).filter((collections) => collections.type === "collection");
+ if (childCollections.length < 4) return null;
+ // if number of collection is less than 4 return null
+
+ const { theme = "" } = config;
+ const textColor = getTextColor(theme);
+ return (
+
+ {childCollections.slice(0, 4).map((collection, index) => getChildCollectionData(collection, config, index))}
+
+ );
+};
+
+FourColSixteenStories.propTypes = {
+ collection: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ }),
+};
+
+export default StateProvider(FourColSixteenStories);
diff --git a/app/isomorphic/arrow/components/Rows/FourColSixteenStories/stories.js b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/stories.js
new file mode 100644
index 000000000..872c3a843
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColSixteenStories/stories.js
@@ -0,0 +1,47 @@
+import React from "react";
+import FourColSixteenStories from ".";
+import { generateCollections } from "../../Fixture";
+import { color, boolean, text, number } from "@storybook/addon-knobs";
+import Readme from "./README.md";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+
+const collection = generateCollections(4);
+
+withStore(
+ "Rows/Four Col Sixteen Stories",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", "#3a9fdd"),
+ borderColor: color("Section Tag Border Color", "#3a9fdd"),
+ theme: color("BG Color", "#ffffff"),
+ withSeparator: boolean("Separator", true),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ numberOfStoriesToShowInEachColumn: number("Number of stories to show in each column", 4),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourColTwelveStory/README.md b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/README.md
new file mode 100644
index 000000000..8db327dec
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/README.md
@@ -0,0 +1,39 @@
+# Four Column Twelve Stories
+
+The FourColTwelveStories component accepts collection of collections
+
+## Usage
+
+### Import
+
+```jsx
+import { FourColTwelveStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const otherTextData = {
+ text: "hello",
+ textColor: "#345678",
+ Icon: () =>
+};
+
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ footerSlotConfig: { footerSlot: () => },
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showRowTitle: true,
+ collectionNameTemplate: "default",
+ showSubheadline: true
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/FourColTwelveStory/four-col-twelve-story.m.css b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/four-col-twelve-story.m.css
new file mode 100644
index 000000000..fcc4095d7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/four-col-twelve-story.m.css
@@ -0,0 +1,71 @@
+@custom-media --viewport-medium (width >= 992px);
+@custom-media --viewport-tab (width >= 768px);
+
+.columns-container {
+ display: grid;
+ grid-template-columns: 1fr;
+ @media (--viewport-medium) {
+ grid-template-columns: repeat(4, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+
+ :global .arr--story-card {
+ margin-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.small-card-content-container {
+ margin-left: var(--arrow-spacing-xs);
+ @media (--viewport-medium) {
+ margin-left: var(--arrow-spacing-s);
+ }
+}
+
+html[dir="rtl"] {
+ .small-card-content-container {
+ margin-left: 0;
+ margin-right: var(--arrow-spacing-xs);
+ @media (--viewport-medium) {
+ margin-right: var(--arrow-spacing-s);
+ }
+ }
+
+ .arrow-icon {
+ transform: rotate(-180deg);
+ }
+
+ .ltr-icon {
+ display: none;
+ }
+}
+
+.other-news-container {
+ display: flex;
+ align-items: center;
+}
+
+.other-news-text {
+ margin-right: 6px;
+ font-size: var(--arrow-fs-xs);
+ font-weight: bold;
+ color: var(--arrow-c-mono1);
+}
+
+.other-news-link {
+ display: flex;
+ justify-content: flex-end;
+ margin: var(--arrow-spacing-l) 0;
+}
+
+.collection-name-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.other-news-top-container {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-tab) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourColTwelveStory/index.js b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/index.js
new file mode 100644
index 000000000..bed6edac2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/index.js
@@ -0,0 +1,190 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { collectionToStories, Link } from "@quintype/components";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { getTextColor, getSlug } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { StoryCard } from "../../Molecules/StoryCard/index";
+import "./four-col-twelve-story.m.css";
+
+function OtherNewsLinkComp({ slug, textColor, Icon, text }) {
+ return (
+
+
+
+ );
+}
+const SingleCol3Story = ({ collection, border, collectionNameTemplate, otherTextData, slug, theme, config = {} }) => {
+ const items = collectionToStories(collection);
+ if (items.length < 1) {
+ return null;
+ }
+ const [firstStory, ...rest] = items || [];
+ let text;
+ let Icon;
+ let textColor;
+ let contentPosition = "top";
+ const { localizationConfig = {} } = config;
+
+ if (otherTextData) {
+ text = otherTextData.text;
+ Icon = otherTextData.Icon;
+ textColor = otherTextData.textColor;
+ contentPosition = otherTextData.contentPosition || contentPosition;
+ }
+
+ return (
+
+
+
+ {contentPosition === "top" && text && Icon && (
+
+
+
+ )}
+
+
+
+
+ {rest.slice(0, 2).map((story, index) => {
+ return (
+
+
+
+
+ );
+ })}
+
+ {contentPosition === "bottom" && text && Icon && (
+
+
+
+ )}
+
+ );
+};
+
+const FourColTwelveStory = ({ collection = {}, publisherConfig = {}, config = {}, otherTextData }) => {
+ const { theme = "", border = "", footerSlotConfig = {}, collectionNameTemplate = "" } = config;
+ const { footerSlot } = footerSlotConfig;
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ const getFilteredCollection = get(collection, ["items"], []).filter(
+ (collections) => collections.type === "collection"
+ );
+
+ // if number of collection is less than 4 return null
+ if (getFilteredCollection.length < 4) return null;
+
+ const textColor = getTextColor(theme);
+
+ return (
+
+
+ {getFilteredCollection.slice(0, 4).map((collection, index) => {
+ const slugName = getSlug(collection, publisherConfig);
+ return (
+
+ );
+ })}
+
+ {footerSlotComp}
+
+ );
+};
+
+FourColTwelveStory.propTypes = {
+ collection: PropTypes.object.isRequired,
+ publisherConfig: PropTypes.object,
+ otherTextData: PropTypes.shape({
+ text: PropTypes.string,
+ Icon: PropTypes.func,
+ textColor: PropTypes.string,
+ }),
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ showSection: PropTypes.bool,
+ showSubheadline: PropTypes.bool,
+ showAuthor: PropTypes.bool,
+ showTime: PropTypes.bool,
+ showRowTitle: PropTypes.bool,
+ collectionNameTemplate: PropTypes.string,
+ }),
+};
+
+SingleCol3Story.propTypes = {
+ collection: PropTypes.object.isRequired,
+ border: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ slug: PropTypes.string,
+ theme: PropTypes.string,
+ otherTextData: PropTypes.shape({
+ text: PropTypes.string,
+ Icon: PropTypes.func,
+ textColor: PropTypes.string,
+ // can be either 'top' or 'bottom'
+ contentPosition: PropTypes.string,
+ }),
+ config: PropTypes.object,
+};
+
+OtherNewsLinkComp.propTypes = {
+ slug: PropTypes.string,
+ textColor: PropTypes.string,
+ Icon: PropTypes.func,
+ text: PropTypes.string,
+};
+
+export default StateProvider(FourColTwelveStory);
diff --git a/app/isomorphic/arrow/components/Rows/FourColTwelveStory/stories.js b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/stories.js
new file mode 100644
index 000000000..0d4758458
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourColTwelveStory/stories.js
@@ -0,0 +1,117 @@
+import React from "react";
+import { color, boolean } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, collectionNameTemplates } from "../../../../storybook";
+import FourColTwelveStory from "./index";
+import Readme from "././README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Carat from "../../Svgs/caret-right.svg";
+
+import { generateCollections } from "../../Fixture";
+
+const collection = generateCollections(4);
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const borderOptions = {
+ "No Value": "",
+ bottom: "bottom",
+};
+
+const otherTextPositionOptions = {
+ top: "top",
+ bottom: "bottom",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+const IconComp = (_) => (
+
+);
+
+withStore(
+ "Rows/Four Col 12 Stories",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "collection-slug",
+ name: "Health",
+ "section-url": "https://ace-web.qtstage.io/collection-slug",
+ id: 11181,
+ "parent-id": null,
+ "display-name": "Health",
+ collection: {
+ slug: "health",
+ name: "Health",
+ id: 15603,
+ },
+ data: null,
+ },
+ ],
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const otherTextData = {
+ text: "hello",
+ textColor: color(" text color", "#345678"),
+ contentPosition: optionalSelect("Other text Position", otherTextPositionOptions),
+ Icon: IconComp,
+ };
+
+ const publisherConfig = {
+ sections: [
+ {
+ "domain-slug": null,
+ slug: "collection-slug",
+ name: "Health",
+ "section-url": "https://ace-web.qtstage.io/collection-slug",
+ id: 11181,
+ "parent-id": null,
+ "display-name": "Health",
+ collection: {
+ slug: "health",
+ name: "Health",
+ id: 15603,
+ },
+ data: null,
+ },
+ ],
+ };
+
+ const contextConfig = {
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border Level", borderOptions),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showSection: boolean("Section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ collectionNameTemplate: optionalSelect("Collection row Templates", collectionNameTemplates),
+ showSubheadline: boolean("Subheadline", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return (
+
+ );
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySlider/README.md b/app/isomorphic/arrow/components/Rows/FourStorySlider/README.md
new file mode 100644
index 000000000..7463cefa7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySlider/README.md
@@ -0,0 +1,45 @@
+# Four Story Slider
+
+The _FourStorySlider_ component is a slider Row which shows 4 stories at a time on the Desktop and 1 at a time on mobile.
+
+## Usage
+
+### Import
+
+```jsx
+import { FourStorySlider } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true)
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySlider/four-story-slider.m.css b/app/isomorphic/arrow/components/Rows/FourStorySlider/four-story-slider.m.css
new file mode 100644
index 000000000..97783f8ca
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySlider/four-story-slider.m.css
@@ -0,0 +1,78 @@
+@custom-media --viewport-small (width < 768px);
+@custom-media --viewport-medium (width >= 768px);
+
+.four-story-slider-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+
+ :global .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ :global .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ .story-grid {
+ :global .arr--story-card {
+ margin-bottom: var(--arrow-fs-xs);
+ }
+ }
+
+ @media (--viewport-medium) {
+ .story-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ grid-gap: var(--arrow-fs-xs);
+ }
+ }
+
+ @media (--viewport-medium) {
+ :global .scroll-snap-carousel {
+ > div {
+ width: 33%;
+ margin: 0 1%;
+ }
+ }
+
+ :global .scroll-snap-carousel:nth-child(1) {
+ > div {
+ margin-left: 0;
+ }
+ }
+ }
+
+ @media (min-width: 992px) {
+ :global .scroll-snap-carousel {
+ > div {
+ width: 24%;
+ margin: 0 2%;
+ }
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySlider/index.js b/app/isomorphic/arrow/components/Rows/FourStorySlider/index.js
new file mode 100644
index 000000000..df58df199
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySlider/index.js
@@ -0,0 +1,129 @@
+import React, { useState, useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { StateProvider } from "../../SharedContext";
+import { getTextColor, getNumberOfStoriesToShow, navigateTo, generateNavigateSlug } from "../../../utils/utils";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import PropTypes from "prop-types";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { ScrollSnap } from "../../Atoms/ScrollSnap";
+
+import "./four-story-slider.m.css";
+
+const FourStorySlider = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ numberOfStoriesToShow = 0,
+ footerButton = "",
+ footerSlotConfig = {},
+ navigationArrows = true,
+ slideIndicator = "none",
+ isInfinite = false,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const items = collectionToStories(collection);
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+ const [perView, setPerView] = useState(1);
+
+ const showNumberOfStoriesToShow = getNumberOfStoriesToShow(numberOfStoriesToShow);
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ useEffect(() => {
+ const deviceWidth = get(global, ["innerWidth"], 480);
+ if (deviceWidth > 992) setPerView(4);
+ else if (deviceWidth > 764) setPerView(3);
+ else setPerView(1);
+ }, []);
+
+ const getItems = () => {
+ return items.slice(0, showNumberOfStoriesToShow).map((story, index) => {
+ return (
+
+
+
+
+ );
+ });
+ };
+
+ return (
+
+
+
+ {items.length > 4 ? (
+
+ {getItems()}
+
+ ) : (
+
{getItems()}
+ )}
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+FourStorySlider.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // section tag border color
+ borderColor: PropTypes.string,
+ // background color of the row
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ // configure ad slot widget and story
+ collectionNameTemplate: PropTypes.string,
+ autoplaySpeed: PropTypes.number,
+ // speed of the slider(ms)
+ numberOfStoriesToShow: PropTypes.number,
+ // no of slides in slider
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+FourStorySlider.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
+
+export default StateProvider(FourStorySlider);
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySlider/stories.js b/app/isomorphic/arrow/components/Rows/FourStorySlider/stories.js
new file mode 100644
index 000000000..3997350ef
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySlider/stories.js
@@ -0,0 +1,117 @@
+import React from "react";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import FourStorySlider from "./index";
+import { generateCollection } from "../../Fixture/";
+import { color, boolean, text, number } from "@storybook/addon-knobs";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+
+const collection = generateCollection({ stories: 13 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const navigationStyle = {
+ defaultvalue: "none",
+ dots: "dots",
+ dashes: "dashes",
+};
+const borderTemplate = {
+ default: " ",
+ border: "full",
+};
+const footerButton = {
+ NavigateToPage: "NavigateToPage",
+ SubsequentLoadCount: "SubsequentLoadCount",
+};
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Four Story Slider ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "ltr",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
+
+withStore(
+ "Rows/Four Story Slider ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "rtl",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Support Rtl ", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/README.md b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/README.md
new file mode 100644
index 000000000..6fd6da75c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/README.md
@@ -0,0 +1,45 @@
+# Four Story Slider Portrait
+
+The _FourStorySliderPortrait_ component is a slider Row which shows 5 stories with 20% of the fifth story at a time on the Desktop, 3 stories with 80% of third story at a time on Tablet and 2 stories with 20% of the second story at a time on mobile.
+
+## Usage
+
+### Import
+
+```jsx
+import { FourStorySliderPortrait } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true)
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/four-story-slider-portrait.m.css b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/four-story-slider-portrait.m.css
new file mode 100644
index 000000000..feee24178
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/four-story-slider-portrait.m.css
@@ -0,0 +1,272 @@
+/*eslint-disable no-descending-specificity, scss/operator-no-unspaced */
+@custom-media --viewport-medium (width >=768px);
+@custom-media --viewport-desktop (width >=992px);
+@custom-media --viewport-mobile (width >=400px);
+
+.four-story-slider-portrait-wrapper {
+ position: relative;
+ margin: 0 0 var(--arrow-spacing-m) var(--arrow-spacing-s);
+
+ @media (--viewport-medium) {
+ margin: 0 0 var(--arrow-spacing-m) var(--arrow-spacing-28);
+ }
+
+ @media (--viewport-desktop) {
+ margin: 0;
+ }
+
+ .story-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-gap: var(--arrow-fs-xs);
+
+ @media (--viewport-mobile) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ @media (--viewport-medium) {
+ grid-template-columns: repeat(4, 1fr);
+ }
+ }
+
+ :global {
+ .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ }
+
+ .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ .collection-crossLine:after {
+ margin-left: 0;
+ }
+
+ .collection-crossLine:before {
+ margin-right: 0;
+ }
+
+ .arrow-component {
+ color: var(--arrow-c-light);
+ }
+
+ .arr--headline h6 {
+ color: var(--arrow-c-light);
+ }
+
+ .author-name {
+ color: var(--arrow-c-light);
+ }
+
+ .arr--author-time > span > div {
+ display: block;
+ margin-bottom: var(--arrow-spacing-xxs);
+ fill: var(--arrow-c-invert-mono1);
+ }
+
+ .arr--author-time > span {
+ padding: 0 var(--arrow-spacing-xs);
+ }
+
+ .arr--ad-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ }
+
+ .arr--story-card {
+ margin-bottom: var(--arrow-fs-xs);
+ border-radius: var(--arrow-spacing-xxs);
+ }
+
+ .arr--section-name {
+ opacity: 0.7;
+ font-size: var(--arrow-fs-tiny);
+ line-height: var(--arrow-lh-1);
+ }
+
+ .arr-headline {
+ margin: 2px 0 var(--arrow-spacing-xs);
+ font-size: var(--arrow-fs-s);
+ font-weight: var(--arrow-fw-bold);
+ line-height: var(--arrow-lh-4);
+ }
+
+ .arr--content-wrapper {
+ border-radius: var(--arrow-spacing-xxs);
+
+ @media (--viewport-desktop) {
+ padding: var(--arrow-spacing-m);
+ }
+ }
+
+ .arr--content-wrapper time {
+ color: var(--arrow-c-light);
+ }
+
+ .read-time-wrapper {
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-xxs);
+ }
+ }
+
+ .arr--read-time > span:nth-child(1) > div {
+ fill: var(--arrow-c-invert-mono1);
+ position: relative;
+ top: -2px;
+ padding-left: var(--arrow-spacing-xs);
+ }
+
+ .arr--read-time > span:nth-child(2) {
+ margin-bottom: var(--arrow-spacing-xxs);
+ }
+
+ .arr--hero-image {
+ margin-bottom: 0;
+ }
+
+ .arr--story-card > div:nth-child(2) {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ color: var(--arrow-c-light);
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-m) var(--arrow-spacing-l);
+ }
+
+ .scroll-snap-carousel > div {
+ scroll-snap-align: end;
+ width: 70%;
+ margin: 0 0 0 4%;
+
+ @media (--viewport-medium) {
+ width: 33%;
+ margin: 0 0 0 3%;
+ }
+
+ @media (--viewport-desktop) {
+ scroll-snap-align: start;
+ width: 21%;
+ margin: 0 0 0 2%;
+ }
+ }
+
+ .scroll-snap-carousel > div:last-of-type {
+ @media (--viewport-medium) {
+ margin-left: 2%;
+ margin-right: 0;
+ }
+ }
+
+ .arr--story-card > a > figure {
+ border-radius: var(--arrow-spacing-xxs);
+ }
+
+ .arr--story-card > a > figure::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: linear-gradient(0deg, rgba(0, 0, 0, 0.7), transparent 50%, transparent);
+ }
+
+ .scroll-snap-carousel > div:first-of-type {
+ margin-left: 0;
+ }
+
+ .scroll-snap-wrapper button {
+ top: calc(25% + 85px);
+
+ @media (--viewport-medium) {
+ top: calc(25% + 110px);
+ }
+
+ @media (--viewport-desktop) {
+ top: calc(25% + 100px);
+ }
+ }
+
+ .arr--button {
+ margin: var(--arrow-spacing-l) auto var(--arrow-spacing-xs) auto;
+ }
+
+ .arrow-component li {
+ line-height: 0;
+ }
+
+ .slider-indicators .indicator {
+ margin: var(--arrow-spacing-l) 0 0;
+ padding: 0 var(--arrow-spacing-xxs);
+ }
+ }
+
+ .footer-slot-wrapper {
+ margin-top: var(--arrow-spacing-l);
+ }
+}
+
+html[dir="rtl"] {
+ .four-story-slider-portrait-wrapper {
+ margin: 0 var(--arrow-spacing-s) var(--arrow-spacing-m) 0;
+
+ @media (--viewport-medium) {
+ margin: 0 var(--arrow-spacing-28) var(--arrow-spacing-m) 0;
+ }
+
+ :global .arr--author-time > span {
+ display: none;
+ }
+
+ :global .scroll-snap-carousel > div {
+ @media (--viewport-desktop) {
+ margin: 0 2% 0 0;
+ }
+ }
+
+ :global .scroll-snap-carousel > div:last-of-type {
+ @media (--viewport-medium) {
+ margin-right: 2%;
+ }
+ }
+
+ :global .scroll-snap-carousel > div:first-of-type {
+ margin-right: 0;
+ }
+
+ :global .arr--seperator {
+ padding: 0 var(--arrow-spacing-xxs) 0;
+
+ @media (--viewport-mobile) {
+ display: none;
+ }
+
+ @media (--viewport-desktop) {
+ display: block;
+ }
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/index.js b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/index.js
new file mode 100644
index 000000000..6f6d25a48
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/index.js
@@ -0,0 +1,131 @@
+import { collectionToStories } from "@quintype/components";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import React, { useState, useEffect } from "react";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { getTextColor, getNumberOfStoriesToShow, navigateTo, generateNavigateSlug } from "../../../utils/utils";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { ScrollSnap } from "../../Atoms/ScrollSnap";
+import { StateProvider } from "../../SharedContext";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+
+import "./four-story-slider-portrait.m.css";
+
+const FourStorySliderPortrait = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ numberOfStoriesToShow = 0,
+ footerButton = "",
+ footerSlotConfig = {},
+ navigationArrows = true,
+ slideIndicator = "none",
+ isInfinite = false,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const items = collectionToStories(collection);
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+ const [perView, setPerView] = useState(1);
+
+ const showNumberOfStoriesToShow = getNumberOfStoriesToShow(numberOfStoriesToShow);
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ useEffect(() => {
+ const deviceWidth = get(global, ["innerWidth"], 480);
+ if (deviceWidth > 992) setPerView(4);
+ else if (deviceWidth > 764) setPerView(3);
+ else setPerView(1);
+ }, []);
+
+ const getItems = () => {
+ return items.slice(0, showNumberOfStoriesToShow).map((story, index) => {
+ return (
+
+
+
+
+ );
+ });
+ };
+
+ return (
+
+
+
+ {showNumberOfStoriesToShow > 4 ? (
+
+ {getItems()}
+
+ ) : (
+
+ {getItems()}
+
+ )}
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+FourStorySliderPortrait.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // section tag border color
+ borderColor: PropTypes.string,
+ // background color of the row
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ // configure ad slot widget and story
+ collectionNameTemplate: PropTypes.string,
+ autoplaySpeed: PropTypes.number,
+ // speed of the slider(ms)
+ numberOfStoriesToShow: PropTypes.number,
+ // no of slides in slider
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+FourStorySliderPortrait.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
+
+export default StateProvider(FourStorySliderPortrait);
diff --git a/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/stories.js b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/stories.js
new file mode 100644
index 000000000..304548ecf
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourStorySliderPortrait/stories.js
@@ -0,0 +1,117 @@
+import React from "react";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import FourStorySliderPortrait from "./index.js";
+import { generateCollection } from "../../Fixture";
+import { color, boolean, text, number } from "@storybook/addon-knobs";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+
+const collection = generateCollection({ stories: 13 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const navigationStyle = {
+ defaultvalue: "none",
+ dots: "dots",
+ dashes: "dashes",
+};
+const borderTemplate = {
+ default: " ",
+ border: "full",
+};
+const footerButton = {
+ NavigateToPage: "NavigateToPage",
+ SubsequentLoadCount: "SubsequentLoadCount",
+};
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Four Story Slider Portrait",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "ltr",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
+
+withStore(
+ "Rows/Four Story Slider Portrait",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "rtl",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Support Rtl ", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ numberOfStoriesToShow: number("Number of slide to show", 10),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/README.md b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/README.md
new file mode 100644
index 000000000..0d7b80084
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/README.md
@@ -0,0 +1,39 @@
+# FourTabbedBigStorySlider
+
+The _FourTabbedBigStorySlider_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with horizontal scroll to select respective story card.
+
+There are no configurable slots in this row.
+
+## Usage
+
+### Import
+
+```jsx
+import { FourTabbedBigStorySlider } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "LoadMore",
+ showRowTitle: true,
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/four-tabbed-big-story-slider.m.css b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/four-tabbed-big-story-slider.m.css
new file mode 100644
index 000000000..e1e57c158
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/four-tabbed-big-story-slider.m.css
@@ -0,0 +1,266 @@
+/* eslint-disable scss/at-rule-no-unknown, no-descending-specificity */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+@custom-media --viewport-mobile (width < 768px);
+@custom-media --viewport-desktop (width > 1024px);
+
+@media (--viewport-mobile) {
+ .four-tabbed-big-story-slider {
+ padding: 0 var(--arrow-spacing-s);
+ }
+ .story-card-content {
+ display: none;
+ }
+}
+
+.four-tabbed-big-story-slider {
+ :global {
+ .author-name,
+ .arr--publish-time,
+ .arr--read-time,
+ .section-tag {
+ color: var(--arrow-c-invert-mono1);
+ }
+
+ .arr-author-name {
+ margin-top: 0;
+ position: relative;
+ top: -4px;
+ margin-bottom: 0;
+ }
+
+ .arr--responsive-hero-image .story-icon,
+ .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ @media (--viewport-mobile) {
+ height: 24px;
+ width: 24px;
+ }
+ }
+
+ .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ .arr--author-time {
+ min-height: 16px;
+ }
+
+ .arr--publish-time {
+ position: relative;
+ top: -4px;
+ margin-bottom: 0;
+
+ div {
+ margin-bottom: 0;
+ }
+ }
+
+ .arr--read-time {
+ position: relative;
+ top: -4px;
+
+ span {
+ padding-bottom: 0;
+ }
+ }
+
+ .arr--read-time,
+ .arr-separator {
+ svg {
+ fill: var(--arrow-c-invert-mono1);
+ }
+
+ div {
+ position: relative;
+ top: -2px;
+ }
+ }
+
+ .arr--story-card > div:nth-child(2) {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 0 var(--arrow-spacing-96) var(--arrow-spacing-48);
+ }
+
+ .arr--story-card > a > figure::after {
+ background: linear-gradient(0deg, rgba(0, 0, 0, 0.7), transparent 75%, transparent);
+ }
+
+ .arr-separator {
+ top: -11px;
+ padding: 0 1px;
+ height: 3px;
+ }
+
+ .arr--button {
+ margin: var(--arrow-spacing-l) auto;
+ }
+
+ .arr--hero-image {
+ margin-bottom: 0;
+ }
+
+ .arr--sub-headline {
+ color: var(--arrow-c-light);
+ font-size: var(--arrow-fs-m);
+ line-height: var(--arrow-lh-4);
+ }
+ }
+}
+
+.light-wrapper {
+ :global {
+ .arr--collection-name h3 {
+ color: var(--arrow-c-invert-mono1);
+ }
+ }
+ .stories-wrapper::-webkit-scrollbar {
+ background-color: var(--arrow-c-invert-mono6);
+ }
+
+ .stories-wrapper::-webkit-scrollbar-thumb {
+ background-color: var(--arrow-c-invert-mono7);
+ }
+}
+
+.no-button-wrapper {
+ margin-top: var(--arrow-spacing-l);
+}
+
+.stories-wrapper {
+ display: flex;
+ overflow-x: scroll;
+ scrollbar-width: none;
+}
+
+.stories-wrapper::-webkit-scrollbar {
+ background-color: var(--arrow-c-invert-mono5);
+}
+
+.stories-wrapper::-webkit-scrollbar-thumb {
+ background-color: var(--arrow-c-mono7);
+ border-radius: var(--arrow-spacing-xs);
+}
+
+.story-headline-container {
+ display: flex;
+ scroll-snap-align: start;
+ padding: var(--arrow-spacing-l);
+ border-right: 1px solid var(--arrow-c-invert-mono8);
+ min-width: 80%;
+ cursor: pointer;
+ position: relative;
+
+ h6 {
+ margin-bottom: 0;
+ }
+
+ @media (min-width: mobile) {
+ min-width: 44%;
+ }
+ @media (min-width: desktop) {
+ min-width: 30%;
+ }
+}
+
+.story.dark:hover,
+.active-story > .story.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.story.light:hover,
+.active-story > .story.light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+.dark-headline-container {
+ background-color: var(--arrow-c-accent5);
+
+ h6 {
+ opacity: 0.64;
+ color: var(--arrow-c-mono1);
+ }
+}
+
+.active-dark-headline-container {
+ background-color: var(--arrow-c-accent6);
+ h6 {
+ opacity: 1;
+ }
+}
+
+.light-headline-container {
+ background-color: var(--arrow-c-invert-mono7);
+ border-color: var(--arrow-c-invert-mono9);
+
+ h6 {
+ opacity: 0.64;
+ color: var(--arrow-c-invert-mono1);
+ }
+}
+
+.active-light-headline-container {
+ background-color: var(--arrow-c-invert-mono6);
+
+ h6 {
+ opacity: 1;
+ }
+}
+
+.story-card-content {
+ h6 {
+ line-height: var(--arrow-lh-2);
+ font-size: var(--arrow-fs-big);
+ color: var(--arrow-c-light);
+ }
+}
+
+@media only screen and (max-width: desktop) {
+ .four-tabbed-big-story-slider {
+ :global {
+ .section-tag {
+ margin-bottom: var(--arrow-spacing-m);
+ }
+
+ .arr--story-card h6 {
+ display: none;
+ }
+ }
+ }
+}
+
+@media (--viewport-desktop) {
+ .four-tabbed-big-story-slider {
+ :global {
+ .arr--author-time {
+ margin-bottom: 8px;
+ }
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/index.js b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/index.js
new file mode 100644
index 000000000..b38faa477
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/index.js
@@ -0,0 +1,161 @@
+import React, { useState } from "react";
+import PropTypes from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+import { collectionToStories, Link } from "@quintype/components";
+import { StateProvider } from "../../SharedContext";
+import {
+ generateNavigateSlug,
+ getStoryUrl,
+ getTextColor,
+ isExternalStory,
+ navigateTo,
+ rgbToHex,
+} from "../../../utils/utils";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../../components/Molecules/StorycardContent";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { Subheadline } from "../../Atoms/Subheadline";
+
+import "./four-tabbed-big-story-slider.m.css";
+
+export const FourTabbedBigStorySlider = ({ collection, config = {} }) => {
+ const items = collectionToStories(collection);
+ if (items.length < 1) {
+ return null;
+ }
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ borderColor = "",
+ showSubheadline = true,
+ showButton = true,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const [currentStory, updateCurrentStory] = useState(items[0]);
+ const [active, handleActive] = useState(0);
+
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const textColor = getTextColor(theme);
+ const dispatch = useDispatch();
+
+ const updateActiveTab = (story, index) => {
+ updateCurrentStory(story);
+ handleActive(index);
+ };
+
+ const getActiveIndexStylename = (isActive) => {
+ if (isActive) {
+ return `active-story active-${textColor}-headline-container`;
+ } else return "";
+ };
+
+ const getCustomStyleName = textColor === "light" ? "light-wrapper" : "";
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {items.map((story, index) => (
+
updateActiveTab(story, index)}
+ key={story.id}
+ className={`story-headline-container ${getActiveIndexStylename(index === active)}`}
+ >
+
+
+ ))}
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+const DefaultStoryCardContent = ({ story, config = {}, borderColor, showSubheadline }) => {
+ const SectionTagborderColor = rgbToHex(borderColor);
+ const { localizationConfig = {} } = config;
+ return (
+
+
+
+
+ {showSubheadline && (
+
+
+
+ )}
+
+ );
+};
+
+export default StateProvider(FourTabbedBigStorySlider);
+
+FourTabbedBigStorySlider.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+DefaultStoryCardContent.propTypes = {
+ story: PropTypes.object.isRequired,
+ config: PropTypes.object,
+ borderColor: PropTypes.string,
+ showSubheadline: PropTypes.bool,
+};
diff --git a/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/stories.js b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/stories.js
new file mode 100644
index 000000000..884c71f2e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FourTabbedBigStorySlider/stories.js
@@ -0,0 +1,63 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import FourTabbedBigStorySlider from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Four Tabbed Big Story Slider ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ theme: color(label, defaultvalue),
+ showRowTitle: boolean("Row title", true),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ showSubheadline: boolean("Show subheadline", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/FullScreenSlider/README.md b/app/isomorphic/arrow/components/Rows/FullScreenSlider/README.md
new file mode 100644
index 000000000..5a2ef674b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FullScreenSlider/README.md
@@ -0,0 +1,46 @@
+# Full Screen Slider
+
+The _FullScreenSlider_ component is a slider Row which shows 1 story at a time on desktop and mobile.
+
+## Usage
+
+### Import
+
+```jsx
+import { FullScreenSlider } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showSubheadline: boolean("subheadline enable ", false),
+ isFullWidth: boolean("isFullWidth", false),
+ numberOfStoriesToShow: number("Number of slide to show", 8),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ contentAlignment: optionalSelect("content alignment", contentAlignment),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true)
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/FullScreenSlider/full-screen-slider.m.css b/app/isomorphic/arrow/components/Rows/FullScreenSlider/full-screen-slider.m.css
new file mode 100644
index 000000000..2db34304b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FullScreenSlider/full-screen-slider.m.css
@@ -0,0 +1,155 @@
+@custom-media --viewport-small (width < 768px);
+@custom-media --viewport-medium (width >= 768px);
+
+.full-screen-slider.container-width {
+ padding: 0 12px;
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
+.full-width :global(.arr--story-content) {
+ padding: 0 12px;
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
+
+.full-screen-slider-wrapper:global(.arrow-component.full-width-with-padding.full-width) {
+ padding: unset;
+}
+
+.container-width,
+.container-width :global(.arr--collection-name) {
+ max-width: 1140px;
+ margin: 0 auto;
+}
+.wrapper {
+ position: relative;
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.full-screen-slider :global(.arr--section-name) div,
+.full-screen-slider :global(.arr--headline) h1,
+.full-screen-slider :global(.arr-author-name),
+.full-screen-slider :global(.arr--publish-time),
+.full-screen-slider :global(.arr--sub-headline),
+.full-screen-slider :global(.author-name),
+.full-screen-slider :global(.arr-separator),
+.full-screen-slider :global(.arr-author-prefix),
+.full-screen-slider :global(.arr--read-time) {
+ @media (--viewport-medium) {
+ color: var(--arrow-c-light);
+ fill: var(--arrow-c-light);
+ }
+}
+.content-align :global(.arr--author-time) {
+ justify-content: center;
+}
+
+.content-align {
+ text-align: center;
+}
+
+.full-screen-slider :global(.arr--headline) h2 {
+ font-size: var(--arrow-fs-l);
+ @media (--viewport-medium) {
+ font-size: var(--arrow-fs-huge);
+ }
+}
+
+.full-screen-slider {
+ :global .left-arrow {
+ @media (--viewport-medium) {
+ background-color: var(--arrow-c-light);
+ }
+ }
+
+ @media (--viewport-medium) {
+ :global .right-arrow-rtl,
+ :global .left-arrow-ltr {
+ transform: translate(125px, -50%);
+ }
+ :global .left-arrow-rtl,
+ :global .right-arrow-ltr {
+ transform: translate(-125px, -50%);
+ }
+ }
+
+ @media (--viewport-small) {
+ :global .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ :global .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ }
+ }
+
+ @media (--viewport-medium) {
+ :global .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ :global .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ }
+ }
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ :global .right-arrow {
+ @media (--viewport-medium) {
+ background-color: var(--arrow-c-light);
+ }
+ }
+
+ @media (--viewport-medium) {
+ :global .left-arrow path,
+ :global .right-arrow path {
+ stroke: var(--arrow-c-dark);
+ }
+ :global .slider-indicators {
+ position: absolute;
+ bottom: var(--arrow-spacing-m);
+ }
+ :global .indicator-dots {
+ background-color: var(--arrow-c-light);
+ border-color: var(--arrow-c-light);
+ }
+
+ :global .indicator-dots[aria-pressed="true"],
+ :global .indicator-dashes [aria-pressed="true"] {
+ background-color: var(--arrow-c-light);
+ border-color: var(--arrow-c-light);
+ }
+
+ :global .indicator-dashes {
+ background-color: var(--arrow-c-light);
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/FullScreenSlider/index.js b/app/isomorphic/arrow/components/Rows/FullScreenSlider/index.js
new file mode 100644
index 000000000..996d56d77
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FullScreenSlider/index.js
@@ -0,0 +1,125 @@
+import React from "react";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { collectionToStories } from "@quintype/components";
+import PropTypes from "prop-types";
+import { getTextColor, getNumberOfStoriesToShow, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { ScrollSnap } from "../../Atoms/ScrollSnap";
+
+import "./full-screen-slider.m.css";
+
+const FullScreenSlider = ({ collection = {}, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ isFullWidth = false,
+ numberOfStoriesToShow = 1,
+ contentAlignment = "",
+ footerButton = "",
+ footerSlotConfig = {},
+ navigationArrows = true,
+ slideIndicator = "none",
+ isInfinite = false,
+ aspectRatio = [
+ [16, 9],
+ [4, 2],
+ ],
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const items = collectionToStories(collection);
+ const containerStyle = isFullWidth ? "full-width" : "container-width";
+ const alignment = contentAlignment === "center" ? "content-align" : "";
+
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const showNumberOfStoriesToShow = getNumberOfStoriesToShow(numberOfStoriesToShow);
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const imageAspectRatio = aspectRatio || [
+ [16, 9],
+ [4, 2],
+ ];
+ return (
+
+
+
+
+
+
+
+ {items.slice(0, showNumberOfStoriesToShow).map((story, index) => {
+ return (
+
+ );
+ })}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+FullScreenSlider.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // section tag border color
+ borderColor: PropTypes.string,
+ // backgroundColor and text color based on theme
+ theme: PropTypes.string,
+ // row title style
+ collectionTemplate: PropTypes.string,
+ // width of the slider
+ isFullWidth: PropTypes.bool,
+ // no of slides in slider
+ numberOfStoriesToShow: PropTypes.number,
+ // content alignment
+ contentAlignment: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ // aspect ratio of the image
+ aspectRatio: PropTypes.array,
+ }),
+};
+
+export default StateProvider(FullScreenSlider);
diff --git a/app/isomorphic/arrow/components/Rows/FullScreenSlider/stories.js b/app/isomorphic/arrow/components/Rows/FullScreenSlider/stories.js
new file mode 100644
index 000000000..8eb39a893
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/FullScreenSlider/stories.js
@@ -0,0 +1,115 @@
+import { color, boolean, number } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import FullScreenSlider from "./index";
+import { generateCollection } from "../../Fixture/";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import React from "react";
+import Readme from "./README.md";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const navigationStyle = {
+ defaultvalue: "none",
+ dots: "dots",
+ dashes: "dashes",
+};
+const contentAlignment = {
+ defaultvalue: "left",
+ center: "center",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Full Screen Slider ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showSubheadline: boolean("subheadline enable ", false),
+ isFullWidth: boolean("isFullWidth", false),
+ numberOfStoriesToShow: number("Number of slide to show", 8),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ contentAlignment: optionalSelect("content alignment", contentAlignment),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+});
+
+withStore(
+ "Rows/Full Screen Slider",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "rtl",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Support Rtl ", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showRowTitle: boolean("Row Title disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showSubheadline: boolean("subheadline enable ", false),
+ isFullWidth: boolean("isFullWidth", false),
+ numberOfStoriesToShow: number("Number of slide to show", 8),
+ isInfinite: boolean("Autoplay", false),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ contentAlignment: optionalSelect("content alignment", contentAlignment),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/HalfScreenSlider/README.md b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/README.md
new file mode 100644
index 000000000..e08249bd5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/README.md
@@ -0,0 +1,43 @@
+# Half Screen Slider
+
+The _HalfScreenSlider_ component is a slider Row which shows 1 story at a time on desktop and mobile. It is a variation of the Full Screen Slider with a different story card.
+
+## Usage
+
+### Import
+
+```jsx
+import { HalfScreenSlider } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ collectionNameTemplate: optionalSelect("Collection row Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ buttonText: text("Footer text"),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ numberOfStoriesToShow: number("Number of slide to show", 3),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true)
+};
+```
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/HalfScreenSlider/half-screen-slider.m.css b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/half-screen-slider.m.css
new file mode 100644
index 000000000..bd33ab454
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/half-screen-slider.m.css
@@ -0,0 +1,80 @@
+@custom-media --viewport-small (width < 768px);
+@custom-media --viewport-medium (width >= 768px);
+
+.half-screen-slider {
+ padding: var(--arrow-spacing-s) var(--arrow-spacing-s) 0;
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ margin: var(--arrow-spacing-m) 0;
+ }
+
+ @media (--viewport-small) {
+ :global .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ :global .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: var(--z-index-1);
+ }
+ }
+ @media (--viewport-medium) {
+ :global .scroll-snap-container .arr--responsive-hero-image .story-icon,
+ :global .scroll-snap-container .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: var(--z-index-1);
+ }
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+}
+
+.border.dark {
+ border: 1px solid var(--dark-border);
+}
+
+.border.light {
+ border: 1px solid var(--light-border);
+}
+
+.half-screen-slider :global(.arr--content) {
+ padding-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ padding-bottom: unset;
+ }
+}
+.half-screen-slider .border :global(.arr--content) {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-m) var(--arrow-spacing-xl) var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/HalfScreenSlider/index.js b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/index.js
new file mode 100644
index 000000000..58fca55a8
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/index.js
@@ -0,0 +1,105 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { SliderHorizontalCard } from "../../Molecules/SliderHorizontalCard";
+import { StateProvider } from "../../SharedContext";
+import { getTextColor, getNumberOfStoriesToShow, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { ScrollSnap } from "../../Atoms/ScrollSnap";
+
+import "./half-screen-slider.m.css";
+
+const HalfScreenSlider = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ numberOfStoriesToShow = 1,
+ collectionNameTemplate,
+ footerButton = "",
+ footerSlotConfig = {},
+ navigationArrows = true,
+ slideIndicator = "none",
+ isInfinite = false,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const items = collectionToStories(collection);
+ const textColor = getTextColor(theme);
+ const isBorderEnable = border === "full" ? "border" : "";
+ if (items.length < 1) {
+ return null;
+ }
+
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const showNumberOfStoriesToShow = getNumberOfStoriesToShow(numberOfStoriesToShow);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ return (
+
+
+
+
+
+ {items.slice(0, showNumberOfStoriesToShow).map((story, index) => {
+ return (
+
+ );
+ })}
+
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+ );
+};
+export default StateProvider(HalfScreenSlider);
+HalfScreenSlider.propTypes = {
+ footerButton: PropTypes.string,
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ // config is the configuration of the row
+ config: PropTypes.object.isRequired,
+ // border around the storycard
+ border: PropTypes.string,
+ // speed of the slider(ms)
+ autoplaySpeed: PropTypes.number,
+ // no of slides in slider
+ numberOfStoriesToShow: PropTypes.number,
+ // background and text color configuration
+ theme: PropTypes.string,
+ // style of the row title
+ collectionTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ // section tag border color
+ borderColor: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Rows/HalfScreenSlider/stories.js b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/stories.js
new file mode 100644
index 000000000..44b01cdbc
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/HalfScreenSlider/stories.js
@@ -0,0 +1,115 @@
+import React from "react";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import { color, boolean, text, number } from "@storybook/addon-knobs";
+import HalfScreenSlider from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import { generateCollection } from "../../Fixture";
+import Readme from "./README.md";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: " ",
+ border: "full",
+};
+
+const navigationStyle = {
+ defaultvalue: "none",
+ dots: "dots",
+ dashes: "dashes",
+};
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Half Screen Slider ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ collectionNameTemplate: optionalSelect("Collection row Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ buttonText: text("Footer text"),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ numberOfStoriesToShow: number("Number of slide to show", 3),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
+
+withStore(
+ "Rows/Half Screen Slider",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ language: {
+ direction: "rtl",
+ },
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Support Rtl ", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ collectionNameTemplate: optionalSelect("Collection row Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ buttonText: text("Footer text"),
+ slideIndicator: optionalSelect("Slide Indicator", navigationStyle),
+ navigationArrows: boolean("Arrows enable", true),
+ isInfinite: boolean("Autoplay", false),
+ numberOfStoriesToShow: number("Number of slide to show", 3),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ListComponent/README.md b/app/isomorphic/arrow/components/Rows/ListComponent/README.md
new file mode 100644
index 000000000..10d6ae792
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ListComponent/README.md
@@ -0,0 +1,48 @@
+# List Stories
+
+The _ListComponent_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with a list of storycards.
+
+## Usage
+
+### Import
+
+```jsx
+import { ListComponent } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ border: "bottom",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ showRowTitle: true
+};
+```
+
+### Use as a component
+
+#### Default Usage
+
+```jsx
+
+```
+
+#### ListComponent with border bottom
+
+```jsx
+
+```
+
+#### ListComponent with full border
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/ListComponent/index.js b/app/isomorphic/arrow/components/Rows/ListComponent/index.js
new file mode 100644
index 000000000..aaf4cae71
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ListComponent/index.js
@@ -0,0 +1,152 @@
+import React from "react";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { generateNavigateSlug, getTextColor, navigateTo } from "../../../utils/utils";
+import { collectionToStories } from "@quintype/components";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { SectionTag } from "../../Atoms/SectionTag";
+import PropTypes from "prop-types";
+import { StateProvider } from "../../SharedContext";
+import "./list-component.m.css";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+const ListComponent = ({ collection, config = {}, getMoreStories, limit, hideButton, authorPrefix = "By" }) => {
+ const storyItems = collectionToStories(collection);
+ if (storyItems.length < 1) {
+ return null;
+ }
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ collectionNameTemplate = "",
+ footerButton = "",
+ localizationConfig = {},
+ } = config;
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const borderHandler = (story) => {
+ switch (border) {
+ case "bottom":
+ return (
+
+
+
+ );
+ case "full":
+ return (
+
+ );
+ default:
+ return (
+
+
+
+ );
+ }
+ };
+
+ const textColor = getTextColor(theme);
+
+ return (
+
+
+
+
+
+ {storyItems.slice(0, limit).map((story, index) => {
+ return
{borderHandler(story)}
;
+ })}
+
+
+ {!hideButton && (
+
+ {getMoreStories ? (
+ navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ ) : null}
+
+ )}
+
+
+ );
+};
+ListComponent.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ // row title style
+ collectionNameTemplate: PropTypes.string,
+ // row title style colour
+ collectionNameBorderColor: PropTypes.string,
+ }),
+ getMoreStories: PropTypes.object.isRequired,
+ limit: PropTypes.number,
+ hideButton: PropTypes.bool,
+ authorPrefix: PropTypes.string,
+};
+
+export default StateProvider(ListComponent);
diff --git a/app/isomorphic/arrow/components/Rows/ListComponent/list-component.m.css b/app/isomorphic/arrow/components/Rows/ListComponent/list-component.m.css
new file mode 100644
index 000000000..99eef28de
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ListComponent/list-component.m.css
@@ -0,0 +1,65 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-small (width < 768px);
+
+.list-wrapper {
+ padding: var(--arrow-spacing-s);
+}
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: 44px;
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.one-col-border-full {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.one-col-border-bottom {
+ padding-top: var(--arrow-spacing-m);
+}
+.one-col-border-default {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.list {
+ grid-column: 1/3;
+ grid-row: 2/3;
+}
+
+.story-card-content-wrapper {
+ padding: var(--arrow-spacing-s) var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-m) 0;
+ }
+}
+
+.list :global .arr--hero-image {
+ @media (--viewport-medium) {
+ padding-right: 24px;
+ }
+}
+
+html[dir="rtl"] {
+ .list :global .arr--hero-image {
+ @media (--viewport-medium) {
+ padding-left: 24px;
+ padding-right: 0;
+ }
+ }
+}
+
+.list-wrapper :global(.arr--sub-headline) {
+ @media (--viewport-small) {
+ display: none;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/ListComponent/stories.js b/app/isomorphic/arrow/components/Rows/ListComponent/stories.js
new file mode 100644
index 000000000..c3ad556a4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ListComponent/stories.js
@@ -0,0 +1,63 @@
+import ListComponent from "./index";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { generateCollection } from "../../Fixture/";
+import readme from "./README.md";
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const collection = generateCollection({ stories: 10 });
+const borderOptions = {
+ "No Value": "",
+ bottom: "bottom",
+ full: "full",
+};
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+};
+
+const getMoreStories = () => {
+ console.log("load more data");
+};
+
+withStore(
+ "Rows/List stories",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ border: optionalSelect("Border Level", borderOptions),
+ showSection: boolean("Section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ initialLoadCount: text("Initial Load Count", 6),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/Listicles/README.md b/app/isomorphic/arrow/components/Rows/Listicles/README.md
new file mode 100644
index 000000000..fa728b4e5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/Listicles/README.md
@@ -0,0 +1,32 @@
+# Collection Filter
+
+The _Listicles_ component is a collection of collections in a tabbed interface which showcase onlu story headlines with number.
+
+The last 3 stories can be replaced with an Ad/Widget based on the slotConfig.
+
+## Usage
+
+### Import
+
+```jsx
+import { Listicles } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ slotConfig: ,
+ showRowTitle: true
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/Listicles/index.js b/app/isomorphic/arrow/components/Rows/Listicles/index.js
new file mode 100644
index 000000000..acce83839
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/Listicles/index.js
@@ -0,0 +1,165 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { getTextColor } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { Loading } from "../../Svgs/Loading/loading";
+import { Headline } from "../../Atoms/Headline";
+
+import "./listicles.m.css";
+
+const Listicles = ({ collection, config = {} }) => {
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ slotConfig = null,
+ localizedNumbers = null,
+ } = config;
+
+ const [subCollectionStories, handleApiData] = useState([]);
+ const [cachedStories, updateCachedStories] = useState({});
+ const [active, handleActive] = useState(0);
+ const [loading, handleLoading] = useState(true);
+
+ const getFilteredCollection = get(collection, ["items"], []).filter(
+ (collections) => collections.type === "collection"
+ );
+
+ if (getFilteredCollection.length < 1) {
+ return null;
+ }
+
+ const filteredCollectionids = getFilteredCollection.map((subCol) => subCol.id);
+
+ const memoizeStories = (id, data) => {
+ updateCachedStories((cachedStories) => ({ ...cachedStories, [id]: data }));
+ };
+
+ async function fetchSubCollectionData(id = filteredCollectionids[0]) {
+ handleLoading(true);
+ try {
+ let data = {};
+ if (!(id in cachedStories)) {
+ const res = await axios.get(`/api/v1/collections/${id}?limit=9`);
+ data = res.data;
+ } else data = cachedStories[id];
+ memoizeStories(id, data);
+
+ const childCollectionItems = collectionToStories(data);
+ handleLoading(false);
+ handleApiData(childCollectionItems);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("API call failed", error);
+ }
+ }
+
+ useEffect((id) => {
+ fetchSubCollectionData(id);
+ }, []);
+
+ const items = subCollectionStories.slice(0, 9);
+ const textColor = getTextColor(theme);
+
+ const openChildCollectionItems = (event, index, slug) => {
+ event.stopPropagation();
+ fetchSubCollectionData(slug);
+ const activeIndex = active === index ? 0 : index;
+ handleActive(activeIndex);
+ };
+
+ const getCustomStyleName = textColor === "light" ? "light-wrapper" : "";
+
+ const generateOpinions = () => {
+ if (slotConfig) {
+ return (
+
+ {getOpinions(6)}
+
{slotConfig()}
+
+ );
+ }
+
+ return getOpinions();
+ };
+
+ const getOpinions = (numberOfStories = 9) => {
+ return (
+
+ {items.slice(0, numberOfStories).map((story, index) => (
+
+ {localizedNumbers && localizedNumbers.length > 0 ? (
+
{localizedNumbers[index]}
+ ) : (
+
{index + 1}
+ )}
+
+
+ ))}
+
+ );
+ };
+
+ return (
+
+
+
+
+ {getFilteredCollection.map((subCollections, index) => {
+ return (
+
+
openChildCollectionItems(event, index, subCollections.id)}
+ >
+ {subCollections.name}
+
+
+ );
+ })}
+
+
+
{loading ? : generateOpinions()}
+
+
+ );
+};
+
+export default StateProvider(Listicles);
+
+Listicles.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ // background color of the row
+ theme: PropTypes.string,
+ // configure ad slot widget and story
+ slotConfig: PropTypes.func,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+Listicles.defaultProps = {
+ theme: "#ffffff",
+ slotConfig: "story",
+ border: "",
+};
diff --git a/app/isomorphic/arrow/components/Rows/Listicles/listicles.m.css b/app/isomorphic/arrow/components/Rows/Listicles/listicles.m.css
new file mode 100644
index 000000000..46c4a7420
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/Listicles/listicles.m.css
@@ -0,0 +1,149 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-desktop (width > 1024px);
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ margin-bottom: var(--arrow-spacing-m);
+
+ @media (--viewport-medium) {
+ padding: unset;
+ }
+}
+
+.child-collections {
+ display: flex;
+ font-size: var(--arrow-fs-xs);
+ margin-bottom: var(--arrow-spacing-l);
+ scroll-snap-type: x mandatory;
+ overflow: scroll;
+ scrollbar-width: none;
+ border-bottom: 1px solid var(--dark-border);
+}
+
+.child-collections::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+.child-collection.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.child-collection.light {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.child-collection {
+ margin: 0 var(--arrow-spacing-s) var(--arrow-spacing-s);
+ cursor: pointer;
+ font-size: var(--arrow-fs-xs);
+ font-weight: var(--arrow-fw-bold);
+}
+
+.child-collection-wrapper {
+ display: flex;
+ scroll-snap-align: start;
+}
+
+.child-collection.dark:hover,
+.open-subchild > .child-collection.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.child-collection.light:hover,
+.open-subchild > .child-collection.light {
+ color: var(--arrow-c-invert-mono1);
+}
+
+.open-subchild {
+ border-bottom: var(--arrow-spacing-xxs) solid var(--arrow-c-brand1);
+}
+
+.opinion-counter {
+ font-size: var(--arrow-fs-huge);
+ color: var(--arrow-c-mono5);
+ margin-right: var(--arrow-spacing-l);
+ position: relative;
+ top: -2px;
+}
+
+.light-wrapper {
+ :global {
+ .arr--collection-name h3,
+ .arr--headline h6 {
+ color: var(--arrow-c-invert-mono1);
+ }
+ }
+
+ .opinion-counter {
+ color: var(--arrow-c-invert-mono4);
+ }
+
+ .child-collections {
+ border-bottom: 1px solid var(--light-border);
+ }
+}
+
+html[dir="rtl"] {
+ .opinion-counter {
+ margin-left: var(--arrow-spacing-l);
+ margin-right: 0;
+ }
+}
+
+.opinions-wrapper {
+ display: grid;
+ grid-column-gap: var(--arrow-spacing-s);
+ grid-row-gap: var(--arrow-spacing-l);
+ margin-bottom: var(--arrow-spacing-l);
+
+ @media (--viewport-medium) {
+ grid-template-columns: repeat(2, 1fr);
+ margin-bottom: 0;
+ }
+
+ @media (--viewport-desktop) {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+.six-opinions-container {
+ display: grid;
+
+ @media (--viewport-medium) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ @media (--viewport-desktop) {
+ grid-template-columns: 2fr 1fr;
+ }
+
+ .opinions-wrapper {
+ display: grid;
+
+ @media (--viewport-medium) {
+ grid-template-columns: 1fr;
+
+ h6 {
+ margin-bottom: 0;
+ }
+ }
+ @media (--viewport-desktop) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ }
+}
+
+.opinion-wrapper {
+ display: flex;
+ margin: 0 var(--arrow-spacing-s);
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/Listicles/stories.js b/app/isomorphic/arrow/components/Rows/Listicles/stories.js
new file mode 100644
index 000000000..335ba940b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/Listicles/stories.js
@@ -0,0 +1,67 @@
+/* eslint-disable prefer-regex-literals */
+import React from "react";
+import { color, boolean } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, collectionNameTemplates } from "../../../../storybook";
+import Listicles from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+import axios from "axios";
+import MockAdapter from "axios-mock-adapter";
+
+import { generateCollections, generateCollection } from "../../Fixture";
+
+const mock = new MockAdapter(axios);
+
+const API_REQUEST = new RegExp(`/api/v1/collections/*`);
+
+const nestedCollection = generateCollection({ stories: 10 });
+
+const collection = generateCollections(5);
+
+const defaultvalue = "#ffffff";
+const collectionNameDefaultValue = "#3a9fdd";
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Listicles",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ theme: color("color", defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ slotConfig: configurableSlot,
+ showRowTitle: boolean("Row title", true),
+ };
+
+ mock.onGet(API_REQUEST).reply(200, nestedCollection);
+
+ return ;
+ })
+ .add("with localized numbers", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", collectionNameDefaultValue),
+ theme: color("color", defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ slotConfig: configurableSlot,
+ showRowTitle: boolean("Row title", true),
+ localizedNumbers: ["১", "২", "৩", "৪", "৫", "৬", "৭", "৮", "৯"],
+ };
+
+ mock.onGet(API_REQUEST).reply(200, nestedCollection);
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/MagazineEditions/README.md b/app/isomorphic/arrow/components/Rows/MagazineEditions/README.md
new file mode 100644
index 000000000..5a489dec3
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineEditions/README.md
@@ -0,0 +1,21 @@
+# MagazineEditions
+
+Magazine Editions is used to represent the issues of a magazine.
+
+## Usage
+
+Import the MagazineEditions and pass magazine collection, props and the config.
+
+```jsx
+import { MagazineEditions } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+};
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/MagazineEditions/index.js b/app/isomorphic/arrow/components/Rows/MagazineEditions/index.js
new file mode 100644
index 000000000..7c3a0d647
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineEditions/index.js
@@ -0,0 +1,92 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import { MagazineCoverImageCard } from "../../Atoms/MagazineCoverImage";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { generateNavigateSlug, getTextColor, getTimeStamp, navigateTo, timestampToFormat } from "../../../utils/utils";
+import "./magazine-editions.m.css";
+import { StateProvider } from "../../SharedContext";
+import get from "lodash/get";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+const MagazineEditions = ({ collection = [], config = {}, onClick, limit, showLoadmore = false }) => {
+ if (!collection.length) return null;
+ const {
+ theme = "",
+ collectionNameTemplate = "",
+ showRowTitle = true,
+ rowTitle = "Other Issues",
+ editionNameFormat = "magazineDate",
+ template = "NavigateToPage",
+ } = config;
+ const textColor = getTextColor(theme);
+ const issues = collection.map((issue) => issue.collection) || [];
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const dispatch = useDispatch();
+ const url = generateNavigateSlug(collection, { ...qtConfig, ...config });
+
+ return (
+
+
+ {showRowTitle && (
+
+ )}
+
+ {issues.slice(0, limit).map((issue, index) => {
+ const { name, "created-at": createdAt, "collection-date": issueDate } = issue;
+ const date = issueDate || createdAt;
+ const timeStampConfig = {
+ isUpperCase: true,
+ disableMeridiem: true,
+ };
+ return (
+
+
+
+ {editionNameFormat === "magazineTitle"
+ ? name
+ : getTimeStamp(date, timestampToFormat, timeStampConfig)}
+
+
+ );
+ })}
+
+ {showLoadmore && (
+
navigateTo(dispatch, url)}
+ />
+ )}
+
+
+ );
+};
+
+MagazineEditions.propTypes = {
+ collection: PropTypes.array,
+ config: PropTypes.shape({
+ rowTitle: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ theme: PropTypes.string,
+ editionNameFormat: PropTypes.string,
+ showRowTitle: PropTypes.bool,
+ }),
+ onClick: PropTypes.func,
+ limit: PropTypes.number,
+ showLoadmore: PropTypes.bool,
+};
+
+export default StateProvider(MagazineEditions);
diff --git a/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.m.css b/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.m.css
new file mode 100644
index 000000000..897646b74
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.m.css
@@ -0,0 +1,51 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value tablet from viewports;
+
+.container {
+ margin: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ margin: var(--arrow-spacing-l);
+ @media (min-width: tablet) {
+ margin: 0;
+ }
+ }
+}
+
+.issues {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ grid-row-gap: var(--arrow-spacing-l);
+ @media (min-width: mobile) {
+ grid-template-columns: repeat(4, 1fr);
+ @media (min-width: tablet) {
+ grid-column-gap: 70px;
+ }
+ }
+}
+
+.date {
+ display: flex;
+ justify-content: center;
+ margin-top: var(--arrow-spacing-s);
+ font-weight: var(--arrow-fw-bold);
+ line-height: var(--arrow-lh-2);
+ font-size: var(--arrow-fs-xs);
+ @media (min-width: mobile) {
+ margin-top: var(--arrow-spacing-m);
+ }
+}
+
+.info {
+ text-align: center;
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
diff --git a/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.test.js b/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.test.js
new file mode 100644
index 000000000..a4ed3f215
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineEditions/magazine-editions.test.js
@@ -0,0 +1,33 @@
+import React from "react";
+import { mount, shallow } from "enzyme";
+import MagazineEditions from ".";
+import { generateStore, generateMagazineIssues } from "../../Fixture";
+import { Provider } from "react-redux";
+
+const magazineCollection = generateMagazineIssues();
+
+describe("MagazineEditions component", () => {
+ it("Should render template with array of collections", () => {
+ const contextConfig = {
+ collectionNameBorderColor: "",
+ theme: "",
+ collectionNameTemplate: "",
+ footerButton: "",
+ editionsTitle: "",
+ enableEditionsTitle: true,
+ title: "",
+ };
+ const wrapper = mount(
+
+
+ );
+
+ );
+ expect(wrapper.find(MagazineEditions)).toHaveLength(1);
+ });
+
+ it("Should render `no editions message` if there are no editions", () => {
+ const wrapper = shallow( );
+ expect(wrapper.find(MagazineEditions)).toHaveLength(0);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/MagazineEditions/stories.js b/app/isomorphic/arrow/components/Rows/MagazineEditions/stories.js
new file mode 100644
index 000000000..bcff547ff
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineEditions/stories.js
@@ -0,0 +1,45 @@
+import React from "react";
+import { generateMagazineIssues } from "../../Fixture";
+import MagazineEditions from "./index";
+import Readme from "./README.md";
+import { color, text, boolean } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, collectionNameTemplates } from "../../../../storybook";
+
+const magazineCollection = generateMagazineIssues();
+
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+ CustomUrlPath: "CustomUrlPath",
+};
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+withStore(
+ "Rows/Magazine Editions",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Magazine Editions", () => {
+ const contextConfig = {
+ theme: color("Background Color", "#ffffff"),
+ showRowTitle: boolean("Show Row Title", true),
+ title: text("Magazine Row title", "Magazine Editions"),
+ collectionNameTemplate: optionalSelect("Collection row Templates", collectionNameTemplates),
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ buttonText: text("Footer text", "Read More"),
+ footerButton: optionalSelect("Button Settings", footerButton),
+ customUrlPath: text("Custom URL", ""),
+ enableEditionsTitle: boolean("Show Editions Title", true),
+ editionsTitle: text("Editions Title", "Magazine Title"),
+ showButton: boolean("Show Button", true),
+ initialLoadCount: text("Initial Load Count", 4),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/README.md b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/README.md
new file mode 100644
index 000000000..7744ac94d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/README.md
@@ -0,0 +1,21 @@
+# MagazineHeaderCard
+
+Magazine Header Card is used to display the brief content of the magazine.
+
+## Usage
+
+Import the MagazineHeaderCard and pass collection as props and the config.
+
+```jsx
+import { MagazineHeaderCard } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+};
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/index.js b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/index.js
new file mode 100644
index 000000000..cc6fd7dba
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/index.js
@@ -0,0 +1,73 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { generateNavigateSlug, getTextColor, getTimeStamp, navigateTo, timestampToFormat } from "../../../utils/utils";
+import "./magazine-cards.m.css";
+import { StateProvider } from "../../SharedContext";
+import { MagazineCoverImageCard } from "../../Atoms/MagazineCoverImage";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import upperCase from "lodash/upperCase";
+
+const MagazineHeaderCard = ({ collection = {}, config = {} }) => {
+ const { "created-at": createdAt, "collection-date": issueDate, summary } = collection;
+ const { theme = "", customUrlPath = "", magazineTitle = "", magazinePageUrl = "", magazineSlug = "" } = config;
+ const textColor = getTextColor(theme);
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig, customUrlPath);
+ const date = issueDate || createdAt;
+ const timeStampConfig = {
+ isUpperCase: true,
+ disableMeridiem: true,
+ };
+ return (
+
+
+
+
+
+
+
+ {summary}
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+
+
+ );
+};
+
+MagazineHeaderCard.propTypes = {
+ collection: PropTypes.shape({
+ "created-at": PropTypes.number,
+ summary: PropTypes.string,
+ items: PropTypes.array,
+ }),
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ footerButton: PropTypes.string,
+ magazineTitle: PropTypes.string,
+ magazinePageUrl: PropTypes.string,
+ customUrlPath: PropTypes.string,
+ }),
+};
+
+export default StateProvider(MagazineHeaderCard);
diff --git a/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/magazine-cards.m.css b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/magazine-cards.m.css
new file mode 100644
index 000000000..ed2f526d4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/magazine-cards.m.css
@@ -0,0 +1,107 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value tablet from viewports;
+
+.magazine-header {
+ display: grid;
+ grid-gap: var(--arrow-fs-l);
+ margin: 0 var(--arrow-fs-xs);
+ @media (min-width: mobile) {
+ grid-template-columns: 1fr 3fr;
+ }
+ @media (min-width: tablet) {
+ margin: 0 0 var(--arrow-spacing-xs);
+ grid-row-gap: 0;
+ grid-column-gap: 48px;
+ }
+}
+
+.content {
+ text-align: center;
+ @media (min-width: mobile) {
+ text-align: left;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+}
+
+.time {
+ font-size: var(--arrow-fs-xs);
+ font-weight: var(--arrow-fw-bold);
+ line-height: var(--arrow-lh-1);
+}
+
+.magazine-header :global(.arr--collection-name) {
+ margin: var(--arrow-spacing-xs) 0;
+}
+
+.story-cards {
+ display: grid;
+ grid-gap: var(--arrow-fs-xs);
+ grid-template-columns: 1fr;
+ @media (min-width: mobile) {
+ grid-template-columns: 1fr 1fr;
+ grid-gap: var(--arrow-spacing-l);
+ }
+ :global(.arr--story-card) {
+ display: grid;
+ grid-template-columns: 1fr 4fr;
+ }
+}
+
+.widget {
+ margin-top: var(--arrow-spacing-xl);
+ @media (min-width: mobile) {
+ grid-column: 1/3;
+ margin-top: var(--arrow-spacing-l);
+ }
+}
+
+.intro-button :global(.arr--button) {
+ @media (min-width: mobile) {
+ margin: var(--arrow-spacing-l) 0 0;
+ }
+}
+
+.widget-button :global(.arr--button) {
+ @media (min-width: tablet) {
+ margin: var(--arrow-spacing-l) 0 0;
+ }
+}
+
+.summary {
+ font-size: var(--arrow-fs-xs);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-s);
+ }
+}
+
+.summary.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.summary.light {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
+
+html[dir="rtl"] {
+ .time {
+ direction: ltr;
+ text-align: right;
+ }
+
+ .intro-button :global(.arr--button) {
+ @media (min-width: mobile) {
+ margin: var(--arrow-spacing-l) 0 0;
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/stories.js b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/stories.js
new file mode 100644
index 000000000..99e8b5573
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineHeaderCard/stories.js
@@ -0,0 +1,36 @@
+import React from "react";
+import { generateCollection } from "../../Fixture";
+import MagazineHeaderCard from "./index";
+import Readme from "./README.md";
+import { boolean, color, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect } from "../../../../storybook";
+
+const collection = generateCollection({ stories: 4 });
+
+const footerButton = {
+ NavigateToPage: "NavigateToPage",
+ CustomUrlPath: "CustomUrlPath",
+};
+
+withStore(
+ "Rows/Magazine Header Card",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Magazine header card", () => {
+ const contextConfig = {
+ theme: color("Color", "#ffffff"),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Subscribe"),
+ footerButton: optionalSelect("Button Settings", footerButton),
+ customUrlPath: text("Custom URL", ""),
+ magazineTitle: "Fortune",
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Rows/MagazineWidget/README.md b/app/isomorphic/arrow/components/Rows/MagazineWidget/README.md
new file mode 100644
index 000000000..1ccc8bda5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineWidget/README.md
@@ -0,0 +1,21 @@
+# MagazineWidget
+
+Magazine widget is used to represent the stories within the magazine.
+
+## Usage
+
+Import the MagazineWidget and pass collection, props and the config.
+
+```jsx
+import { MagazineWidget } from "@quintype/arrow";
+```
+
+### Use as a component
+
+```jsx
+const contextConfig = {
+ theme: #333333;
+};
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/MagazineWidget/index.js b/app/isomorphic/arrow/components/Rows/MagazineWidget/index.js
new file mode 100644
index 000000000..ff56ae12e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineWidget/index.js
@@ -0,0 +1,128 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import {
+ getTextColor,
+ getTimeStamp,
+ clientWidth,
+ generateNavigateSlug,
+ navigateTo,
+ timestampToFormat,
+} from "../../../utils/utils";
+import "../MagazineHeaderCard/magazine-cards.m.css";
+import { StateProvider } from "../../SharedContext";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { MagazineCoverImageCard } from "../../Atoms/MagazineCoverImage";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+const MagazineWidget = ({ collection = {}, config = {} }) => {
+ const { "created-at": createdAt, "collection-date": issueDate, summary, items, metadata = {} } = collection;
+ const { theme = "", footerButton = "", border, localizationConfig = {}, navigate = true, magazineSlug = "" } = config;
+ const textColor = getTextColor(theme);
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const updatedMagazineSlug =
+ magazineSlug || get(metadata, ["entities", "collectionEntities", "magazine", 0, "slug"], "");
+ const updatedConfig = { ...config, magazineSlug: updatedMagazineSlug };
+
+ const url = generateNavigateSlug(collection, {
+ ...qtConfig,
+ ...updatedConfig,
+ });
+ const sliceValue = 4;
+ const isTablet = clientWidth("tablet");
+ const timeStampConfig = {
+ isUpperCase: true,
+ disableMeridiem: true,
+ };
+ const date = issueDate || createdAt;
+
+ const storyCards = (items) => {
+ return (
+
+ {items.slice(0, sliceValue).map((storyItems, index) => {
+ const story = storyItems.story;
+ const isMobile = clientWidth("mobile");
+ const borderBottom = () => {
+ const lastStoryIndex = 3;
+ const lastTwoStoriesIndex = [2, 3];
+ if (isMobile && !lastStoryIndex === index) {
+ return "bottom";
+ }
+ if (!lastTwoStoriesIndex.includes(index)) {
+ return "bottom";
+ }
+ };
+
+ const borderSettings = border === "borderBottom" ? borderBottom() : "";
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+ })}
+
+ );
+ };
+
+ const bottomCard = () => {
+ return (
+
+ {storyCards(items)}
+ navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+ return (
+
+
+
+
+
+ {getTimeStamp(date, timestampToFormat, timeStampConfig)}
+
+
+
+ {summary}
+
+ {!isTablet && bottomCard()}
+
+ {isTablet && bottomCard()}
+
+
+ );
+};
+
+MagazineWidget.propTypes = {
+ collection: PropTypes.shape({
+ "created-at": PropTypes.number,
+ summary: PropTypes.string,
+ items: PropTypes.array,
+ }),
+ config: PropTypes.shape({ theme: PropTypes.string, footerButton: PropTypes.string, border: PropTypes.string }),
+};
+
+export default StateProvider(MagazineWidget);
diff --git a/app/isomorphic/arrow/components/Rows/MagazineWidget/stories.js b/app/isomorphic/arrow/components/Rows/MagazineWidget/stories.js
new file mode 100644
index 000000000..43d7403a5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/MagazineWidget/stories.js
@@ -0,0 +1,43 @@
+import React from "react";
+import { generateCollection } from "../../Fixture";
+import MagazineHeaderCard from "./index";
+import Readme from "./README.md";
+import { boolean, color, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect } from "../../../../storybook";
+
+const collection = generateCollection({ stories: 4 });
+
+const footerButton = {
+ NavigateToPage: "NavigateToPage",
+ CustomUrlPath: "CustomUrlPath",
+};
+
+const border = {
+ default: "borderNone",
+ borderBottom: "borderBottom",
+};
+
+withStore(
+ "Rows/Magazine Widget",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Magazine Widget", () => {
+ const contextConfig = {
+ theme: color("Color", "#ffffff"),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Subscribe"),
+ footerButton: optionalSelect("Button Settings", footerButton),
+ customUrlPath: text("Custom URL", ""),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Show Time", true),
+ border: optionalSelect("Border settings", border),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Rows/OneColStoryList/README.md b/app/isomorphic/arrow/components/Rows/OneColStoryList/README.md
new file mode 100644
index 000000000..c7f66d91c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OneColStoryList/README.md
@@ -0,0 +1,48 @@
+# One Column Story List
+
+The _OneColStoryList_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with a list of storycards.
+
+## Usage
+
+### Import
+```jsx
+import { OneColStoryList } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ border: "bottom",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "ad", component: () => }],
+ showFooterButton: true,
+ buttonText: "Load More",
+ showRowTitle: true,
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+#### Default Usage
+```jsx
+
+```
+
+#### OneColStoryList with border bottom
+
+```jsx
+
+```
+
+#### OneColStoryList with full border
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/OneColStoryList/index.js b/app/isomorphic/arrow/components/Rows/OneColStoryList/index.js
new file mode 100644
index 000000000..3edea9541
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OneColStoryList/index.js
@@ -0,0 +1,219 @@
+import { collectionToStories } from "@quintype/components";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import React from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { generateNavigateSlug, getSlot, getTextColor, navigateTo } from "../../../utils/utils";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StateProvider } from "../../SharedContext";
+import "./one-col-story-list.m.css";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+
+const loadMore = ({ isLoading, storyItems, getMoreStories, subsequentLoadCount = 8 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = storyItems.length;
+ getMoreStories(offset, limit);
+};
+
+const OneColStoryList = ({
+ collection,
+ config = {},
+ getMoreStories,
+ isLoadMoreVisible,
+ isLoading,
+ isolatedLoadMore,
+}) => {
+ const storyItems = collectionToStories(collection);
+ if (!storyItems.length) return null;
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ border = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ localizationConfig = {},
+ subsequentLoadCount = 8,
+ } = config;
+
+ const { footerSlot } = footerSlotConfig;
+ const { type, component } = get(slotConfig, [0], {});
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const borderHandler = (story) => {
+ switch (border) {
+ case "bottom":
+ return (
+
+
+
+ );
+ case "full":
+ return (
+
+ );
+ default:
+ return (
+
+
+
+ );
+ }
+ };
+
+ const textColor = getTextColor(theme);
+ return (
+
+
+
+
+
+ {storyItems.slice(0, 1).map((story, index) => {
+ return
{borderHandler(story)}
;
+ })}
+
+
+ {storyItems.slice(1).map((story, index) => {
+ return
{borderHandler(story)}
;
+ })}
+
+
+ {getSlot(
+ type,
+ component,
+ () => null,
+ () => null
+ )}
+
+
+
+ {getLoadMore({ isLoading, storyItems, getMoreStories, subsequentLoadCount })}
+
{footerSlotComp}
+
+
+
+ );
+};
+OneColStoryList.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ // row title style
+ collectionNameTemplate: PropTypes.string,
+ // row title style colour
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+OneColStoryList.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+};
+
+export default StateProvider(OneColStoryList);
diff --git a/app/isomorphic/arrow/components/Rows/OneColStoryList/one-col-story-list.m.css b/app/isomorphic/arrow/components/Rows/OneColStoryList/one-col-story-list.m.css
new file mode 100644
index 000000000..d031fdc15
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OneColStoryList/one-col-story-list.m.css
@@ -0,0 +1,81 @@
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-small (width < 768px);
+
+.one-col-story-list-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-column-gap: 44px;
+ }
+}
+.one-col-first-story {
+ grid-column: 1/3;
+ grid-row: 1/2;
+ order: -1;
+}
+.one-col-border-full {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.one-col-border-bottom {
+ padding-top: var(--arrow-spacing-m);
+}
+.one-col-border-default {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.one-col-story-list {
+ grid-column: 1/3;
+ grid-row: 2/3;
+}
+.one-col-ads {
+ grid-row: 1/9;
+ order: -1;
+ padding-bottom: var(--arrow-spacing-m);
+}
+.footer-ad-wrapper {
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+.footer-wrapper {
+ grid-row: 1/2;
+ grid-column: 1/3;
+}
+.ad-wrapper {
+ grid-row: 2/3;
+ grid-column: 1/4;
+}
+.ad-wrapper :global(.arr--ad-wrapper) {
+ justify-content: left;
+}
+.story-card-content-wrapper {
+ padding: 12px 8px 12px 0;
+ @media (--viewport-medium) {
+ padding: 16px 16px 16px 0;
+ }
+}
+
+.one-col-story-list-wrapper :global(.arr--sub-headline) {
+ @media (--viewport-small) {
+ display: none;
+ }
+}
+
+.one-col-story-list-wrapper :global(.arr--button) {
+ margin: 0 auto var(--arrow-spacing-xs) auto;
+}
diff --git a/app/isomorphic/arrow/components/Rows/OneColStoryList/stories.js b/app/isomorphic/arrow/components/Rows/OneColStoryList/stories.js
new file mode 100644
index 000000000..15c319670
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OneColStoryList/stories.js
@@ -0,0 +1,61 @@
+import OneColStoryList from "./index";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { generateCollection } from "../../Fixture/";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const collection = generateCollection({ stories: 10 });
+const borderOptions = {
+ "No Value": "",
+ bottom: "bottom",
+ full: "full",
+};
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+ NavigateToPage: "NavigateToPage",
+};
+const configurableSlot = () => {
+ return ;
+};
+const footerSlot = () => {
+ return ;
+};
+withStore("Rows/One Col Story List ", {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+})
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ border: optionalSelect("Border Level", borderOptions),
+ showSection: boolean("Section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/OpinionCollection/README.md b/app/isomorphic/arrow/components/Rows/OpinionCollection/README.md
new file mode 100644
index 000000000..75b5907ff
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OpinionCollection/README.md
@@ -0,0 +1,36 @@
+# Opinion Collection
+
+## Usage
+
+### Import
+
+```jsx
+import { OpinionCollection } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ showSection: true,
+ showAuthor: false,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ showRowTitle: true
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ slotConfig: [{ type: "ad", component: () => }],
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/OpinionCollection/index.js b/app/isomorphic/arrow/components/Rows/OpinionCollection/index.js
new file mode 100644
index 000000000..b30d6a030
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OpinionCollection/index.js
@@ -0,0 +1,157 @@
+import React from "react";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { collectionToStories } from "@quintype/components";
+import { Author } from "../../Atoms/Author";
+import { Headline } from "../../Atoms/Headline";
+import AuthorImage from "../../Atoms/AuthorImage";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StateProvider } from "../../SharedContext";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { generateNavigateSlug, getSlot, getTextColor, navigateTo } from "../../../utils/utils";
+import "./opinion-collection.m.css";
+
+const loadMore = ({ isLoading, renderStories, getMoreStories, subsequentLoadCount = 3 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = renderStories.length;
+ getMoreStories(offset, limit);
+};
+
+const OpinionCollection = ({ collection, config, getMoreStories, isLoadMoreVisible, isLoading, isolatedLoadMore }) => {
+ const stories = collectionToStories(collection);
+ const {
+ theme = "",
+ footerButton = "",
+ border = "fullBorder",
+ collectionNameBorderColor = "",
+ collectionNameTemplate = "",
+ slotConfig = [],
+ subsequentLoadCount = 3,
+ localizationConfig = {},
+ } = config;
+ const { type = "story", component = null } = get(slotConfig, [0], {});
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ if (!stories.length) return null;
+ const textColor = getTextColor(theme);
+ const borderStyle = border === "fullBorder" ? "border" : "";
+ const slot = type === "ad" ? getSlot(type, component) : null;
+ const adStyle = slot ? "custom-grid" : "";
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ const getAuthorCard = (story) => {
+ const authorData = get(story, ["authors", "0"]);
+ return (
+
+ );
+ };
+
+ const countStyleName = stories.length === 2 || stories.length === 3 ? `count-${stories.length}` : "";
+
+ return (
+
+
+
+
+ {slot ? (
+ <>
+
+ {stories.slice(0, 4).map((story) => getAuthorCard(story))}
+
+
{slot}
+ >
+ ) : (
+ stories.map((story) => getAuthorCard(story))
+ )}
+
+ {getLoadMore({ isLoading, stories, getMoreStories, subsequentLoadCount })}
+
+
+ );
+};
+
+OpinionCollection.propTypes = {
+ collection: PropTypes.object,
+ config: PropTypes.shape({
+ collectionNameBorderColor: PropTypes.string,
+ theme: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ footerButton: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ subsequentLoadCount: PropTypes.number,
+ localizationConfig: PropTypes.object,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoading: PropTypes.bool,
+ isLoadMoreVisible: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+OpinionCollection.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+ collection: {},
+ config: {},
+ hideFirstCard: false,
+};
+
+export default StateProvider(OpinionCollection);
diff --git a/app/isomorphic/arrow/components/Rows/OpinionCollection/opinion-collection.m.css b/app/isomorphic/arrow/components/Rows/OpinionCollection/opinion-collection.m.css
new file mode 100644
index 000000000..09d7c2f0c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OpinionCollection/opinion-collection.m.css
@@ -0,0 +1,227 @@
+/* eslint-disable scss/at-rule-no-unknown, no-descending-specificity*/
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.opinion-collection {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+
+.content-wrapper {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-s);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: repeat(3, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+}
+
+.custom-grid {
+ @media (min-width: mobile) {
+ grid-template-columns: 1fr;
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: 2fr 1fr;
+ align-items: center;
+ }
+}
+
+.wrapper-with-ads {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-s);
+ }
+ @media (min-width: desktop) {
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+}
+
+.ad-slot {
+ @media only screen and (max-width: desktop) {
+ margin: var(--arrow-spacing-l) 0;
+ }
+}
+
+.headline-author {
+ display: flex;
+ :last-child {
+ margin-left: auto;
+ }
+ :global(.arr--headline) {
+ flex-basis: 80%;
+ }
+}
+
+.content-wrapper > div,
+.wrapper-with-ads > div {
+ padding: 0 0 8px 0;
+ @media (min-width: desktop) {
+ padding: 0 var(--arrow-spacing-l) var(--arrow-spacing-m) 0;
+ }
+}
+
+.custom-grid > div {
+ padding: 0;
+}
+
+.content-wrapper.border.light > div,
+.content-wrapper.border.light .wrapper-with-ads > div {
+ @media (min-width: mobile) {
+ border-right: 1px solid var(--light-border);
+ }
+}
+
+.content-wrapper.border.dark > div,
+.content-wrapper.border.dark .wrapper-with-ads > div {
+ @media (min-width: mobile) {
+ border-right: 1px solid var(--dark-border);
+ }
+}
+
+.custom-grid.border.light > div,
+.custom-grid.border.dark > div {
+ border-right: none;
+}
+
+.content-wrapper :global(.author-name) {
+ color: var(--arrow-c-brand1);
+ font-size: var(--arrow-spacing-m);
+}
+
+.content-wrapper.border.light,
+.content-wrapper.border.light .wrapper-with-ads {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--light-border);
+ padding-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.content-wrapper.border.dark,
+.content-wrapper.border.dark .wrapper-with-ads {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--dark-border);
+ padding-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.content-wrapper.border,
+.content-wrapper.border .wrapper-with-ads {
+ div:nth-last-child(-n + 1) {
+ :global(.arr--author-time) {
+ border-bottom: none;
+ }
+ }
+ @media (min-width: mobile) {
+ div:nth-last-child(-n + 2) {
+ :global(.arr--author-time) {
+ border-bottom: none;
+ }
+ }
+ }
+ @media (min-width: desktop) {
+ div:nth-last-child(-n + 3) {
+ :global(.arr--author-time) {
+ border-bottom: none;
+ }
+ }
+ }
+}
+
+.content-wrapper.border.light .count-3 {
+ @media (min-width: desktop) {
+ div:nth-last-child(-n + 3) {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--light-border);
+ }
+ }
+ }
+}
+
+.content-wrapper.border.dark .count-3 {
+ @media (min-width: desktop) {
+ div:first-child {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--dark-border);
+ }
+ }
+ }
+}
+
+.content-wrapper.border.light .wrapper-with-ads {
+ @media (min-width: desktop) {
+ div:nth-child(2) {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--light-border);
+ }
+ }
+ }
+}
+
+.content-wrapper.border.dark .wrapper-with-ads {
+ @media (min-width: desktop) {
+ div:nth-child(2) {
+ :global(.arr--author-time) {
+ border-bottom: 1px solid var(--dark-border);
+ }
+ }
+ }
+}
+
+.content-wrapper.custom-grid.border.dark .count-3,
+.content-wrapper.border.dark .count-3,
+.content-wrapper.custom-grid.border.dark .count-2,
+.content-wrapper.border.dark .count-2 {
+ @media (min-width: desktop) {
+ div:nth-child(2) {
+ :global(.arr--author-time) {
+ border-bottom: none;
+ }
+ }
+ }
+}
+
+.content-wrapper.border.light > div:nth-child(2n),
+.content-wrapper.border.light .wrapper-with-ads > div:nth-child(2n) {
+ @media (min-width: mobile) {
+ border-right: none;
+ }
+ @media (min-width: desktop) {
+ border-right: 1px solid var(--light-border);
+ }
+}
+
+.content-wrapper.border.dark > div:nth-child(2n),
+.content-wrapper.border.dark .wrapper-with-ads > div:nth-child(2n) {
+ @media (min-width: mobile) {
+ border-right: none;
+ }
+ @media (min-width: desktop) {
+ border-right: 1px solid var(--dark-border);
+ }
+}
+
+.custom-grid.border.dark > div:nth-child(2n),
+.custom-grid.border.light > div:nth-child(2n) {
+ @media (min-width: desktop) {
+ border-right: none;
+ }
+}
+
+.content-wrapper.border.light > div:nth-child(3n) {
+ @media (min-width: desktop) {
+ border-right: none;
+ }
+}
+
+.content-wrapper.border.dark > div:nth-child(3n) {
+ @media (min-width: desktop) {
+ border-right: none;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/OpinionCollection/stories.js b/app/isomorphic/arrow/components/Rows/OpinionCollection/stories.js
new file mode 100644
index 000000000..08376663c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/OpinionCollection/stories.js
@@ -0,0 +1,82 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, collectionNameTemplates } from "../../../../storybook";
+import OpinionCollection from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 6 });
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const border = {
+ default: "fullBorder",
+ noBorder: "noBorder",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Opinion Collection",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Timestamp", true),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showReadTime: boolean("Read time", true),
+ border: optionalSelect("Border settings", border),
+ };
+ return ;
+ });
+
+withStore(
+ "Rows/Opinion Collection",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("With AD", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Timestamp", true),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showReadTime: boolean("Read time", true),
+ border: optionalSelect("Border settings", border),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/SixColSixStories/README.md b/app/isomorphic/arrow/components/Rows/SixColSixStories/README.md
new file mode 100644
index 000000000..3fe39fc75
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/SixColSixStories/README.md
@@ -0,0 +1,37 @@
+# Six Col Six Stories
+
+The _SixColSixStories_ component is a six column grid with six stories
+
+## Usage
+
+### Import
+
+```jsx
+import { SixColSixStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "borderLeft",
+ sectionTagTemplate: "borderBottomSml",
+ showSection: true,
+ showRowTitle: true,
+ showAuthor: true,
+ showTime: true,
+ showSubheadline: true,
+ showFooterButton: false,
+ buttonText: "Read More",
+ border: "fullBorder"
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/SixColSixStories/index.js b/app/isomorphic/arrow/components/Rows/SixColSixStories/index.js
new file mode 100644
index 000000000..9622d1fd4
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/SixColSixStories/index.js
@@ -0,0 +1,148 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { getTextColor, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+
+import "./six-col-six-stories.m.css";
+import { useDispatch, useSelector } from "react-redux";
+
+const loadMore = ({ isLoading, stories, getMoreStories, subsequentLoadCount = 6 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = stories.length;
+ getMoreStories(offset, limit);
+};
+
+const SixColSixStories = ({
+ collection = {},
+ config = {},
+ getMoreStories,
+ isLoadMoreVisible,
+ isLoading,
+ isolatedLoadMore,
+}) => {
+ const stories = collectionToStories(collection);
+ if (!stories.length) return null;
+
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ footerButton = "",
+ border = "",
+ localizationConfig = {},
+ subsequentLoadCount = 6,
+ } = config;
+
+ const textColor = getTextColor(theme);
+ const borderStyle = border === "fullBorder" ? "vertical-border" : "";
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ return (
+
+
+
+
+ {stories.map((story, index) => (
+
+ ))}
+
+ {getLoadMore({ isLoading, stories, getMoreStories, subsequentLoadCount })}
+
+
+ );
+};
+export default StateProvider(SixColSixStories);
+
+SixColSixStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+SixColSixStories.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+};
diff --git a/app/isomorphic/arrow/components/Rows/SixColSixStories/six-col-six-stories.m.css b/app/isomorphic/arrow/components/Rows/SixColSixStories/six-col-six-stories.m.css
new file mode 100644
index 000000000..6ea85b928
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/SixColSixStories/six-col-six-stories.m.css
@@ -0,0 +1,76 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+
+.row-wrapper {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ @media (min-width: mobile) {
+ grid-template-columns: repeat(3, 1fr);
+ grid-column-gap: var(--arrow-spacing-28);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: repeat(6, 1fr);
+ grid-column-gap: var(--arrow-spacing-28);
+ }
+}
+
+.card-wrapper {
+ margin-bottom: var(--arrow-spacing-m);
+}
+
+.card-wrapper.vertical-border {
+ position: relative;
+}
+
+.card-wrapper.vertical-border::after {
+ content: "";
+ width: 1px;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ right: -13px;
+}
+
+.card-wrapper.vertical-border.light::after {
+ background-color: var(--light-border);
+}
+
+.card-wrapper.vertical-border.dark::after {
+ background-color: var(--dark-border);
+}
+
+.card-wrapper.vertical-border:nth-child(2n)::after {
+ content: none;
+ @media (min-width: mobile) {
+ content: "";
+ }
+}
+
+.card-wrapper.vertical-border:nth-child(3n)::after {
+ @media (min-width: mobile) {
+ content: none;
+ }
+ @media (min-width: desktop) {
+ content: "";
+ }
+}
+
+.card-wrapper.vertical-border:nth-child(6n)::after {
+ @media (min-width: desktop) {
+ content: none;
+ }
+}
+
+.content-wrapper {
+ height: 100%;
+}
diff --git a/app/isomorphic/arrow/components/Rows/SixColSixStories/stories.js b/app/isomorphic/arrow/components/Rows/SixColSixStories/stories.js
new file mode 100644
index 000000000..b519f9ef5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/SixColSixStories/stories.js
@@ -0,0 +1,73 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import SixColSixStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 6 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#ff5858";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#ff5858";
+
+const borderTemplate = {
+ default: "noBorder",
+ verticalBorder: "fullBorder",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Six Col Six Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/README.md b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/README.md
new file mode 100644
index 000000000..d6c96cd20
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/README.md
@@ -0,0 +1,19 @@
+# Listicle Story Template
+
+Listicle story template is a basic component used to represent a Listicle story.
+
+## Usage
+
+## Default template
+
+```jsx
+
+```
+
+## Template with Children components
+
+```jsx
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/__snapshots__/listicle-story.test.js.snap b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/__snapshots__/listicle-story.test.js.snap
new file mode 100644
index 000000000..b5769e1eb
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/__snapshots__/listicle-story.test.js.snap
@@ -0,0 +1,841 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Listicle Story Templates Should render listicle story template with section tag 1`] = `
+
+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 9,
+ },
+ Object {
+ "card-added-at": 1602155520864,
+ "card-updated-at": 1602155725655,
+ "content-id": "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ "content-version-id": "43ff8777-4176-4294-95a3-5aaee8e34537",
+ "id": "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Credits: Asif Asharaf",
+ "caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "11f060e0-a1aa-4963-9778-24f6b6eca401",
+ "id": "82244bcd-c453-41c4-a215-bb038f569375",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/82244bcd-c453-41c4-a215-bb038f569375",
+ "subtype": null,
+ "text": "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ "title": "",
+ "type": "title",
+ },
+ Object {
+ "description": "",
+ "family-id": "a970d458-c7d8-4512-8420-4bf3a4046caf",
+ "id": "ae7131f6-450a-4589-9995-812ba5832c58",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/ae7131f6-450a-4589-9995-812ba5832c58",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 7,
+ },
+ Object {
+ "card-added-at": 1602155541845,
+ "card-updated-at": 1602156067786,
+ "content-id": "d6876a49-9319-4c24-9bf9-881ea454b967",
+ "content-version-id": "dbfeb570-f9aa-4875-9c3d-f48bf551dd91",
+ "id": "d6876a49-9319-4c24-9bf9-881ea454b967",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Jon Doe",
+ "caption": null,
+ "key": "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ "metadata": Object {
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ "file-size": 26544,
+ "height": 540,
+ "mime-type": "image/jpeg",
+ "width": 960,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "1b8bcf8e-19cb-4d16-ac0e-bb4f0939dbbf",
+ "id": "d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ "subtype": null,
+ "text": "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ "title": "",
+ "type": "title",
+ },
+ Object {
+ "description": "",
+ "family-id": "6d5d82b8-f879-476d-84a2-1f62f74acdd5",
+ "hyperlink": "https://www.google.com/",
+ "id": "a7f79ffa-4be0-4207-a495-a035a020cac3",
+ "image-attribution": "Jon Doe",
+ "image-metadata": Object {
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ "file-size": 26544,
+ "height": 540,
+ "mime-type": "image/jpeg",
+ "width": 960,
+ },
+ "image-s3-key": "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/a7f79ffa-4be0-4207-a495-a035a020cac3",
+ "subtype": null,
+ "title": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
+ "type": "image",
+ },
+ Object {
+ "description": "",
+ "family-id": "b9a1fdd8-5727-4e5a-86a6-467922aa155f",
+ "id": "2a248f86-3181-4456-9f96-f7e56668fbc1",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/2a248f86-3181-4456-9f96-f7e56668fbc1",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 10,
+ },
+ Object {
+ "card-added-at": 1602156035607,
+ "card-updated-at": 1602156067786,
+ "content-id": "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ "content-version-id": "dc523cfa-dcf2-41b5-ab7a-b5c3577e11f7",
+ "id": "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Credits: Asif Asharaf",
+ "caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "b8f84eb7-4a91-45ba-a5d3-95d5f48c7772",
+ "id": "20db973e-4612-4933-9acd-5a6d4c3d281d",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/20db973e-4612-4933-9acd-5a6d4c3d281d",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ Object {
+ "description": "",
+ "id": "00000000-0000-0000-0000-000000000000",
+ "metadata": Object {
+ "promotional-message": true,
+ },
+ "subtype": null,
+ "text": " text promotional message
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 2,
+ },
+ ],
+ "comments": null,
+ "content-created-at": 1602154991378,
+ "content-type": "story",
+ "content-updated-at": 1602234199729,
+ "contributors": Array [],
+ "created-at": 1602234185901,
+ "custom-slug": null,
+ "customSlotAfterStory": Object {
+ "config": Object {
+ "targetingId": "",
+ },
+ "layout": "Leaderboard",
+ "layoutLabel": "Leaderboard",
+ "type": "ad",
+ },
+ "entities": Object {},
+ "external-id": null,
+ "first-published-at": 1602155728566,
+ "headline": "It pays to be kind: improving workplace culture through kindness",
+ "hero-image-attribution": "Credits: Asif Asharaf",
+ "hero-image-caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "hero-image-hyperlink": "https://www.google.com",
+ "hero-image-metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "hero-image-s3-key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "id": "307e5a1b-5cf0-4321-8193-827b2add174f",
+ "is-amp-supported": true,
+ "is-live-blog": false,
+ "last-published-at": 1602234199516,
+ "linked-entities": Array [],
+ "linked-story-ids": Array [],
+ "metadata": Object {
+ "card-share": Object {
+ "shareable": false,
+ },
+ },
+ "owner-id": 927927,
+ "owner-name": "Pravin Atigre",
+ "promotional-message": " text promotional message
",
+ "publish-at": null,
+ "published-at": 1602234199516,
+ "publisher-id": 97,
+ "push-notification": null,
+ "push-notification-title": null,
+ "read-time": 2,
+ "sections": Array [
+ Object {
+ "collection": Object {
+ "id": 15603,
+ "name": "Health",
+ "slug": "health",
+ },
+ "data": null,
+ "display-name": "Health",
+ "domain-slug": null,
+ "id": 11181,
+ "name": "Health",
+ "parent-id": 38586,
+ "section-url": "https://ace-web.qtstage.io/news/health",
+ "slug": "health",
+ },
+ ],
+ "seo": Object {
+ "claim-reviews": Object {
+ "story": null,
+ },
+ },
+ "sequence-no": null,
+ "slug": "health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "status": "published",
+ "story-audio": Object {
+ "s3-key": "ace/story-audio/2020-10/307e5a1b-5cf0-4321-8193-827b2add174f/.0e6d9a89-3bb7-47b0-8bee-a34b5f75f5e9.mp3",
+ },
+ "story-content-id": "307e5a1b-5cf0-4321-8193-827b2add174f",
+ "story-template": "listicle",
+ "story-version-id": "8768d161-6433-4f1e-b8ef-e632d180ede9",
+ "storyline-id": null,
+ "storyline-title": null,
+ "subheadline": "Neque porro quisquam est, qui dolorem ipsum quia dolor sit.",
+ "summary": null,
+ "tags": Array [
+ Object {
+ "id": 1421524,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "health",
+ "slug": "health",
+ "tag-type": "Tag",
+ },
+ Object {
+ "id": 1421525,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "culture",
+ "slug": "culture",
+ "tag-type": "Tag",
+ },
+ Object {
+ "id": 1421526,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "workplace",
+ "slug": "workplace",
+ "tag-type": "Tag",
+ },
+ ],
+ "updated-at": 1602234193104,
+ "url": "https://ace-web.qtstage.io/health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "version": 18,
+ "votes": Object {},
+ "word-count": 232,
+ }
+ }
+ storyElementsConfig={
+ Object {
+ "also-read": Object {},
+ "answer": Object {},
+ "blockquote": Object {},
+ "blurb": Object {},
+ "q-and-a": Object {},
+ "question": Object {},
+ "quote": Object {},
+ "references": Object {},
+ "summary": Object {},
+ }
+ }
+ />
+
+`;
+
+exports[`Listicle Story Templates Should render listicle story template without section tag 1`] = `
+
+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 9,
+ },
+ Object {
+ "card-added-at": 1602155520864,
+ "card-updated-at": 1602155725655,
+ "content-id": "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ "content-version-id": "43ff8777-4176-4294-95a3-5aaee8e34537",
+ "id": "70988ea9-90a0-42ea-b2b1-1f7374de06e1",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Credits: Asif Asharaf",
+ "caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "11f060e0-a1aa-4963-9778-24f6b6eca401",
+ "id": "82244bcd-c453-41c4-a215-bb038f569375",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/82244bcd-c453-41c4-a215-bb038f569375",
+ "subtype": null,
+ "text": "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ "title": "",
+ "type": "title",
+ },
+ Object {
+ "description": "",
+ "family-id": "a970d458-c7d8-4512-8420-4bf3a4046caf",
+ "id": "ae7131f6-450a-4589-9995-812ba5832c58",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/ae7131f6-450a-4589-9995-812ba5832c58",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 7,
+ },
+ Object {
+ "card-added-at": 1602155541845,
+ "card-updated-at": 1602156067786,
+ "content-id": "d6876a49-9319-4c24-9bf9-881ea454b967",
+ "content-version-id": "dbfeb570-f9aa-4875-9c3d-f48bf551dd91",
+ "id": "d6876a49-9319-4c24-9bf9-881ea454b967",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Jon Doe",
+ "caption": null,
+ "key": "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ "metadata": Object {
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ "file-size": 26544,
+ "height": 540,
+ "mime-type": "image/jpeg",
+ "width": 960,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "1b8bcf8e-19cb-4d16-ac0e-bb4f0939dbbf",
+ "id": "d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/d3cbfcce-a503-499d-80cd-52fce2215ef3",
+ "subtype": null,
+ "text": "Nemo enim ipsam voluptatem quia volup tas sit aspernatur aut odit aut fugit",
+ "title": "",
+ "type": "title",
+ },
+ Object {
+ "description": "",
+ "family-id": "6d5d82b8-f879-476d-84a2-1f62f74acdd5",
+ "hyperlink": "https://www.google.com/",
+ "id": "a7f79ffa-4be0-4207-a495-a035a020cac3",
+ "image-attribution": "Jon Doe",
+ "image-metadata": Object {
+ "file-name": "quintype-website_2020-01_2f2ba42d-7a45-4a60-b4af-7d4758f27f5d_Untitled_presentation.jpg",
+ "file-size": 26544,
+ "height": 540,
+ "mime-type": "image/jpeg",
+ "width": 960,
+ },
+ "image-s3-key": "ace/2020-10/920f35b9-023e-44d2-b4de-69dce8587bf7/quintype_website_2020_01_2f2ba42d_7a45_4a60_b4af_7d4758f27f5d_Untitled_presentation.jpg",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/a7f79ffa-4be0-4207-a495-a035a020cac3",
+ "subtype": null,
+ "title": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
+ "type": "image",
+ },
+ Object {
+ "description": "",
+ "family-id": "b9a1fdd8-5727-4e5a-86a6-467922aa155f",
+ "id": "2a248f86-3181-4456-9f96-f7e56668fbc1",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/2a248f86-3181-4456-9f96-f7e56668fbc1",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 10,
+ },
+ Object {
+ "card-added-at": 1602156035607,
+ "card-updated-at": 1602156067786,
+ "content-id": "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ "content-version-id": "dc523cfa-dcf2-41b5-ab7a-b5c3577e11f7",
+ "id": "539c1ed1-79e5-4771-a304-60b46d8376c6",
+ "metadata": Object {
+ "attributes": Object {},
+ "social-share": Object {
+ "image": Object {
+ "attribution": "Credits: Asif Asharaf",
+ "caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "url": null,
+ },
+ "message": null,
+ "shareable": false,
+ "title": "It pays to be kind: improving workplace culture through kindness",
+ },
+ },
+ "status": "draft",
+ "story-elements": Array [
+ Object {
+ "description": "",
+ "family-id": "b8f84eb7-4a91-45ba-a5d3-95d5f48c7772",
+ "id": "20db973e-4612-4933-9acd-5a6d4c3d281d",
+ "metadata": Object {},
+ "page-url": "/story/307e5a1b-5cf0-4321-8193-827b2add174f/element/20db973e-4612-4933-9acd-5a6d4c3d281d",
+ "subtype": null,
+ "text": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est
",
+ "title": "",
+ "type": "text",
+ },
+ Object {
+ "description": "",
+ "id": "00000000-0000-0000-0000-000000000000",
+ "metadata": Object {
+ "promotional-message": true,
+ },
+ "subtype": null,
+ "text": " text promotional message
",
+ "title": "",
+ "type": "text",
+ },
+ ],
+ "version": 2,
+ },
+ ],
+ "comments": null,
+ "content-created-at": 1602154991378,
+ "content-type": "story",
+ "content-updated-at": 1602234199729,
+ "contributors": Array [],
+ "created-at": 1602234185901,
+ "custom-slug": null,
+ "customSlotAfterStory": Object {
+ "config": Object {
+ "targetingId": "",
+ },
+ "layout": "Leaderboard",
+ "layoutLabel": "Leaderboard",
+ "type": "ad",
+ },
+ "entities": Object {},
+ "external-id": null,
+ "first-published-at": 1602155728566,
+ "headline": "It pays to be kind: improving workplace culture through kindness",
+ "hero-image-attribution": "Credits: Asif Asharaf",
+ "hero-image-caption": "Saque ipsa quae ab illo inventore veritatis et quip",
+ "hero-image-hyperlink": "https://www.google.com",
+ "hero-image-metadata": Object {
+ "file-name": "seo.jpg",
+ "file-size": 228062,
+ "height": 900,
+ "mime-type": "image/jpeg",
+ "width": 1600,
+ },
+ "hero-image-s3-key": "ace/2020-10/ec88fad0-ed4d-44ec-9930-72e9d897bdd0/seo.jpg",
+ "id": "307e5a1b-5cf0-4321-8193-827b2add174f",
+ "is-amp-supported": true,
+ "is-live-blog": false,
+ "last-published-at": 1602234199516,
+ "linked-entities": Array [],
+ "linked-story-ids": Array [],
+ "metadata": Object {
+ "card-share": Object {
+ "shareable": false,
+ },
+ },
+ "owner-id": 927927,
+ "owner-name": "Pravin Atigre",
+ "promotional-message": " text promotional message
",
+ "publish-at": null,
+ "published-at": 1602234199516,
+ "publisher-id": 97,
+ "push-notification": null,
+ "push-notification-title": null,
+ "read-time": 2,
+ "sections": Array [
+ Object {
+ "collection": Object {
+ "id": 15603,
+ "name": "Health",
+ "slug": "health",
+ },
+ "data": null,
+ "display-name": "Health",
+ "domain-slug": null,
+ "id": 11181,
+ "name": "Health",
+ "parent-id": 38586,
+ "section-url": "https://ace-web.qtstage.io/news/health",
+ "slug": "health",
+ },
+ ],
+ "seo": Object {
+ "claim-reviews": Object {
+ "story": null,
+ },
+ },
+ "sequence-no": null,
+ "slug": "health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "status": "published",
+ "story-audio": Object {
+ "s3-key": "ace/story-audio/2020-10/307e5a1b-5cf0-4321-8193-827b2add174f/.0e6d9a89-3bb7-47b0-8bee-a34b5f75f5e9.mp3",
+ },
+ "story-content-id": "307e5a1b-5cf0-4321-8193-827b2add174f",
+ "story-template": "listicle",
+ "story-version-id": "8768d161-6433-4f1e-b8ef-e632d180ede9",
+ "storyline-id": null,
+ "storyline-title": null,
+ "subheadline": "Neque porro quisquam est, qui dolorem ipsum quia dolor sit.",
+ "summary": null,
+ "tags": Array [
+ Object {
+ "id": 1421524,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "health",
+ "slug": "health",
+ "tag-type": "Tag",
+ },
+ Object {
+ "id": 1421525,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "culture",
+ "slug": "culture",
+ "tag-type": "Tag",
+ },
+ Object {
+ "id": 1421526,
+ "meta-description": null,
+ "meta-title": null,
+ "name": "workplace",
+ "slug": "workplace",
+ "tag-type": "Tag",
+ },
+ ],
+ "updated-at": 1602234193104,
+ "url": "https://ace-web.qtstage.io/health/2020/10/08/it-pays-to-be-kind-improving-workplace-culture-through-kindness",
+ "version": 18,
+ "votes": Object {},
+ "word-count": 232,
+ }
+ }
+ storyElementsConfig={
+ Object {
+ "also-read": Object {},
+ "answer": Object {},
+ "blockquote": Object {},
+ "blurb": Object {},
+ "q-and-a": Object {},
+ "question": Object {},
+ "quote": Object {},
+ "references": Object {},
+ "summary": Object {},
+ }
+ }
+ />
+
+`;
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/index.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/index.js
new file mode 100644
index 000000000..e0f0af249
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/index.js
@@ -0,0 +1,307 @@
+import { SocialShare } from "@quintype/components";
+import React from "react";
+import { useSelector } from "react-redux";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { AuthorCard } from "../../../Atoms/AuthorCard";
+import { CaptionAttribution } from "../../../Atoms/CaptionAttribution";
+import { StoryHeadline } from "../../../Atoms/StoryHeadline";
+import { HeroImage } from "../../../Atoms/HeroImage";
+import { PublishDetails } from "../../../Atoms/PublishDetail";
+import { SectionTag } from "../../../Atoms/SectionTag";
+import { Subheadline } from "../../../Atoms/Subheadline";
+import { StateProvider } from "../../../SharedContext";
+import { SocialShareTemplate } from "../../../Molecules/SocialShareTemplate";
+import "./listicle-story.m.css";
+import { StoryElementCard, SlotAfterStory } from "../../../Molecules/StoryElementCard";
+import { StoryTags } from "../../../Atoms/StoryTags";
+import AsideCollection from "../../AsideCollection";
+
+const ListicleStoryTemplate = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ adComponent,
+ widgetComp = () => {},
+ firstChild,
+ secondChild,
+}) => {
+ const {
+ theme = "",
+ noOfVisibleCards = -1,
+ publishedDetails = {},
+ templateType = "",
+ authorDetails = {
+ template: "default",
+ },
+ asideCollection = {},
+ showSection,
+ sectionTagSettings,
+ premiumStoryIconConfig = {},
+ } = config;
+ const visibledCards = noOfVisibleCards < 0 ? story.cards : story.cards.slice(0, noOfVisibleCards);
+ const { "bullet-type": storyBulletType = "" } = story;
+ const storyId = get(story, ["id"], "");
+ const isNumberedBullet = storyBulletType !== "bullets";
+ const timezone = useSelector((state) => get(state, ["qt", "data", "timezone"], null));
+
+ // Content Blocks
+ const HeroImageBlock = (settings) => {
+ const { widths = [320, 480, 768, 1024, 1140], aspectRatio = [[16, 9]] } = settings;
+ return (
+
+ );
+ };
+ const CaptionAttributionBlock = () => (
+
+
+
+ );
+ const HeadlineBlock = () => (
+
+
{showSection && }
+
+
+
+ );
+
+ const AuthourBlock = () => (
+ <>
+
+
+ >
+ );
+
+ const BodyBlock = () => (
+
+ {visibledCards.map((card, index) => {
+ return (
+
+ {!isNumberedBullet ? (
+
+ ) : (
+ {index + 1}.
+ )}
+
+
+ );
+ })}
+ {firstChild}
+
+
+
+
+ {secondChild}
+
+ );
+ const SideColumnBlock = () =>
+ asideCollection && (
+
+ );
+
+ // Templates
+ const DefaultTemplate = () => (
+ <>
+
+
+ >
+ );
+ const HeroPriority = () => (
+
+ );
+ const HeadlinePriority = () => (
+
+ );
+ const HeadlineHeroPriority = () => (
+
+ );
+ const HeroOverlay = () => (
+
+ );
+ const HeadlineSideway = () => (
+
+ );
+
+ const renderTemplate = () => {
+ switch (templateType) {
+ case "hero-priority":
+ return ;
+ case "headline-priority":
+ return ;
+ case "headline-hero-priority":
+ return ;
+ case "hero-overlay":
+ return ;
+ case "headline-sideway":
+ return ;
+ default:
+ return ;
+ }
+ };
+ const dataTestId = templateType ? `listicle-story-${templateType}` : "listicle-story";
+ return (
+
+ {renderTemplate()}
+
+ );
+};
+
+ListicleStoryTemplate.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ authorCard: PropTypes.object,
+ asideCollection: PropTypes.object,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+ premiumStoryIconConfig: PropTypes.object,
+};
+
+export default StateProvider(ListicleStoryTemplate);
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.m.css b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.m.css
new file mode 100644
index 000000000..7a0d1e1e1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.m.css
@@ -0,0 +1,249 @@
+@import "../../../../../../assets/arrow/stylesheets/mixins.scss";
+:root {
+ --grid-container-width: 1140px;
+ --fluid-grid-container-width: 1440px;
+}
+
+/* Generinc style */
+.headline-block :global(.arr--story-headline) {
+ font-size: var(--arrow-fs-28);
+ line-height: normal;
+ @include tablet {
+ font-size: var(--arrow-fs-big);
+ line-height: var(--arrow-lh-2);
+ }
+}
+
+.headline-block :global(.arr--sub-headline) {
+ font-size: var(--arrow-fs-s);
+ font-style: italic;
+ margin: var(--arrow-spacing-xs) 0 var(--arrow-spacing-l);
+ @include tablet {
+ font-size: var(--arrow-fs-m);
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+}
+
+.section-tag {
+ margin-bottom: var(--arrow-spacing-s);
+}
+.caption-attribution-block {
+ margin-bottom: var(--arrow-spacing-l);
+ @include tablet {
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+}
+
+.body-block :global(.arr--element-container) {
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ margin-top: var(--arrow-spacing-m);
+ }
+}
+
+.body-block :global(.story-element-title) {
+ line-height: var(--arrow-lh-2);
+}
+
+.timestamp-social-share {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: var(--arrow-spacing-xxs) 0 var(--arrow-spacing-l);
+}
+
+.bullet-style-number {
+ font-size: var(--arrow-fs-l);
+ font-weight: var(--arrow-fw-bold);
+ color: var(--arrow-c-brand1);
+ @include tablet {
+ font-size: var(--arrow-fs-huge);
+ font-weight: var(--arrow-fw-semi-bold);
+ }
+}
+
+.bullet-style-dash {
+ content: "";
+ width: 24px;
+ height: 8px;
+ background-color: var(--arrow-c-brand1);
+ @include tablet {
+ height: 12px;
+ }
+}
+
+.story-tags > * {
+ margin: var(--arrow-spacing-l) 0;
+}
+
+/* Layout */
+.grid-container {
+ max-width: var(--grid-container-width);
+ padding: 0 var(--arrow-spacing-s);
+ margin: 0 auto;
+ @include tablet {
+ display: grid;
+ grid-column-gap: 24px;
+ grid-template-columns: repeat(12, 1fr);
+ padding: 0 var(--arrow-spacing-l);
+
+ .full-grid {
+ grid-column: 1 / 13;
+ @include desktop {
+ margin: 0 -24px;
+ }
+ }
+ }
+}
+
+.left-column {
+ @include tablet {
+ grid-column: 1 / 8;
+ }
+
+ @include desktop {
+ grid-column: 1 / 9;
+ }
+}
+
+.right-column {
+ @include tablet {
+ grid-column: 8 / 13;
+ }
+
+ @include desktop {
+ grid-column: 9 / 13;
+ }
+}
+
+.right-column :global(.aside-collection-ad-slot:first-child) {
+ margin-top: 0;
+}
+
+.center-column {
+ @include tablet {
+ grid-column: 3 / 11;
+ }
+}
+
+/* Template specific */
+.hero-faded-relative {
+ position: relative;
+}
+.hero-faded-wrapper {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ z-index: var(--z-index-1);
+ background: rgb(0 0 0 / 0.5);
+
+ display: flex;
+ align-items: center;
+
+ @include tablet {
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ }
+}
+
+.hero-faded-wrapper .headline-content-holder {
+ padding: 0 24px;
+
+ @include tablet {
+ grid-column: 2 / 12;
+ }
+ @include desktop {
+ grid-column: 3 / 11;
+ }
+}
+
+.numbered-bullet-style :global(.arr--story-page-card-wrapper) > :last-child {
+ margin-bottom: var(--arrow-spacing-xl);
+}
+
+.hero-faded-wrapper .headline-content-holder :global(.arr--story-headline),
+.hero-faded-wrapper .headline-content-holder :global(.arr--sub-headline),
+.hero-faded-wrapper .headline-content-holder :global(.arr--section-name .section-tag) {
+ color: var(--arrow-c-light);
+}
+
+.sideway-grid {
+ padding: 0 var(--arrow-spacing-s);
+ @include tablet {
+ display: grid;
+ max-width: var(--fluid-grid-container-width);
+ margin: 0 auto var(--arrow-spacing-l);
+ padding: 0 0 0 24px;
+ grid-template-columns: repeat(12, 1fr);
+ grid-column-gap: 24px;
+ }
+
+ @include desktop {
+ padding: 0;
+ }
+}
+
+.sideway-headline {
+ @include tablet {
+ grid-column: 1 / 6;
+ display: flex;
+ align-items: center;
+ }
+ @include desktop {
+ grid-column: 2 / 7;
+ }
+}
+
+.sideway-hero {
+ @include tablet {
+ grid-column: 6 / 13;
+
+ .caption-attribution-block {
+ margin-bottom: 0;
+ }
+ }
+ @include desktop {
+ grid-column: 7 / 13;
+ }
+}
+
+.headline-priority-grid {
+ @include tablet {
+ grid-column: 1 / 13;
+ }
+
+ @include desktop {
+ grid-column: 1 / 9;
+ }
+}
+
+.hero-priority-template,
+.headline-priority-template,
+.headline-hero-priority-template,
+.hero-overlay-template,
+.headline-sideway-template {
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ margin-top: var(--arrow-spacing-l);
+ }
+ @include desktop {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.numbered-bullet-style :global(.arr--story-page-card-wrapper) > * {
+ margin-top: var(--arrow-spacing-xs);
+ margin-bottom: var(--arrow-spacing-s);
+ @include tablet {
+ margin-bottom: var(--arrow-spacing-20);
+ @include desktop {
+ margin-top: var(--arrow-spacing-xxs);
+ }
+ }
+}
+
+.numbered-bullet-style :global(.story-element-title) h2 {
+ padding: 0;
+}
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.test.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.test.js
new file mode 100644
index 000000000..a4bcd2635
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/listicle-story.test.js
@@ -0,0 +1,47 @@
+import * as React from "react";
+import { shallow } from "enzyme";
+import { generateStory } from "../../../Fixture";
+import ListicleStoryTemplate from "./index";
+import { mockIntersectionObserver } from "../../../../utils/testing-utils";
+
+const story = generateStory("listicle-story");
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+ references: {},
+};
+
+beforeEach(() => {
+ mockIntersectionObserver();
+});
+
+describe("Listicle Story Templates", () => {
+ it("Should render listicle story template with section tag", () => {
+ const templateConfig = {
+ templateType: "default",
+ showSection: true,
+ };
+ const wrapper = shallow(
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("Should render listicle story template without section tag", () => {
+ const templateConfig = {
+ templateType: "default",
+ showSection: false,
+ };
+ const wrapper = shallow(
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/stories.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/stories.js
new file mode 100644
index 000000000..d51f47a1a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates/stories.js
@@ -0,0 +1,116 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import ListicleStoryTemplate from ".";
+import { optionalSelect, withStore } from "../../../../../storybook";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { generateCollection, generateStory } from "../../../Fixture";
+import { slotData } from "../../../Fixture/slot-config";
+import Readme from "./README.md";
+
+const story = generateStory("listicle-story");
+const collection = generateCollection({ stories: 4 });
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const configurableSlot = () => {
+ return ;
+};
+
+const storyTemplates = {
+ Default: "default",
+ "Hero Priority": "hero-priority",
+ "Headline Priority": "headline-priority",
+ "Headline Hero Priority": "headline-hero-priority",
+ "Hero Overlay": "hero-overlay",
+ "Headline Sideway": "headline-sideway",
+};
+
+const sectionTagTemplates = {
+ Default: "",
+ "Border Bottom Small": "borderBottomSml",
+ Solid: "solid",
+ "Border Left": "borderLeft",
+};
+
+const authorTemplates = {
+ default: "default",
+ leftAligned: "leftAligned",
+ centerAligned: "centerAligned",
+};
+
+withStore(
+ "Rows/Story Templates/Listicle Story Template",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const templateConfig = {
+ templateType: optionalSelect("Template Options", storyTemplates),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: -1,
+ showSection: boolean("Show Section Tag", true),
+ publishedDetails: {
+ showReadTime: boolean("Read time", true),
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ },
+ authorDetails: {
+ template: optionalSelect("Author Templates", authorTemplates) || "default",
+ opts: {
+ showBio: boolean("Author Bio", false),
+ showImage: boolean("Author Image", true),
+ showName: boolean("Author Name", true),
+ },
+ },
+ sectionTagSettings: {
+ template: optionalSelect("Section Template", sectionTagTemplates) || "borderBottomSml",
+ borderColor: color("Section Tag Border Color", "#ff214b"),
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color("Aside Collection Name Border Color", "#3a9fdd"),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ adSlot: [{ type: "ad", component: configurableSlot }],
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+
+ const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+ references: {},
+ };
+ return (
+ }
+ secondChild={ }
+ adComponent={configurableSlot}
+ widgetComp={configurableSlot}
+ />
+ );
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/README.md b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/README.md
new file mode 100644
index 000000000..89e6ecbe2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/README.md
@@ -0,0 +1,25 @@
+# LiveBlog Story Template
+
+LiveBlog story template is a basic component used to represent a LiveBlog story.
+
+## Usage
+
+## Default template
+
+```jsx
+
+```
+
+## Hero Priority
+
+```jsx
+
+```
+
+## Template with Children
+
+```jsx
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/index.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/index.js
new file mode 100644
index 000000000..59c8edda7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/index.js
@@ -0,0 +1,58 @@
+/* eslint-disable max-len */
+import React from "react";
+import PropTypes from "prop-types";
+import { useSelector } from "react-redux";
+import get from "lodash/get";
+import { LiveBlogStoryTemplates } from "./templates";
+import { StateProvider } from "../../../SharedContext";
+import "./live-blog.m.css";
+
+const LiveBlogStoryTemplate = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ adComponent,
+ widgetComp,
+ firstChild,
+ secondChild,
+}) => {
+ const { theme = "", templateType = "default", verticalShare = "" } = config;
+
+ const containerClass = templateType !== "hero-overlay" ? "container" : "";
+ const timezone = useSelector((state) => get(state, ["qt", "data", "timezone"], null));
+
+ return (
+
+
+
+ );
+};
+
+LiveBlogStoryTemplate.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export default StateProvider(LiveBlogStoryTemplate);
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/live-blog.m.css b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/live-blog.m.css
new file mode 100644
index 000000000..53bd4563b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/live-blog.m.css
@@ -0,0 +1,326 @@
+@import "../../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.header-details {
+ margin-top: var(--arrow-spacing-l);
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.header-details :global(.arr--sub-headline) {
+ font-size: var(--arrow-fs-s);
+ font-style: italic;
+ @include tablet {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.wrapper :global(.gap-32) {
+ margin-top: var(--arrow-spacing-l);
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.container :global(.story-element-title) {
+ line-height: var(--arrow-lh-2);
+}
+
+.container :global(.arr--caption-attribution) {
+ margin: var(--arrow-spacing-xs) 0;
+}
+
+.live-headline {
+ display: flex;
+ align-items: center;
+}
+
+.time-wrapper {
+ display: flex;
+ align-items: center;
+ font-size: var(--arrow-fs-tiny);
+ svg {
+ margin-right: var(--arrow-spacing-xxs);
+ }
+}
+
+.card-share {
+ display: flex;
+ justify-content: space-between;
+}
+
+.live-icon {
+ border-radius: 50%;
+ margin-right: var(--arrow-fs-xs);
+ animation: blink 1s linear infinite;
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+.timestamp-social-share {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--arrow-fs-l);
+ @include tablet {
+ margin-bottom: 40px;
+ }
+}
+
+.header-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / 10;
+ }
+}
+
+.grid-col-full {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: grid-start / grid-end;
+ }
+}
+
+.grid-container {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start/container-end;
+ margin: var(--arrow-spacing-xl) 0 0;
+ }
+}
+
+.top-space {
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.grid-col-9-15 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9/15;
+ margin: var(--arrow-spacing-xl) 0 0;
+ }
+}
+
+.grid-col-2-9 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / 9;
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+}
+
+.sideway {
+ align-self: center;
+}
+
+.grid-col-9-14 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / 14;
+ grid-row: 1 / auto;
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.grid-col-4-12 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 4 / 12;
+ }
+}
+
+.side-column {
+ grid-column: 1/5;
+ @include tablet {
+ margin-top: 40px;
+ grid-column: 10 / container-end;
+ grid-row: 2 / auto;
+ @include desktop {
+ margin-top: 60px;
+ }
+ }
+}
+
+.story-details {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--arrow-spacing-l);
+ @include tablet {
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+}
+
+.headline-space {
+ margin: 0 0 var(--arrow-spacing-l);
+ @include tablet {
+ margin: 0 0 var(--arrow-spacing-xl);
+ }
+}
+
+.grid-col-9-15 > :global(.arr--caption-attribution) {
+ @include tablet {
+ margin: var(--arrow-spacing-xs) 0;
+ }
+}
+
+.overlay-hero {
+ grid-column: 1/5;
+ grid-row: 1/2;
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ grid-column: container-start/container-end;
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.overlay-hero > :global(.arr--hero-image) img {
+ -webkit-filter: blur(4px);
+ filter: blur(4px);
+ opacity: 0.5;
+}
+
+.overlay-grid {
+ align-self: center;
+ z-index: var(--z-index-9);
+ grid-row: 1/2;
+ grid-column: 1/5;
+ padding: 0 36px;
+ @include tablet {
+ grid-column: 4/12;
+ padding: 0;
+ }
+}
+
+.key-events {
+ margin: var(--arrow-fs-l) 0 36px;
+}
+
+.share-cards.dark {
+ border-bottom: 1px solid var(--arrow-c-mono5);
+ margin-bottom: var(--arrow-spacing-m);
+}
+
+.share-cards.light {
+ border-bottom: 1px solid var(--arrow-c-invert-mono5);
+ margin-bottom: var(--arrow-spacing-m);
+}
+
+.time-wrapper.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.time-wrapper.light {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.space-32 > * {
+ margin-top: var(--arrow-spacing-l);
+}
+
+.sticky :global(.arr--share) {
+ grid-column: 4/5;
+ justify-self: flex-end;
+ @include tablet {
+ grid-column: 9 / grid-end;
+ @include desktop {
+ position: sticky;
+ position: -webkit-sticky;
+ grid-column: 3/4;
+ grid-row: 2/3;
+ align-self: flex-start;
+ margin-top: var(--arrow-spacing-xs);
+ top: 120px;
+ }
+ }
+}
+
+.card-share :global(.desktop-share-wrapper) {
+ max-width: 42px;
+}
+
+.sticky:global(.default) {
+ :global(.arr--share) {
+ @include tablet {
+ grid-column: 9/10;
+ @include desktop {
+ grid-column: grid-start/container-start;
+ }
+ }
+ }
+}
+
+.sticky:global(.headline-priority) {
+ :global(.arr--share) {
+ @include tablet {
+ grid-column: 8/9;
+ @include desktop {
+ grid-column: grid-start/container-start;
+ }
+ }
+ }
+}
+
+.sticky:global(.headline-hero-priority) {
+ :global(.arr--share) {
+ @include desktop {
+ grid-row: 3/4;
+ }
+ }
+}
+
+.sticky .index-2 {
+ z-index: 2;
+ @include desktop {
+ z-index: unset;
+ }
+}
+
+.grid-row-1-2 {
+ @include desktop {
+ grid-row: 1/2;
+ }
+}
+
+.grid-row-2-3 {
+ @include desktop {
+ grid-row: 2/3;
+ }
+}
+
+.grid-row-3-4 {
+ @include desktop {
+ grid-row: 3/4;
+ }
+}
+
+.grid-row-1-6 {
+ @include tablet {
+ grid-row: 1/6;
+ }
+}
+
+.tablet-index-2 {
+ @include tablet {
+ z-index: 2;
+ }
+}
+
+html[dir="rtl"] {
+ .time-wrapper {
+ direction: ltr;
+ flex-direction: row-reverse;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/stories.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/stories.js
new file mode 100644
index 000000000..93f14a41c
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/stories.js
@@ -0,0 +1,120 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import { optionalSelect, withStore } from "../../../../../storybook";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { generateCollection, generateStory } from "../../../Fixture";
+import { slotData } from "../../../Fixture/slot-config";
+import LiveBlog from "./index";
+import Readme from "./README.md";
+
+const story = generateStory("live-blog");
+const collection = generateCollection({ stories: 4 });
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+};
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const authorTemplate = {
+ default: "default",
+ leftAligned: "leftAligned",
+ centerAligned: "centerAligned",
+};
+
+const iconTypeOptions = {
+ "Circular Color Svg": "circular-color-svg",
+ "Circular Plain Svg": "circular-plain-svg",
+ "Plain Svg": "plain-svg",
+ "Plain Color Svg": "plain-color-svg",
+ "Square Svg": "square-svg",
+};
+
+const shareType = {
+ "Vertical Share": "sticky",
+ "Horizontal Share": "",
+};
+
+const templateTypes = {
+ default: "default",
+ "Hero Overlay": "hero-overlay",
+ "Headline Sideway": "headline-sideway",
+ "Hero Priority": "hero-priority",
+ "Headline Priority": "headline-priority",
+ "Headline Hero Priority": "headline-hero-priority",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Story Templates/Live Blog Story Template",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("Default", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: -1,
+ initialLoadCount: text("Initial key events count", 4),
+ showSection: boolean("Show Section Tag", true),
+ templateType: optionalSelect("Template", templateTypes),
+ buttonText: text("Key Events Button text", "Load More"),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", false),
+ },
+ authorDetails: {
+ template: optionalSelect("Author templates", authorTemplate),
+ opts: {
+ showBio: boolean("Author Bio", false),
+ showImage: boolean("Author Image", false),
+ showName: boolean("Author Name", true),
+ },
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Show Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+ return (
+ }
+ secondChild={ }
+ />
+ );
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/templates.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/templates.js
new file mode 100644
index 000000000..7d8a45291
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates/templates.js
@@ -0,0 +1,392 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { SocialShare } from "@quintype/components";
+import { SocialShareTemplate } from "../../../Molecules/SocialShareTemplate";
+import { SectionTag } from "../../../Atoms/SectionTag";
+import { Subheadline } from "../../../Atoms/Subheadline";
+import AsideCollection from "../../AsideCollection";
+import { AuthorCard } from "../../../Atoms/AuthorCard";
+import { StoryElementCard, SlotAfterStory } from "../../../Molecules/StoryElementCard";
+import { PublishDetails } from "../../../Atoms/PublishDetail";
+import { StoryTags } from "../../../Atoms/StoryTags";
+import { HeroImage } from "../../../Atoms/HeroImage";
+import "./live-blog.m.css";
+import { CaptionAttribution } from "../../../Atoms/CaptionAttribution";
+import { clientWidth, getTextColor, getTimeStamp, timestampToFormat } from "../../../../utils/utils";
+import LiveIcon from "../../../Svgs/liveicon";
+import KeyEvents from "../../../Molecules/KeyEvents";
+import { ClockIcon } from "../../../Svgs/clock-icon";
+import { StoryHeadline } from "../../../Atoms/StoryHeadline";
+
+export const LiveBlogStoryTemplates = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ adComponent,
+ widgetComp,
+ firstChild,
+ secondChild,
+ timezone,
+}) => {
+ const {
+ theme = "",
+ asideCollection = {},
+ templateType,
+ noOfVisibleCards = -1,
+ publishedDetails = {},
+ verticalShare = "",
+ shareIconType = "plain-color-svg",
+ authorDetails = {
+ template: "default",
+ },
+ premiumStoryIconConfig = {},
+ } = config;
+
+ const visibleCards = noOfVisibleCards < 0 ? story.cards : story.cards.slice(0, noOfVisibleCards);
+ const textColor = getTextColor(theme);
+ const dark = "#333";
+ const light = "#fff";
+ const updateColor = textColor === "dark" ? dark : light;
+
+ const isMobile = clientWidth("mobile");
+ const storyId = get(story, ["id"], "");
+
+ const HeaderCard = () => {
+ const isBlogClosed = get(story, ["metadata", "is-closed"]);
+ return (
+
+
+
+ {!isBlogClosed && (
+
+
+
+ )}
+
+
+
+
+ );
+ };
+ const StoryCards = () => {
+ return visibleCards.map((card = {}, index) => {
+ const borderBottom = index === visibleCards.length - 1 ? "" : `share-cards ${textColor}`;
+ const cardId = get(card, ["id"], "");
+ const { "card-added-at": cardAddedAt } = card;
+ return (
+
+
+
+
+ {getTimeStamp(cardAddedAt, timestampToFormat, { isTimeFirst: true })}
+
+
+
+
+
+
+
+ );
+ });
+ };
+
+ const keyEvents = () => {
+ return (
+
+
+
+ );
+ };
+
+ const SocialShareComponent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const showKeyEvents = () => {
+ if ((templateType === "default" || templateType === "headline-priority") && !isMobile) {
+ return null;
+ }
+ return keyEvents();
+ };
+
+ const StoryData = () => {
+ return (
+ <>
+
+
+
+ {!verticalShare &&
}
+
+ {showKeyEvents()}
+
+
+ {firstChild}
+
+
+ {secondChild}
+
+ >
+ );
+ };
+
+ const DefaultTemplate = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ {asideCollection && (
+
+ )}
+
+ >
+ );
+ };
+
+ const HeroOverlay = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const HeadlineSideway = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const HeadlinePriority = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ {asideCollection && (
+
+ )}
+
+ >
+ );
+ };
+
+ const HeroPriority = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const HeadlineHeroPriority = (story) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const getLiveBlogStoryTemplates = (templateType) => {
+ switch (templateType) {
+ case "hero-priority":
+ return HeroPriority(story);
+ case "hero-overlay":
+ return HeroOverlay(story);
+ case "headline-sideway":
+ return HeadlineSideway(story);
+ case "headline-priority":
+ return HeadlinePriority(story);
+ case "headline-hero-priority":
+ return HeadlineHeroPriority(story);
+ default:
+ return DefaultTemplate(story);
+ }
+ };
+
+ return <>{getLiveBlogStoryTemplates(templateType)}>;
+};
+
+LiveBlogStoryTemplates.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ authorCard: PropTypes.object,
+ asideCollection: PropTypes.object,
+ }),
+ timezone: PropTypes.string,
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/index.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/index.js
new file mode 100644
index 000000000..a26028d6b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/index.js
@@ -0,0 +1,259 @@
+/* eslint-disable max-len */
+import { SocialShare } from "@quintype/components";
+import PropTypes from "prop-types";
+import React from "react";
+import get from "lodash/get";
+import { useSelector } from "react-redux";
+import kebabCase from "lodash/kebabCase";
+import { AuthorCard } from "../../../Atoms/AuthorCard";
+import { CaptionAttribution } from "../../../Atoms/CaptionAttribution";
+import { SocialShareTemplate } from "../../../Molecules/SocialShareTemplate";
+import { SectionTag } from "../../../Atoms/SectionTag";
+import { StoryHeadline } from "../../../Atoms/StoryHeadline";
+import { Subheadline } from "../../../Atoms/Subheadline";
+import { HeroImage } from "../../../Atoms/HeroImage";
+import { PublishDetails } from "../../../Atoms/PublishDetail";
+import { StoryTags } from "../../../Atoms/StoryTags";
+import { PhotoStoryElement, SlotAfterStory } from "../../../Molecules/StoryElementCard";
+import { StateProvider } from "../../../SharedContext";
+import AsideCollection from "../../AsideCollection";
+import "./photo.m.css";
+
+const PhotoStory = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ widgetComp,
+ adComponent,
+ firstChild,
+ secondChild,
+}) => {
+ const {
+ theme = "",
+ asideCollection = {},
+ templateType = "default",
+ noOfVisibleCards = -1,
+ publishedDetails = {},
+ verticalShare = "",
+ shareIconType = "plain-color-svg",
+ authorDetails = {
+ template: "default",
+ },
+ imageRender = "fullBleed",
+ premiumStoryIconConfig = {},
+ } = config;
+ const isFullBleed = imageRender === "fullBleed";
+ const visibledCards = noOfVisibleCards < 0 ? story.cards : story.cards.slice(0, noOfVisibleCards);
+ const storyId = get(story, ["id"], "");
+ const timezone = useSelector((state) => get(state, ["qt", "data", "timezone"], null));
+
+ const HeaderCard = () => {
+ return (
+
+
+
+
+
+ );
+ };
+
+ const SocialShareComponent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const StoryData = () => (
+ <>
+ {authorDetails && (
+
+ )}
+
+
+ {!verticalShare &&
}
+
+ {visibledCards.map((card) => {
+ return (
+
+ );
+ })}
+
+ {firstChild}
+
+
+ {secondChild}
+
+ >
+ );
+
+ const renderImages = (imageRender) => {
+ switch (imageRender) {
+ case "fullBleed":
+ return "grid-col-full";
+ case "container":
+ return "grid-container";
+ default:
+ return "grid-col-full";
+ }
+ };
+
+ const DefaultTemplate = ({ story }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const HeroPriority = ({ story }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const HeadlinePriority = ({ story }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const PhotoStoryTemplate = ({ templateType }) => {
+ switch (templateType) {
+ case "hero-priority-center":
+ return ;
+ case "headline-priority":
+ return ;
+ default:
+ return ;
+ }
+ };
+ PhotoStoryTemplate.propTypes = {
+ templateType: PropTypes.string,
+ };
+
+ PhotoStoryTemplate.propTypes = {
+ templateType: PropTypes.string,
+ };
+
+ return (
+
+ );
+};
+
+PhotoStory.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ asideCollection: PropTypes.object,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export default StateProvider(PhotoStory);
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo-story.test.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo-story.test.js
new file mode 100644
index 000000000..8779da1c5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo-story.test.js
@@ -0,0 +1,144 @@
+import * as React from "react";
+import { mount } from "enzyme";
+import { generateStory, generateCollection, generateStore } from "../../../Fixture";
+import PhotoStory from "./index";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { Provider } from "react-redux";
+import { mockIntersectionObserver } from "../../../../utils/testing-utils";
+
+const story = generateStory("photo-story");
+const collection = generateCollection({ stories: 4 });
+const configurableSlot = () => {
+ return ;
+};
+
+const child = children
;
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+};
+
+const templateConfig = {
+ theme: "#ffffff",
+ noOfVisibleCards: -1,
+ publishedDetails: {
+ enablePublishedTime: true,
+ enableUpdatedTime: false,
+ showReadTime: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: "#3a9fdd",
+ title: "Trending",
+ theme: "#ffffff",
+ adSlot: [{ type: "ad", component: configurableSlot }],
+ },
+ },
+};
+
+beforeEach(() => {
+ mockIntersectionObserver();
+});
+
+describe("photo Story Templates", () => {
+ it("Should render photo story template with image full-bleed", () => {
+ const config = Object.assign({ imageRender: "fullBleed" }, templateConfig);
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "hero-image" }).prop("className")).toMatch(/grid-col-full/);
+ });
+ it("Should render photo story template with image with in container", () => {
+ const config = Object.assign({ imageRender: "container" }, templateConfig);
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "hero-image" }).prop("className")).toMatch(/grid-containe/);
+ });
+
+ it("Should render hero priority center align template", () => {
+ const config = { ...templateConfig, templateType: "hero-priority-center", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "photo-story-hero-priority-center-container" }).prop("className")).toMatch(
+ /hero-priority-center/
+ );
+ });
+
+ it("Should render hero priority left align template", () => {
+ const config = { ...templateConfig, templateType: "default", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "photo-story-default-container" }).prop("className")).toMatch(/default/);
+ });
+
+ it("Should render headline priority template", () => {
+ const config = { ...templateConfig, templateType: "headline-priority", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "photo-story-headline-priority-container" }).prop("className")).toMatch(
+ /headline-priority/
+ );
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo.m.css b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo.m.css
new file mode 100644
index 000000000..ebc07873d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/photo.m.css
@@ -0,0 +1,175 @@
+@import "../../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.grid-col-full {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: grid-start / grid-end;
+ }
+}
+.grid-container {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / container-end;
+ }
+}
+
+.grid-col-2-10 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / 9;
+ @include desktop {
+ grid-column: container-start / 10;
+ }
+ }
+}
+.grid-col-10-14 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / container-end;
+ grid-row: 2 / auto;
+ @include desktop {
+ grid-column: 10 / container-end;
+ }
+ }
+}
+.grid-col-4-12 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 4 / 12;
+ }
+}
+.grid-col-2-9 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / 9;
+ }
+}
+
+.grid-col-9-14 {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / 14;
+ grid-row: 2 / 6;
+ }
+}
+.story-details {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--arrow-spacing-l);
+ @include tablet {
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+}
+.side-space {
+ margin-top: var(--arrow-spacing-m);
+ @include tablet {
+ margin-top: var(--arrow-spacing-l);
+ }
+}
+:global(.gap-32) {
+ margin-top: var(--arrow-spacing-l);
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.header-details {
+ :global(.arr--sub-headline) {
+ font-size: var(--arrow-fs-s);
+ font-style: italic;
+ @include tablet {
+ font-size: var(--arrow-fs-m);
+ }
+ }
+ .space-12 {
+ margin-top: var(--arrow-spacing-m);
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+ }
+}
+
+.story-tags {
+ margin: var(--arrow-spacing-l) 0;
+}
+
+.sticky :global(.arr--share) {
+ grid-column: 4/5;
+ justify-self: flex-end;
+ @include tablet {
+ grid-column: 8/9;
+ @include desktop {
+ position: sticky;
+ position: -webkit-sticky;
+ grid-column: grid-start/container-start;
+ grid-row: 2/3;
+ align-self: flex-start;
+ margin-top: var(--arrow-spacing-xs);
+ top: 120px;
+ }
+ }
+}
+
+.sticky:global(.hero-priority-center) {
+ :global(.arr--share) {
+ @include tablet {
+ grid-column: 9 / grid-end;
+ @include desktop {
+ grid-column: 3/4;
+ }
+ }
+ }
+}
+
+.sticky .index-2 {
+ z-index: 2;
+ @include desktop {
+ z-index: unset;
+ }
+}
+
+.tablet-index-2 {
+ @include tablet {
+ z-index: 2;
+ }
+}
+
+.fullBleed {
+ margin: unset;
+}
+
+.fullBleed:global(.headline-priority) .side-space.index-2 {
+ @include mobile {
+ margin: var(--arrow-spacing-m) 0 0;
+ }
+}
+
+.fullBleed:global(.headline-priority) .grid-container,
+.fullBleed:global(.headline-priority) .grid-col-9-14 {
+ @include mobile {
+ margin: 0 var(--arrow-spacing-s);
+ }
+}
+
+.fullBleed:global(.headline-priority) .side-space {
+ @include mobile {
+ margin: var(--arrow-spacing-m) var(--arrow-spacing-s) 0;
+ }
+}
+
+.fullBleed .space-12 {
+ @include mobile {
+ margin: 0 var(--arrow-spacing-s) 0;
+ }
+}
+
+.space-12 .header-details {
+ @include mobile {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.space-32 > * {
+ margin-top: var(--arrow-spacing-l);
+}
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/stories.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/stories.js
new file mode 100644
index 000000000..94cbab6f6
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates/stories.js
@@ -0,0 +1,123 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import { optionalSelect, withStore } from "../../../../../storybook";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { generateCollection, generateStory } from "../../../Fixture";
+import { slotData } from "../../../Fixture/slot-config";
+import PhotoStory from "./index";
+
+const story = generateStory("photo-story");
+const collection = generateCollection({ stories: 4 });
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Aside Collection Name Border Color";
+
+const imageTypeOptions = {
+ "Full Bleed": "fullBleed",
+ "With In Container": "container",
+};
+
+const templates = {
+ "Hero Priority Left": "default",
+ "Hero Priority Center": "hero-priority-center",
+ "Headline Priority": "headline-priority",
+};
+
+const iconTypeOptions = {
+ "Circular Color Svg": "circular-color-svg",
+ "Circular Plain Svg": "circular-plain-svg",
+ "Plain Svg": "plain-svg",
+ "Plain Color Svg": "plain-color-svg",
+ "Square Svg": "square-svg",
+};
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+};
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const authorTemplate = {
+ default: "default",
+ leftAligned: "leftAligned",
+ centerAligned: "centerAligned",
+};
+
+const shareType = {
+ "Vertical Share": "sticky",
+ "Horizontal Share": "",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore("Rows/Story Templates/Photo Story Template", {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+}).add("Default", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: -1,
+ showSection: boolean("Show Section Tag", true),
+ templateType: optionalSelect("Template Options", templates),
+ imageRender: optionalSelect("image Options", imageTypeOptions),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", true),
+ },
+ authorDetails: {
+ template: optionalSelect("temp", authorTemplate),
+ opts: {
+ showBio: boolean("bio", true),
+ showImage: boolean("image", true),
+ showName: boolean("Author Name", true),
+ },
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ adSlot: [{ type: "ad", component: configurableSlot }],
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+
+ return (
+
+
}
+ secondChild={
}
+ />
+
+ );
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/README.md b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/README.md
new file mode 100644
index 000000000..ee518e88e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/README.md
@@ -0,0 +1,49 @@
+# Text Story Template
+
+Text story template is a basic component used to represent a text story.
+
+## Usage
+
+## Hero Priority Left Align text Story
+
+```jsx
+
+```
+
+## Hero Priority Center Align Text Story
+
+```jsx
+
+```
+
+## Headline Hero Priority Text Story
+
+```jsx
+
+```
+
+## Hero Vertical Priority Text Story
+
+```jsx
+
+```
+
+## Headline Priority Text Story
+
+```jsx
+
+```
+
+## Hero Overlay Priority Text Story
+
+```jsx
+
+```
+
+## Template with Children
+
+```jsx
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/index.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/index.js
new file mode 100644
index 000000000..e90a2a412
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/index.js
@@ -0,0 +1,75 @@
+import PropTypes from "prop-types";
+import React from "react";
+import { useSelector } from "react-redux";
+import kebabCase from "lodash/kebabCase";
+import { StateProvider } from "../../../SharedContext";
+import { StoryTemplate } from "./templates";
+import get from "lodash/get";
+import "./text-story.m.css";
+
+const TextStoryTemplate = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ adComponent,
+ widgetComp,
+ firstChild,
+ secondChild,
+}) => {
+ const {
+ theme = "",
+ templateType = "default",
+ imageRender = "fullBleed",
+ sort = "headline-first",
+ verticalShare = "",
+ } = config;
+ const sortOption = templateType === "hero-vertical-priority" ? sort : "";
+
+ const supportImageType = {
+ default: imageRender,
+ "hero-priority-center": imageRender,
+ "headline-hero-priority": imageRender,
+ "hero-vertical-priority": imageRender,
+ };
+
+ const timezone = useSelector((state) => get(state, ["qt", "data", "timezone"], null));
+
+ const dataTestId = supportImageType[templateType]
+ ? `text-story-${templateType}-${kebabCase(supportImageType[templateType])}`
+ : `text-story-${templateType}`;
+ return (
+
+
+
+ );
+};
+
+TextStoryTemplate.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ authorCard: PropTypes.object,
+ asideCollection: PropTypes.object,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export default StateProvider(TextStoryTemplate);
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/stories.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/stories.js
new file mode 100644
index 000000000..b84dee978
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/stories.js
@@ -0,0 +1,125 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import { optionalSelect, withStore } from "../../../../../storybook";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { generateCollection, generateStory } from "../../../Fixture";
+import { slotData } from "../../../Fixture/slot-config";
+import TextStoryTemplate from "./index";
+import Readme from "./README.md";
+
+const story = generateStory();
+const collection = generateCollection({ stories: 4 });
+const authorTemplate = {
+ default: "default",
+ leftAligned: "leftAligned",
+ centerAligned: "centerAligned",
+ "No value": "",
+};
+const imageTypeOptions = {
+ "Full Bleed": "fullBleed",
+ "With In Container": "container",
+};
+
+const iconTypeOptions = {
+ "Circular Color Svg": "circular-color-svg",
+ "Circular Plain Svg": "circular-plain-svg",
+ "Plain Svg": "plain-svg",
+ "Plain Color Svg": "plain-color-svg",
+ "Square Svg": "square-svg",
+};
+
+const templates = {
+ "Hero Priority Left": "default",
+ "Hero Priority Center": "hero-priority-center",
+ "Headline Hero Priority": "headline-hero-priority",
+ "Hero Vertical Priority": "hero-vertical-priority",
+ "Headline Priority": "headline-priority",
+ "Headline Overlay Priority": "headline-overlay-priority",
+};
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+const sortFirstOptions = {
+ "Headline First": "headline-first",
+ "Image First": "image-first",
+};
+
+const shareType = {
+ "Vertical Share": "sticky",
+ "Horizontal Share": "",
+};
+
+withStore(
+ "Rows/Story Templates/Text Story Template",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+).add("default", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color("BG Color", "#ffffff"),
+ imageRender: optionalSelect("Image Type", imageTypeOptions),
+ sort: optionalSelect("Sort Options", sortFirstOptions),
+ noOfVisibleCards: -1,
+ showSection: boolean("Show Section Tag", true),
+ templateType: optionalSelect("Template Options", templates),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", true),
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color("Aside Collection Name Border Color", "#3a9fdd"),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color("BG Color", "#ffffff"),
+ },
+ slots: slotData,
+ },
+ authorDetails: {
+ template: optionalSelect("Author Template Options", authorTemplate, "default"),
+ opts: {
+ showBio: false,
+ showImage: true,
+ showName: true,
+ },
+ },
+ };
+ return (
+ }
+ secondChild={ }
+ />
+ );
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/templates.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/templates.js
new file mode 100644
index 000000000..414f31198
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/templates.js
@@ -0,0 +1,284 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { SocialShareTemplate } from "../../../Molecules/SocialShareTemplate";
+import { SocialShare } from "@quintype/components";
+import { SectionTag } from "../../../Atoms/SectionTag";
+import { StoryHeadline } from "../../../Atoms/StoryHeadline";
+import { Subheadline } from "../../../Atoms/Subheadline";
+import AsideCollection from "../../AsideCollection";
+import { AuthorCard } from "../../../Atoms/AuthorCard";
+import { CaptionAttribution } from "../../../Atoms/CaptionAttribution";
+import { HeroImage } from "../../../Atoms/HeroImage";
+import { PublishDetails } from "../../../Atoms/PublishDetail";
+import { StoryTags } from "../../../Atoms/StoryTags";
+import { StoryElementCard, SlotAfterStory } from "../../../Molecules/StoryElementCard";
+import "./text-story.m.css";
+
+export const StoryTemplate = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ widgetComp,
+ adComponent,
+ firstChild,
+ secondChild,
+ timezone,
+}) => {
+ const {
+ theme = "",
+ asideCollection = {},
+ templateType = "default",
+ noOfVisibleCards = -1,
+ publishedDetails = {},
+ authorDetails = {
+ template: "default",
+ },
+ verticalShare = "",
+ shareIconType = "plain-color-svg",
+ premiumStoryIconConfig = {},
+ } = config;
+
+ const visibledCards = noOfVisibleCards < 0 ? story.cards : story.cards.slice(0, noOfVisibleCards);
+ const storyId = get(story, ["id"], "");
+ const HeaderCard = () => {
+ return (
+
+
+
+
+
+ );
+ };
+
+ const AsideCollectionCard = () => {
+ return (
+ asideCollection && (
+
+ )
+ );
+ };
+
+ const SocialShareComponent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const StoryData = () => {
+ return (
+
+ {authorDetails &&
}
+
+
+ {!verticalShare &&
}
+
+ {visibledCards.map((card) => {
+ return (
+
+ );
+ })}
+ {firstChild}
+
+
+
+
+ {secondChild}
+
+ );
+ };
+
+ const SideColumn = () => {
+ return (
+ asideCollection && (
+
+ )
+ );
+ };
+
+ const heroPriorityCenterTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const headlineHeroPriorityTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const heroVerticalTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const heroPriorityLeftTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const headlinePriorityTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const headlineOverlayTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const getStoryTemplate = (templateType) => {
+ switch (templateType) {
+ case "hero-priority-center":
+ return heroPriorityCenterTemplate();
+ case "default":
+ return heroPriorityLeftTemplate();
+ case "headline-hero-priority":
+ return headlineHeroPriorityTemplate();
+ case "hero-vertical-priority":
+ return heroVerticalTemplate();
+ case "headline-priority":
+ return headlinePriorityTemplate();
+ case "headline-overlay-priority":
+ return headlineOverlayTemplate();
+ }
+ };
+
+ return <>{getStoryTemplate(templateType)}>;
+};
+
+StoryTemplate.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ authorDetails: PropTypes.object,
+ asideCollection: PropTypes.object,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ timezone: PropTypes.string,
+ storyElementsConfig: PropTypes.object,
+ widgetComp: PropTypes.func,
+ adComponent: PropTypes.func,
+};
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.m.css b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.m.css
new file mode 100644
index 000000000..e1b3cb5bb
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.m.css
@@ -0,0 +1,450 @@
+/*eslint-disable no-descending-specificity */
+@import "../../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.header-details {
+ margin: var(--arrow-spacing-m) 0 var(--arrow-spacing-xs) 0;
+ @include tablet {
+ margin: var(--arrow-fs-huge) 0;
+ }
+}
+
+.header-details :global(.arr--sub-headline) {
+ font-size: var(--arrow-fs-s);
+ font-style: italic;
+ @include tablet {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.timestamp-social-share {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ margin-top: 0;
+ }
+}
+.story-tags > * {
+ margin: var(--arrow-spacing-m) 0;
+ @include tablet {
+ margin: var(--arrow-spacing-l) 0;
+ }
+}
+
+.hero-image :global(.arr--hero-image) {
+ padding: 0;
+}
+
+.dark {
+ color: var(--arrow-c-mono4);
+}
+
+.light {
+ color: var(--arrow-c-invert-mono4);
+}
+
+.hero-vertical-priority,
+.headline-hero-priority,
+.hero-priority-center,
+.headline-overlay-priority {
+ .story-content-inner-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 4/ 12;
+ }
+ }
+ .aside-collection {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / container-end;
+ }
+ }
+}
+
+.default :global(.section-tag) {
+ margin-bottom: var(--arrow-spacing-xs);
+}
+
+.default .story-details,
+.default .aside-collection {
+ margin: 0 var(--arrow-spacing-s);
+ @include tablet {
+ margin: unset;
+ }
+}
+
+.sticky :global(.arr--share) {
+ grid-column: 4/5;
+ justify-self: flex-end;
+ @include tablet {
+ /* grid column value is to make the vertical share sticky */
+ grid-column: 9 / grid-end;
+ @include desktop {
+ position: sticky;
+ position: -webkit-sticky;
+ top: 120px;
+ grid-column: 3/ 4;
+ grid-row: 2/3;
+ align-self: flex-start;
+ margin-top: var(--arrow-spacing-xs);
+ }
+ }
+}
+
+.default .hero-image {
+ @include tablet {
+ grid-column: gutter-1-start/ gutter-2-end;
+ }
+}
+
+.headline-overlay-priority > .hero-image {
+ background-color: var(--arrow-c-dark);
+ overflow: hidden;
+ grid-row: 1 / 2;
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 14;
+ }
+}
+
+.headline-overlay-priority .header-details {
+ align-self: center;
+ grid-row: 1/2;
+ grid-column: 1/5;
+ padding: 0 var(--arrow-fs-l);
+ z-index: 2;
+ @include tablet {
+ grid-column: 3 / 13;
+ @include desktop {
+ grid-column: 4 / 12;
+ }
+ }
+ :global(.section-tag) {
+ color: var(--arrow-c-invert-mono4);
+ }
+ :global(.arr--sub-headline),
+ :global(.arr--story-headline .arr--story--headline-h1) {
+ color: var(--arrow-c-invert-mono1);
+ }
+}
+
+.headline-hero-priority.sticky {
+ :global(.arr--share) {
+ @include desktop {
+ grid-row: 3/4;
+ }
+ }
+}
+
+.default.sticky,
+.headline-priority.sticky {
+ :global(.arr--share) {
+ @include desktop {
+ grid-column: grid-start / container-start;
+ grid-row: 2/3;
+ }
+ }
+}
+
+.hero-vertical-priority.container :global(.section-tag) {
+ padding: var(--arrow-spacing-xxs) 0 var(--arrow-spacing-xs);
+ @include tablet {
+ padding: unset;
+ }
+}
+
+.hero-vertical-priority.container :global(.arr--caption-attribution) {
+ margin: 0 0 var(--arrow-spacing-xs);
+}
+
+.hero-vertical-priority.fullBleed :global(.section-tag) {
+ padding-bottom: var(--arrow-spacing-xs);
+ @include tablet {
+ padding-bottom: unset;
+ }
+}
+
+.fullBleed.hero-vertical-priority :global(.arr--caption-attribution) {
+ @include mobile {
+ margin: var(--arrow-spacing-xs) var(--arrow-spacing-s);
+ }
+}
+
+.hero-vertical-priority.headline-first.sticky :global(.arr--share) {
+ @include desktop {
+ grid-row: 3/4;
+ }
+}
+
+.text-story-side-column {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / 14;
+ grid-row: 2/3;
+ @include desktop {
+ grid-column: 10 / 14;
+ }
+ }
+}
+
+.headline-priority {
+ .header-details {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 14;
+ @include desktop {
+ grid-column: 2 / 10;
+ }
+ }
+ }
+ .story-content-inner-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 9;
+ @include desktop {
+ grid-column: 2 / 10;
+ }
+ }
+ }
+ .text-story-side-column {
+ @include tablet {
+ grid-row: 2/6;
+ }
+ }
+}
+
+.default.fullBleed .hero-image,
+.headline-hero-priority.fullBleed .hero-image,
+.hero-priority-center.fullBleed .hero-image {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: grid-start/ grid-end;
+ }
+}
+
+.default .story-content-inner-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 9;
+ @include desktop-lg {
+ grid-column: 2 / 10;
+ }
+ }
+}
+
+.default .text-story-side-column {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / 14;
+ @include desktop-lg {
+ grid-column: 10 / 14;
+ }
+ }
+}
+
+.fullBleed .story-content-inner-wrapper,
+.fullBleed.hero-vertical-priority .aside-collection,
+.fullBleed.headline-hero-priority .aside-collection .fullBleed.hero-priority-center .aside-collection,
+.fullBleed .text-story-side-column {
+ @include mobile {
+ margin: 0 var(--arrow-spacing-s);
+ }
+}
+
+.fullBleed.hero-vertical-priority .header-details {
+ margin: var(--arrow-spacing-m) var(--arrow-spacing-s) var(--arrow-spacing-xs);
+ @include tablet {
+ margin: var(--arrow-fs-huge) 0;
+ }
+}
+
+.default.container {
+ .hero-image :global(.arr--hero-image) {
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ grid-column: 2 / 14;
+ margin-top: unset;
+ }
+ }
+}
+
+.hero-vertical-priority.image-first .header-details {
+ padding-bottom: 20px;
+ @include tablet {
+ padding-bottom: unset;
+ }
+}
+
+.hero-vertical-priority.image-first .image-wrapper {
+ grid-row: 1/2;
+ @include tablet {
+ grid-row: unset;
+ grid-column: 8 / 14;
+ margin-bottom: var(--arrow-spacing-l);
+ @include desktop-lg {
+ grid-column: 8 / 13;
+ }
+ }
+}
+
+.hero-priority-center.container .hero-image,
+.headline-hero-priority.container .hero-image,
+.default.container .hero-image {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 14;
+ margin-top: var(--arrow-spacing-xs);
+ @include desktop-lg {
+ margin-top: var(--arrow-spacing-m);
+ }
+ }
+}
+
+.hero-vertical-priority.fullBleed.headline-first .hero-image {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 7 / grid-end;
+ @include desktop {
+ grid-column: 8 / grid-end;
+ }
+ }
+}
+.hero-vertical-priority.fullBleed.headline-first :global(.arr--caption-attribution) {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 7 / grid-end;
+ @include desktop {
+ grid-column: 8 / grid-end;
+ grid-row: 2/3;
+ }
+ }
+}
+
+.hero-vertical-priority.fullBleed.headline-first .header-details {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 7;
+ align-self: center;
+ @include desktop {
+ grid-column: 2 / 8;
+ }
+ }
+}
+
+.hero-vertical-priority.container.headline-first .header-details {
+ grid-column: 1/5;
+ @include tablet {
+ align-self: center;
+ grid-column: 2 / 8;
+ @include desktop {
+ grid-column: 3 / 8;
+ }
+ }
+}
+
+.hero-vertical-priority.container.headline-first .hero-image,
+.hero-vertical-priority.container.headline-first :global(.arr--caption-attribution) {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 8 / 14;
+ @include desktop {
+ grid-column: 8 / 13;
+ }
+ }
+}
+
+.hero-vertical-priority.fullBleed.image-first .hero-image {
+ grid-column: 1/5;
+ @include tablet {
+ grid-row: 1 / 2;
+ grid-column: grid-start / 9;
+ @include desktop {
+ grid-column: grid-start / 8;
+ }
+ }
+}
+
+.hero-vertical-priority.fullBleed.image-first :global(.arr--caption-attribution) {
+ grid-column: 1/5;
+ @include tablet {
+ grid-row: 1 / 2;
+ grid-column: grid-start / 9;
+ @include desktop {
+ grid-column: grid-start / 8;
+ }
+ }
+}
+
+.hero-vertical-priority.fullBleed.image-first .header-details {
+ grid-column: 1/5;
+ @include tablet {
+ align-self: center;
+ grid-column: 9 / 14;
+ @include desktop {
+ grid-column: 8 / 14;
+ }
+ }
+}
+
+.hero-vertical-priority.container.image-first .header-details {
+ grid-column: 1/5;
+ @include tablet {
+ align-self: center;
+ grid-column: 8 / 14;
+ @include desktop {
+ grid-column: 8 / 13;
+ }
+ }
+}
+
+.hero-vertical-priority.container.image-first .hero-image,
+.hero-vertical-priority.container.image-first :global(.arr--caption-attribution) {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 2 / 8;
+ grid-row: 1 / 2;
+ @include desktop {
+ grid-column: 3 / 8;
+ }
+ }
+
+ :global(.section-tag) {
+ margin-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.hero-vertical-priority.container .story-content-wrapper {
+ display: grid;
+}
+
+.headline-overlay-priority > .hero-image figure {
+ opacity: 0.5;
+}
+.headline-overlay-priority > .hero-image img {
+ -webkit-filter: blur(4px);
+ filter: blur(4px);
+}
+
+.gap-16 {
+ margin-top: var(--arrow-spacing-m);
+}
+
+.sticky .index-2 {
+ @include tablet {
+ z-index: 2;
+ @include desktop {
+ z-index: unset;
+ }
+ }
+}
+
+.tablet-index-2 {
+ @include tablet {
+ z-index: 2;
+ }
+}
+
+.fullBleed:global(.arr-story-grid) {
+ margin: unset;
+}
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.test.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.test.js
new file mode 100644
index 000000000..d2eecd4de
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates/text-story.test.js
@@ -0,0 +1,222 @@
+import * as React from "react";
+import { mount } from "enzyme";
+import { generateStory, generateCollection, generateStore } from "../../../Fixture";
+import TextStoryTemplate from "./index";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { Provider } from "react-redux";
+import { mockIntersectionObserver } from "../../../../utils/testing-utils";
+
+const story = generateStory();
+const collection = generateCollection({ stories: 4 });
+const configurableSlot = () => {
+ return ;
+};
+
+const child = children
;
+
+const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+};
+
+const templateConfig = {
+ theme: "#ffffff",
+ sort: "image-first",
+ noOfVisibleCards: -1,
+ publishedDetails: {
+ enablePublishedTime: true,
+ enableUpdatedTime: false,
+ showReadTime: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: "#3a9fdd",
+ title: "Trending",
+ theme: "#ffffff",
+ adSlot: [{ type: "ad", component: configurableSlot }],
+ },
+ },
+ authorDetails: {
+ template: "default",
+ opts: {
+ showBio: false,
+ showImage: true,
+ showName: true,
+ },
+ },
+};
+
+beforeEach(() => {
+ mockIntersectionObserver();
+});
+
+describe("Text Story Templates", () => {
+ it("Should render text story template with image full-bleed", () => {
+ const config = { ...templateConfig, imageRender: "fullBleed" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-default-full-bleed" }).prop("className")).toMatch(/fullBleed/);
+ });
+
+ it("Should render text story template with in container image", () => {
+ const config = { ...templateConfig, templateType: "hero-priority-center", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-hero-priority-center-container" }).prop("className")).toMatch(
+ /container/
+ );
+ });
+
+ it("Should render hero priority center align template", () => {
+ const config = { ...templateConfig, templateType: "hero-priority-center", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-hero-priority-center-container" }).prop("className")).toMatch(
+ /hero-priority-center/
+ );
+ expect(wrapper.find("AsideCollectionCard").length).toEqual(1);
+ });
+
+ it("Should render hero priority left align template", () => {
+ const config = { ...templateConfig, templateType: "default", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-default-container" }).prop("className")).toMatch(/default/);
+ expect(wrapper.find("SideColumn").length).toEqual(1);
+ });
+
+ it("Should render headline hero priority template", () => {
+ const config = { ...templateConfig, templateType: "headline-hero-priority", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-headline-hero-priority-container" }).prop("className")).toMatch(
+ /headline-hero-priority/
+ );
+ expect(wrapper.find("AsideCollectionCard").length).toEqual(1);
+ });
+
+ it("Should render hero vertical priority template", () => {
+ const config = { ...templateConfig, templateType: "hero-vertical-priority", imageRender: "container" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-hero-vertical-priority-container" }).prop("className")).toMatch(
+ /hero-vertical-priority/
+ );
+ expect(wrapper.find("AsideCollectionCard").length).toEqual(1);
+ });
+
+ it("Should render headline priority template", () => {
+ const config = { ...templateConfig, templateType: "headline-priority" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-headline-priority" }).prop("className")).toMatch(
+ /headline-priority/
+ );
+ expect(wrapper.find("SideColumn").length).toEqual(1);
+ });
+
+ it("Should render headline overlay priority template", () => {
+ const config = { ...templateConfig, templateType: "headline-overlay-priority" };
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find({ "data-test-id": "text-story-headline-overlay-priority" }).prop("className")).toMatch(
+ /headline-overlay-priority/
+ );
+ expect(wrapper.find("AsideCollectionCard").length).toEqual(1);
+ });
+});
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/README.md b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/README.md
new file mode 100644
index 000000000..fccb68dfa
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/README.md
@@ -0,0 +1,31 @@
+# Video Story Template
+
+Video story template is a basic component used to represent a video story.
+
+## Usage
+
+## Default Video Story
+
+```jsx
+
+```
+
+## Hero priority Video Story
+
+```jsx
+
+```
+
+## Headline priority Video Story
+
+```jsx
+
+```
+
+## Template with Children
+
+```jsx
+
+
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/index.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/index.js
new file mode 100644
index 000000000..81eb70e4d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/index.js
@@ -0,0 +1,240 @@
+import { SocialShare } from "@quintype/components";
+import PropTypes from "prop-types";
+import React from "react";
+import { useSelector } from "react-redux";
+import get from "lodash/get";
+import { SocialShareTemplate } from "../../../Molecules/SocialShareTemplate";
+import { SectionTag } from "../../../Atoms/SectionTag";
+import { StoryHeadline } from "../../../Atoms/StoryHeadline";
+import { Subheadline } from "../../../Atoms/Subheadline";
+import { Video } from "../../../Atoms/StoryElements/Video";
+import AsideCollection from "../../AsideCollection";
+import { AuthorCard } from "../../../Atoms/AuthorCard";
+import { PublishDetails } from "../../../Atoms/PublishDetail";
+import { StoryTags } from "../../../Atoms/StoryTags";
+import { StoryElementCard, SlotAfterStory } from "../../../Molecules/StoryElementCard";
+import { StateProvider } from "../../../SharedContext";
+import "./video-story.m.css";
+
+const VideoStoryTemplate = ({
+ story = {},
+ config = {},
+ storyElementsConfig,
+ widgetComp,
+ adComponent,
+ firstChild,
+ secondChild,
+}) => {
+ const heroVideo =
+ story.cards
+ .flatMap((card) => card["story-elements"])
+ .find(
+ ({ type, subtype }) => type === "youtube-video" || (type === "jsembed" && subtype === "dailymotion-video")
+ ) || {};
+
+ const {
+ theme = "",
+ authorCard = {},
+ authorDetails = {
+ template: "default",
+ },
+ asideCollection = {},
+ templateType = "",
+ noOfVisibleCards = -1,
+ publishedDetails = {},
+ verticalShare = "",
+ shareIconType = "plain-color-svg",
+ premiumStoryIconConfig = {},
+ } = config;
+
+ const visibledCards = noOfVisibleCards < 0 ? story.cards : story.cards.slice(0, noOfVisibleCards);
+ const storyId = get(story, ["id"], "");
+ const timezone = useSelector((state) => get(state, ["qt", "data", "timezone"], null));
+
+ const HeroVideo = () => {
+ return (
+
+
+
+ );
+ };
+
+ const SocialShareComponent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const HeaderCard = () => {
+ return (
+
+
+
+
+
+ );
+ };
+
+ const StoryData = () => {
+ return (
+
+
+
+
+ {!verticalShare &&
}
+
+ {visibledCards.map((card) => {
+ return (
+
+ );
+ })}
+ {firstChild}
+
+
+
+
+ {secondChild}
+
+ );
+ };
+
+ const SideColumn = () => {
+ return (
+ asideCollection && (
+
+ )
+ );
+ };
+
+ const heroPriorityTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+ {verticalShare && }
+ {asideCollection && (
+
+ )}
+ >
+ );
+ };
+
+ const headlinePriorityTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const defaultTemplate = () => {
+ return (
+ <>
+
+
+
+
+
+
+ {verticalShare && }
+
+ >
+ );
+ };
+
+ const getStoryTemplate = (templateType) => {
+ switch (templateType) {
+ case "hero-priority":
+ return heroPriorityTemplate();
+ case "headline-priority":
+ return headlinePriorityTemplate();
+ default:
+ return defaultTemplate();
+ }
+ };
+
+ const templateClass = templateType && templateType !== "default" ? templateType : "default";
+ const dataTestId = templateType ? `video-story-${templateType}` : "video-story";
+ return (
+
+ {getStoryTemplate(templateType)}
+
+ );
+};
+
+VideoStoryTemplate.propTypes = {
+ story: PropTypes.object,
+ config: PropTypes.shape({
+ templateType: PropTypes.string,
+ authorCard: PropTypes.object,
+ asideCollection: PropTypes.object,
+ }),
+ firstChild: PropTypes.node,
+ secondChild: PropTypes.node,
+ storyElementsConfig: PropTypes.object,
+ adComponent: PropTypes.func,
+ widgetComp: PropTypes.func,
+};
+
+export default StateProvider(VideoStoryTemplate);
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/stories.js b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/stories.js
new file mode 100644
index 000000000..1121032f3
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/stories.js
@@ -0,0 +1,204 @@
+import { boolean, color, text } from "@storybook/addon-knobs";
+import React from "react";
+import { withStore, optionalSelect } from "../../../../../storybook";
+import { AdPlaceholder } from "../../../Atoms/AdPlaceholder";
+import { generateCollection, generateStory } from "../../../Fixture";
+import { slotData } from "../../../Fixture/slot-config";
+import VideoStoryTemplate from "./index";
+import Readme from "./README.md";
+
+const story = generateStory();
+const collection = generateCollection({ stories: 4 });
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Aside Collection Name Border Color";
+
+const shareType = {
+ "Vertical Share": "sticky",
+ "Horizontal Share": "",
+};
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+const iconTypeOptions = {
+ "Circular Color Svg": "circular-color-svg",
+ "Circular Plain Svg": "circular-plain-svg",
+ "Plain Svg": "plain-svg",
+ "Plain Color Svg": "plain-color-svg",
+ "Square Svg": "square-svg",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Story Templates/Video Story Template",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ mountAt: "/sub-directory",
+ },
+ },
+ },
+ Readme
+)
+ .add("Default", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: 1,
+ showSection: boolean("Show Section Tag", true),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", true),
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+ const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+ references: {},
+ };
+ return (
+ }
+ secondChild={ }
+ />
+ );
+ })
+ .add("Hero Priority", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: -1,
+ templateType: "hero-priority",
+ showSection: boolean("Show Section Tag", true),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", true),
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+ const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+ references: {},
+ };
+ return (
+ }
+ secondChild={ }
+ />
+ );
+ })
+ .add("Headline Priority", () => {
+ const templateConfig = {
+ shareIconType: optionalSelect("Social Share Icon Type", iconTypeOptions),
+ verticalShare: optionalSelect("Share Type", shareType),
+ theme: color(label, defaultvalue),
+ noOfVisibleCards: -1,
+ templateType: "headline-priority",
+ showSection: boolean("Show Section Tag", true),
+ publishedDetails: {
+ enablePublishedTime: boolean("Show Published Time", true),
+ enableUpdatedTime: boolean("Show Updated Time", false),
+ showReadTime: boolean("Read time", true),
+ },
+ premiumStoryIconConfig: {
+ iconColor: "#F7B500",
+ iconType: "star",
+ enablePremiumStoryIcon: true,
+ },
+ asideCollection: {
+ data: collection,
+ config: {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ title: text("Aside Collection Title", "Trending"),
+ theme: color(label, defaultvalue),
+ adSlot: [{ type: "ad", component: configurableSlot }],
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ },
+ slots: slotData,
+ },
+ };
+ const storyElementsConfig = {
+ summary: {},
+ blurb: {},
+ blockquote: {},
+ quote: {},
+ "also-read": {},
+ "q-and-a": {},
+ question: {},
+ answer: {},
+ references: {},
+ };
+ return (
+ }
+ secondChild={ }
+ />
+ );
+ });
diff --git a/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/video-story.m.css b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/video-story.m.css
new file mode 100644
index 000000000..5959122af
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates/video-story.m.css
@@ -0,0 +1,195 @@
+@import "../../../../../../assets/arrow/stylesheets/mixins.scss";
+
+.default,
+.hero-priority,
+.headline-priority {
+ @include tablet {
+ margin: var(--arrow-spacing-l);
+ @include desktop {
+ margin: 40px 0;
+ }
+ }
+}
+
+.adSlot {
+ margin-top: var(--arrow-spacing-xl);
+}
+
+.hero-video {
+ iframe {
+ width: 100%;
+ height: 190px;
+ @include tablet {
+ height: 540px;
+ @include desktop {
+ height: 640px;
+ }
+ }
+ }
+}
+
+.header-details :global(.arr--sub-headline) {
+ font-size: var(--arrow-fs-s);
+ font-style: italic;
+ @include tablet {
+ font-size: var(--arrow-fs-m);
+ }
+}
+
+.header-details :global(.section-tag) {
+ margin: var(--arrow-fs-m) 0 var(--arrow-fs-tiny);
+}
+
+.headline-priority {
+ .header-details {
+ margin-bottom: var(--arrow-spacing-xl);
+ }
+ .hero-video {
+ iframe {
+ @include tablet {
+ height: 278px;
+ }
+ @include desktop {
+ height: 423px;
+ }
+ }
+ }
+ :global .aside-collection-ad-slot {
+ margin: 0 0 var(--arrow-spacing-l);
+ }
+}
+
+.story-details :global(.arr-author-name) {
+ margin-top: var(--arrow-fs-l);
+ margin-bottom: var(--arrow-spacing-s);
+ @include tablet {
+ margin-top: 0;
+ }
+}
+
+.timestamp-social-share {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: var(--arrow-spacing-s);
+ @include tablet {
+ margin-top: 0;
+ }
+}
+
+.author {
+ margin-top: var(--arrow-spacing-l);
+ @include tablet {
+ margin-top: var(--arrow-spacing-xl);
+ }
+}
+
+.story-tags > * {
+ margin: var(--arrow-spacing-l) 0;
+}
+
+.hero-priority .aside-collection,
+.default .hero-video,
+.hero-priority .hero-video {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / container-end;
+ }
+}
+
+.sticky :global(.arr--share) {
+ grid-column: 4/5;
+ justify-self: flex-end;
+ @include desktop {
+ position: sticky;
+ position: -webkit-sticky;
+ align-self: flex-start;
+ margin-top: var(--arrow-spacing-xs);
+ top: 120px;
+ }
+}
+
+.header-details :global(.arr--headline h6) {
+ font-size: 28px;
+ @include tablet {
+ font-size: var(--arrow-fs-big);
+ }
+}
+
+.sticky.default :global(.arr--share) {
+ @include tablet {
+ grid-column: 8/9;
+ grid-row: 2/3;
+ @include desktop {
+ grid-column: grid-start / container-start;
+ }
+ }
+}
+
+.sticky.headline-priority :global(.arr--share) {
+ @include tablet {
+ grid-column: 8/9;
+ @include desktop {
+ grid-row: 2/3;
+ grid-column: grid-start / container-start;
+ }
+ }
+}
+
+.sticky.hero-priority {
+ :global(.arr--share) {
+ @include tablet {
+ grid-column: 9 / grid-end;
+ @include desktop {
+ grid-column: 3/4;
+ grid-row: 2/3;
+ }
+ }
+ }
+}
+
+.story-content-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: container-start / 9;
+ @include desktop {
+ grid-column: container-start / 10;
+ }
+ }
+}
+
+.video-story-side-column {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 9 / container-end;
+ @include tablet {
+ grid-column: 10 / container-end;
+ }
+ }
+}
+
+.hero-priority .story-content-wrapper {
+ grid-column: 1/5;
+ @include tablet {
+ grid-column: 4/12;
+ }
+}
+
+.sticky .index-2 {
+ z-index: 2;
+ @include desktop {
+ z-index: unset;
+ }
+}
+
+.headline-priority .video-story-side-column {
+ @include tablet {
+ grid-row: 1/6;
+ }
+}
+
+.tablet-index-2 {
+ @include tablet {
+ z-index: 2;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TagIntroductionCard/README.md b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/README.md
new file mode 100644
index 000000000..984526ff3
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/README.md
@@ -0,0 +1,11 @@
+# tag page introduction card
+
+## Usage
+
+# introduction card is responsible for showing tag description and tag name
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TagIntroductionCard/index.js b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/index.js
new file mode 100644
index 000000000..7714af42d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/index.js
@@ -0,0 +1,27 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { getTextColor } from "../../../utils/utils";
+import "./tag-intro-card.module.css";
+
+const TagIntroductionCard = ({ data, config = {} }) => {
+ const { theme = "" } = config;
+ const textColor = getTextColor(theme);
+ const { tagDescription, tagName } = data;
+
+ return (
+
+
{tagName}
+
{tagDescription}
+
+ );
+};
+
+TagIntroductionCard.propTypes = {
+ data: PropTypes.shape({ tagName: PropTypes.string, tagDescription: PropTypes.string }),
+ config: PropTypes.shape({ theme: PropTypes.string }),
+};
+export default TagIntroductionCard;
diff --git a/app/isomorphic/arrow/components/Rows/TagIntroductionCard/stories.js b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/stories.js
new file mode 100644
index 000000000..21ac6973f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/stories.js
@@ -0,0 +1,21 @@
+import React from "react";
+import { withStore } from "../../../../storybook";
+import TagIntroductionCard from "./index";
+import { color } from "@storybook/addon-knobs";
+import Readme from "./README.md";
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const data = {
+ tagName: "News",
+ tagDescription:
+ "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi ",
+};
+
+withStore("Rows/Tag Introduction Card", Readme).add("Default", () => {
+ const contextConfig = {
+ theme: color(label, defaultvalue),
+ };
+ return ;
+});
diff --git a/app/isomorphic/arrow/components/Rows/TagIntroductionCard/tag-intro-card.module.css b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/tag-intro-card.module.css
new file mode 100644
index 000000000..3278d29be
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TagIntroductionCard/tag-intro-card.module.css
@@ -0,0 +1,26 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+
+.description {
+ font-size: var(--arrow-fs-xs);
+ margin-top: var(--arrow-spacing-xs);
+ line-height: var(--arrow-lh-5);
+ @media (min-width: mobile) {
+ font-size: var(--arrow-fs-s);
+ }
+}
+
+.wrapper.dark {
+ border-bottom: 1px solid var(--dark-border);
+}
+.wrapper.light {
+ border-bottom: 1px solid var(--light-border);
+}
+
+.dark {
+ color: var(--arrow-c-mono2);
+}
+.light {
+ color: var(--arrow-c-invert-mono2);
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/README.md b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/README.md
new file mode 100644
index 000000000..d62bec971
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/README.md
@@ -0,0 +1,38 @@
+# Three Col FlexStories
+
+The _ThreeColFlexStories_ component is a three column grid with a more compressed arrangement
+
+## Usage
+
+### Import
+
+```jsx
+import { ThreeColFlexStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "borderLeft",
+ sectionTagTemplate: "borderBottomSml",
+ showSection: true,
+ showRowTitle: true,
+ showAuthor: true,
+ showTime: true,
+ showSubheadline: true,
+ withseparator: true,
+ showFooterButton: false,
+ buttonText: "Read More",
+ slotConfig: [{ type: "story", component: () => }]
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/index.js b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/index.js
new file mode 100644
index 000000000..3774815c2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/index.js
@@ -0,0 +1,189 @@
+import { collectionToStories } from "@quintype/components";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import React from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { generateNavigateSlug, getSlot, getTextColor, navigateTo } from "../../../utils/utils";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import "./three-col-flex-stories.m.css";
+
+const FlexCard = ({ story = {}, config = {} }) => {
+ const { borderColor = "", border = "", theme = "", localizationConfig = {} } = config;
+ return (
+
+ );
+};
+
+const loadMore = ({ isLoading, stories, getMoreStories, subsequentLoadCount = 3 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = stories.length;
+ getMoreStories(offset, limit);
+};
+
+const ThreeColFlexStories = ({
+ collection = {},
+ config = {},
+ getMoreStories,
+ isLoadMoreVisible,
+ isLoading,
+ isolatedLoadMore,
+ hideFirstCard,
+}) => {
+ const collectionItems = collectionToStories(collection);
+ if (collectionItems.length < 1) {
+ return null;
+ }
+
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ footerButton = "",
+ subsequentLoadCount = 3,
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const isAdWidgetEnabled = type === "ad" || type === "widget";
+ const adWidgetSlot = isAdWidgetEnabled ? getSlot(type, component) : null;
+
+ const textColor = getTextColor(theme);
+
+ const stories = type !== "story" ? collectionItems : collectionToStories(collection);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ return (
+
+
+
+ {isAdWidgetEnabled && !hideFirstCard ? (
+ <>
+
+
+ {stories.slice(0, 4).map((story) => (
+
+ ))}
+
+
{adWidgetSlot}
+
+
+ {stories.slice(4).map((story) => (
+
+ ))}
+
+ >
+ ) : (
+
+ {stories.map((story) => (
+
+ ))}
+
+ )}
+ {getLoadMore({ isLoading, stories, getMoreStories, subsequentLoadCount })}
+
+
+ );
+};
+export default StateProvider(ThreeColFlexStories);
+
+ThreeColFlexStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+ hideFirstCard: PropTypes.bool,
+};
+
+FlexCard.propTypes = {
+ story: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ localizationConfig: PropTypes.object,
+ }),
+};
+
+ThreeColFlexStories.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+ hideFirstCard: false,
+};
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/stories.js
new file mode 100644
index 000000000..57ecde0d8
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/stories.js
@@ -0,0 +1,73 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import ThreeColFlexStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 12 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#ff5858";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#ff5858";
+
+const borderTemplate = {
+ default: "",
+ border: "bottom",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Three Col Flex Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/three-col-flex-stories.m.css b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/three-col-flex-stories.m.css
new file mode 100644
index 000000000..a38bf2fef
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFlexStories/three-col-flex-stories.m.css
@@ -0,0 +1,61 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+
+.card-row {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-m);
+ margin-top: var(--arrow-spacing-m);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+.row-with-ad {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-m);
+ margin-bottom: var(--arrow-spacing-m);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: 2fr 1fr;
+ }
+}
+
+.card-with-ad {
+ @media (min-width: desktop) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-m);
+ align-self: center;
+ }
+}
+
+.card {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: desktop) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.card .card-content {
+ margin-left: var(--arrow-spacing-s);
+}
+.ad-wrapper {
+ align-self: center;
+ margin-bottom: var(--arrow-spacing-m);
+ @media (min-width: mobile) {
+ margin-bottom: 0;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/README.md b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/README.md
new file mode 100644
index 000000000..a470c5688
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/README.md
@@ -0,0 +1,28 @@
+# ThreeColFourteenStories(Row/Organisms)
+
+The _ThreeColFourteenStories_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) used to represent a Row with storycards.
+
+The _Row/Organisms_ can be composible based on it's children(Atoms/molecules) or using StoryCardContent component(molecule)
+
+Last column of the ThreeColFourteenStories row supports stories, ad and widget also
+
+## Usage
+
+### Default ThreeColFourteenStories
+
+```jsx
+const contextConfig = {
+ theme: '#ffff',
+ border: 'bottom',
+ showSection: true,
+ showAuthor: true,
+ showTime: false,
+ showRowTitle: true,
+ showSubheadline: false,
+ slotConfig: [{ type: "story"}]
+};
+```
+
+```jsx
+
+```
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/index.js b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/index.js
new file mode 100644
index 000000000..b18cf9330
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/index.js
@@ -0,0 +1,166 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { getTextColor, getSlot } from "../../../utils/utils";
+import { Headline } from "../../Atoms/Headline";
+import { Subheadline } from "../../Atoms/Subheadline/index";
+import { SectionTag } from "../../Atoms/SectionTag";
+
+import "./three-col-fourteen-story.m.css";
+
+const ThreeColFourteenStories = ({ collection = {}, config = {} }) => {
+ const items = collectionToStories(collection);
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ border = "",
+ theme = "",
+ collectionNameTemplate = "",
+ slotConfig = [],
+ } = config;
+ const { type, component } = get(slotConfig, [0], {});
+
+ if (items.length < 1) {
+ return null;
+ }
+
+ const textColor = getTextColor(theme);
+
+ const [firstStory, secondStory, ...restOftheStories] = items || [];
+
+ const storyCallBack = (restOftheStories = []) => {
+ return restOftheStories.slice(10, 13).map((item) => {
+ return (
+
+ );
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {secondStory && (
+
+ )}
+
+
+ {restOftheStories.length > 0 &&
+ restOftheStories.slice(0, 6).map((item) => {
+ return (
+
+ );
+ })}
+
+
+ {restOftheStories.length > 0 &&
+ restOftheStories.slice(6, 10).map((item) => {
+ return (
+
+ );
+ })}
+
{getSlot(type, component, () => storyCallBack(restOftheStories))}
+
+
+
+
+ );
+};
+
+ThreeColFourteenStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ borderColor: PropTypes.string,
+ }),
+};
+
+export default StateProvider(ThreeColFourteenStories);
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/stories.js
new file mode 100644
index 000000000..3893e71c9
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/stories.js
@@ -0,0 +1,60 @@
+import React from "react";
+import { color, boolean } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import ThreeColFourteenStories from "./index";
+import Readme from "././README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 20 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderOptions = {
+ "No Value": "",
+ bottom: "bottom",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Three Col Fourteen Stories",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border Level", borderOptions),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", false),
+ showTime: boolean("Timestamp disable", false),
+ showRowTitle: boolean("Row title", true),
+ showSubheadline: boolean("show sub headline", false),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/three-col-fourteen-story.m.css b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/three-col-fourteen-story.m.css
new file mode 100644
index 000000000..567126eae
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColFourteenStory/three-col-fourteen-story.m.css
@@ -0,0 +1,51 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+
+.storycards-row {
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ align-items: flex-start;
+ }
+}
+
+.card-with-separator {
+ @media (--viewport-medium) {
+ :global .arr--story-card:last-child {
+ border-bottom: none;
+ }
+ }
+
+ :global .arr--story-card {
+ margin-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.first-card {
+ grid-column: 1/6;
+}
+
+.second-card {
+ grid-column: 6/10;
+}
+
+.third-card {
+ grid-column: 10/13;
+ display: flex;
+ flex-direction: column;
+ @media (--viewport-medium) {
+ display: block;
+ }
+}
+
+.ad-slot {
+ order: -1;
+ margin-bottom: var(--arrow-spacing-m);
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColGrid/README.md b/app/isomorphic/arrow/components/Rows/ThreeColGrid/README.md
new file mode 100644
index 000000000..70558d66a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColGrid/README.md
@@ -0,0 +1,36 @@
+# Three Column Grid
+
+The _ThreeColGrid_ component is a basic three column [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms).
+
+Last story of the four column component row supports collection, ad and widget also.
+
+### Import
+```jsx
+import { ThreeColGrid } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColGrid/index.js b/app/isomorphic/arrow/components/Rows/ThreeColGrid/index.js
new file mode 100644
index 000000000..3a14062d1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColGrid/index.js
@@ -0,0 +1,149 @@
+import React, { Fragment } from "react";
+import get from "lodash/get";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { getTextColor, getSlot, navigateTo, generateNavigateSlug } from "../../../utils/utils";
+import { collectionToStories } from "@quintype/components";
+import PropTypes from "prop-types";
+import { StateProvider } from "../../SharedContext";
+import "./three-col.m.css";
+import { ProgressiveHydration } from "../../../hydration-component";
+import { useDispatch, useSelector } from "react-redux";
+import { LoadMoreTarget } from "../../Atoms/LoadMoreTarget";
+
+const loadMore = ({ isLoading, storyItems, getMoreStories, subsequentLoadCount = 3 }) => {
+ if (isLoading) return;
+ const limit = subsequentLoadCount;
+ const offset = storyItems.length;
+ getMoreStories(offset, limit);
+};
+
+const ThreeColGrid = ({ collection, config = {}, getMoreStories, isLoadMoreVisible, isLoading, isolatedLoadMore }) => {
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ theme = "",
+ slotConfig = [],
+ border = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ subsequentLoadCount = 3,
+ } = config;
+ const storyItems = collectionToStories(collection);
+ if (!storyItems.length) return null;
+
+ const textColor = getTextColor(theme);
+ const { footerSlot } = footerSlotConfig;
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ const { type = "", component } = get(slotConfig, [0], {});
+ const adWidgetSlot = type === "ad" || type === "widget" ? getSlot(type, component) : null;
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+
+ const getLoadMore = (opts) => {
+ if (!isLoadMoreVisible) return null;
+
+ if (isolatedLoadMore) {
+ return (
+
+ );
+ }
+
+ const url = generateNavigateSlug(collection, qtConfig);
+ return (
+
+ loadMore(opts)}
+ navigate={() => navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+ };
+
+ const storyCard = (story) => {
+ return (
+
+
+
+
+
+
+ );
+ };
+
+ return (
+
+
+
+
+ {storyItems.slice(0, 2).map((story, index) => {
+ return
{storyCard(story)} ;
+ })}
+ {storyItems.length < 2 &&
}
+ {adWidgetSlot}
+ {storyItems.slice(2).map((story, index) => {
+ return
{storyCard(story)} ;
+ })}
+
+ {getLoadMore({ isLoading, storyItems, getMoreStories, subsequentLoadCount })}
+ {footerSlotComp}
+
+
+ );
+};
+
+ThreeColGrid.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ border: PropTypes.bool,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ subsequentLoadCount: PropTypes.number,
+ }),
+ getMoreStories: PropTypes.func,
+ isLoadMoreVisible: PropTypes.bool,
+ isLoading: PropTypes.bool,
+ isolatedLoadMore: PropTypes.bool,
+};
+
+ThreeColGrid.defaultProps = {
+ getMoreStories: () => {},
+ isLoadMoreVisible: true,
+ isLoading: false,
+};
+
+export default StateProvider(ThreeColGrid);
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColGrid/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColGrid/stories.js
new file mode 100644
index 000000000..de00abb16
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColGrid/stories.js
@@ -0,0 +1,70 @@
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import ThreeColGrid from "./index";
+import { generateCollection } from "../../Fixture/";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import React from "react";
+import Readme from "./README.md";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerButton = {
+ SubsequentLoadCount: "SubsequentLoadCount",
+ NavigateToPage: "NavigateToPage",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Three Col Grid ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showRowTitle: boolean("Row title", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColGrid/three-col.m.css b/app/isomorphic/arrow/components/Rows/ThreeColGrid/three-col.m.css
new file mode 100644
index 000000000..441eaca95
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColGrid/three-col.m.css
@@ -0,0 +1,43 @@
+@custom-media --viewport-medium (width >= 768px);
+.three-col-grid {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ margin: var(--arrow-spacing-xs) 0 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+.ad {
+ margin-bottom: var(--arrow-fs-xs);
+}
+
+.cards {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: 0;
+ }
+}
+
+.wrapper > :first-child,
+.ad {
+ order: -1;
+ @media (--viewport-medium) {
+ order: 0;
+ }
+}
+
+.three-col-grid :global(.arr--story-card .arr--hero-image) {
+ flex-basis: 40%;
+}
+.three-col-grid :global(.arr--story-card .arr--content-wrapper) {
+ flex-basis: 60%;
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/README.md b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/README.md
new file mode 100644
index 000000000..b0371e1ea
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/README.md
@@ -0,0 +1,36 @@
+# Three Column Seven Stories
+
+The _ThreeColSevenStory_ component is a three column [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms)
+
+## Usage
+
+### Import
+```jsx
+import { ThreeColSevenStory } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ showRowTitle: true
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ slotConfig: [{ type: "story", component: () => }],
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
\ No newline at end of file
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/index.js b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/index.js
new file mode 100644
index 000000000..1a653e649
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/index.js
@@ -0,0 +1,190 @@
+import React from "react";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+import { getTextColor, getSlot, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StateProvider } from "../../SharedContext";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import PropTypes from "prop-types";
+import "./three-col-seven-story.m.css";
+import { useDispatch, useSelector } from "react-redux";
+
+const ThreeColSevenStory = ({ collection, config = {} }) => {
+ const storyItems = collectionToStories(collection);
+ if (storyItems.length < 1) {
+ return null;
+ }
+
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ border = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ customCollectionName,
+ navigate = true,
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const borderBottomStyle = border === "bottom" ? "card-bottom" : "card";
+ const lastCardBorderBottom = slotConfig === "ad" ? "border-unset" : "";
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const storyCardComponent = (story) => {
+ return (
+
+
+
+
+ );
+ };
+
+ const storyCardWidthoutBorder = (story) => {
+ return (
+
+
+
+
+ );
+ };
+
+ const storySlot = () => {
+ return (
+
+ {storyItems.slice(5, 6).map((story, index) => {
+ return (
+
+ {storyCardComponent(story)}
+
+ );
+ })}
+ {storyItems.slice(6, 7).map((story, index) => {
+ return (
+
+ {storyCardWidthoutBorder(story)}
+
+ );
+ })}
+
+ );
+ };
+
+ const slot = getSlot(type, component, storySlot);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ const textColor = getTextColor(theme);
+ const Cards = border === "bottom" ? "cards" : "";
+
+ return (
+
+
+
+
+
+ {storyItems.slice(0, 1).map((story, index) => {
+ return (
+
+
+
+ );
+ })}
+
+
+
+ {storyItems.slice(1, 3).map((story, index) => {
+ return (
+
+ {storyCardComponent(story)}
+
+ );
+ })}
+ {storyItems.slice(3, 4).map((story, index) => {
+ return (
+
+ {storyCardWidthoutBorder(story)}
+
+ );
+ })}
+
+
+ {storyItems.slice(4, 5).map((story, index) => {
+ return (
+
+ {storyCardComponent(story)}
+
+ );
+ })}
+
{slot}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+ThreeColSevenStory.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+export default StateProvider(ThreeColSevenStory);
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/stories.js
new file mode 100644
index 000000000..4d9a5cb22
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/stories.js
@@ -0,0 +1,71 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import ThreeColSevenStory from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+import Readme from "./README.md";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "bottom",
+};
+
+const configurableSlot = () => {
+ return ;
+};
+const footerSlot = () => {
+ return ;
+};
+withStore(
+ "Rows/Three Col Seven Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/three-col-seven-story.m.css b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/three-col-seven-story.m.css
new file mode 100644
index 000000000..74451b0f0
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSevenStory/three-col-seven-story.m.css
@@ -0,0 +1,44 @@
+@custom-media --viewport-medium (width >= 768px);
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.three-col-five-stories {
+ display: flex;
+ flex-direction: column;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+}
+
+.card {
+ margin-bottom: var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.card-bottom {
+ margin-bottom: var(--arrow-spacing-s);
+}
+.cards {
+ border-bottom: 1px solid var(--arrow-c-mono5);
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: 0;
+ border-bottom: unset;
+ }
+}
+.border-unset :global(.arr--story-card) {
+ border: none;
+}
+.ads {
+ margin-bottom: var(--arrow-fs-xs);
+}
+
+.wrapper :global(.arr--button) {
+ margin: 0 auto var(--arrow-spacing-xs) auto;
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSixStories/README.md b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/README.md
new file mode 100644
index 000000000..4a52190fc
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/README.md
@@ -0,0 +1,40 @@
+# Three Column Six Stories
+
+The _ThreeColSixStories_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) used to represent a Row with storycards.
+
+The _Row/Organisms_ can be composible based on it's children(Atoms/molecules) or using StoryCardContent component(molecule)
+
+config is the prop which is responsible for handling configuration of 6 stories, 5 stories 1 ad or 5 stories 1 widget row.
+
+second story of the row we can as ad and widget.
+
+### Import
+```jsx
+import { ThreeColSixStories } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ slotConfig: [{ type: "story", component: () => }],
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSixStories/index.js b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/index.js
new file mode 100644
index 000000000..f25540b26
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/index.js
@@ -0,0 +1,173 @@
+import React from "react";
+import PropTypes from "prop-types";
+import get from "lodash/get";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { getTextColor, getSlot, generateNavigateSlug, navigateTo } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+import "./three-col-six-stories.m.css";
+import { useDispatch, useSelector } from "react-redux";
+
+const ThreeColSixStories = ({ collection = {}, config = {} }) => {
+ const itemsArray = collectionToStories(collection);
+
+ const {
+ collectionNameBorderColor = "",
+ borderColor = "",
+ border = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const { footerSlot } = footerSlotConfig;
+
+ if (collectionToStories(collection).length < 1) {
+ return null;
+ }
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+
+ itemsArray.splice(1, 0, null);
+
+ const items = type !== "story" ? itemsArray : collectionToStories(collection);
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const storySlot = () => {
+ return (
+
+
+
+
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
{getSlot(type, component, storySlot)}
+
+
+
+
+
+
+
+
+
+ {items.slice(3, 6).map((story, index) => (
+
+
+
+
+
+
+ ))}
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+export default StateProvider(ThreeColSixStories);
+
+ThreeColSixStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSixStories/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/stories.js
new file mode 100644
index 000000000..07e1ed948
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/stories.js
@@ -0,0 +1,73 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import ThreeColSixStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Three Col Six Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColSixStories/three-col-six-stories.m.css b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/three-col-six-stories.m.css
new file mode 100644
index 000000000..d54c39674
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColSixStories/three-col-six-stories.m.css
@@ -0,0 +1,32 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.storycards-row-two {
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+}
+.storycards-row-one {
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+}
+.card {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+
+.wrapper :global(.arr--button) {
+ margin: 0 auto var(--arrow-spacing-xs) auto;
+}
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/README.md b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/README.md
new file mode 100644
index 000000000..307aae398
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/README.md
@@ -0,0 +1,41 @@
+# Three Column Six Stories
+
+The _ThreeColTwelveStories_ is component that represents 3 child collections withing parent collection.
+
+Column numbers(2 or 3) can vary as per selection of slot type.
+
+config is the prop which is responsible for handling configuration of 12 stories, 8 stories 2 ad or 8 stories 2 widget row.
+
+### Import
+
+```jsx
+import { ThreeColTwelveStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ collectionNameBorderColor: "c70000",
+ theme: theme: "#ffffff",
+ withSeparator: true,
+ collectionNameTemplate: "borderLeft",
+ showAuthor: true,
+ showTime: true,
+ footerButton: {
+ NavigateToPage: "NavigateToPage"
+ },
+ buttonText: "Read More",
+ showButton: true,
+ showReadTime: true,
+ slotConfig: [{ type: "story", component: configurableSlot }, { type: "story", component: configurableSlot }]
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/index.js b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/index.js
new file mode 100644
index 000000000..58272da16
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/index.js
@@ -0,0 +1,124 @@
+import React from "react";
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { Headline } from "../../Atoms/Headline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { HeroImage } from "../../Atoms/HeroImage";
+import "./three-col-twelve-stories.m.css";
+import { generateNavigateSlug, getTextColor, navigateTo, rgbToHex } from "../../../utils/utils";
+import { StateProvider } from "../../SharedContext";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { useDispatch, useSelector } from "react-redux";
+import { SectionTag } from "../../Atoms/SectionTag";
+
+const getChildCollectionData = (collection = {}, config = {}, collectionIndex, qtConfig) => {
+ const stories = collectionToStories(collection);
+ if (!stories.length) return null;
+ const [firstStory, ...otherStories] = stories;
+ const {
+ collectionNameTemplate,
+ withSeparator = true,
+ theme,
+ footerButton = "",
+ localizationConfig = {},
+ collectionNameBorderColor = "",
+ borderColor = "",
+ } = config;
+
+ const sectionTagBorderColor = rgbToHex(borderColor);
+ const dispatch = useDispatch();
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ return (
+
+
+
+
+ {otherStories.slice(0, 3).map((story) => (
+
+ ))}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+ );
+};
+
+function ThreeColTwelveStories({ collection, config = {} }) {
+ const childCollections = get(collection, ["items"], []).filter((collections) => collections.type === "collection");
+ if (childCollections.length < 2) return null; // if number of collection is less than 2 return null
+ const { theme = "", slotConfig = [] } = config;
+ const isSlotTypeStory = get(slotConfig, [0, "type"], "story") === "story";
+ const collectionCount = isSlotTypeStory ? 3 : 2;
+ const textColor = getTextColor(theme);
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const adWidgetComponent = get(slotConfig, [0, "component"]);
+ return (
+
+
+ {childCollections
+ .slice(0, collectionCount)
+ .map((childCollection, index) => getChildCollectionData(childCollection, config, index, qtConfig))}
+ {!isSlotTypeStory && adWidgetComponent &&
{adWidgetComponent()}
}
+
+
+ );
+}
+
+ThreeColTwelveStories.propTypes = {
+ collection: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ slotConfig: PropTypes.array,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+export default StateProvider(ThreeColTwelveStories);
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/stories.js b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/stories.js
new file mode 100644
index 000000000..b9c03ed7d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/stories.js
@@ -0,0 +1,51 @@
+import React from "react";
+import { generateCollections } from "../../Fixture";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ collectionNameTemplates,
+ footerButton,
+ sectionTagTemplates,
+} from "../../../../storybook";
+import Readme from "./README.md";
+import ThreeColTwelveStories from "./index";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+const collection = generateCollections(3);
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Three Col Twelve Stories",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", "#c70000"),
+ theme: color("BG Color", "#ffffff"),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ borderColor: color("Section Tag Border Color", "#3a9fdd"),
+ withSeparator: boolean("Separator", true),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates, "borderLeft"),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/three-col-twelve-stories.m.css b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/three-col-twelve-stories.m.css
new file mode 100644
index 000000000..15d9ddb80
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/ThreeColTwelveStories/three-col-twelve-stories.m.css
@@ -0,0 +1,84 @@
+/*eslint-disable no-descending-specificity */
+@custom-media --viewport-medium (width >= 992px);
+@custom-media --viewport-tab (width >= 768px);
+
+.wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ display: grid;
+ @media (--viewport-tab) {
+ padding: 0 var(--arrow-spacing-l);
+ }
+ @media (--viewport-medium) {
+ padding: 0;
+ grid-column-gap: var(--arrow-spacing-l);
+ grid-template-columns: repeat(3, 2fr);
+ }
+}
+
+/* big card */
+.big-card {
+ position: relative;
+}
+
+.big-card-hero-image {
+ background-color: var(--arrow-c-dark);
+}
+.big-card-hero-image img {
+ opacity: 0.5;
+}
+.big-card-content {
+ position: absolute;
+ bottom: 0;
+ padding: var(--arrow-spacing-s) var(--arrow-spacing-s) var(--arrow-spacing-xs);
+ @media (--viewport-tab) {
+ padding: var(--arrow-spacing-l) var(--arrow-spacing-l) var(--arrow-spacing-m);
+ }
+}
+.big-card-content :global(.arr--headline h6),
+.big-card-content :global(.arr--publish-time),
+.big-card-content :global(.arr-separator),
+.big-card-content :global(.arr--read-time),
+.big-card-content :global(.author-name),
+.big-card-content :global(.arr--section-name .section-tag) {
+ color: var(--arrow-c-light);
+}
+.big-card-content :global(.arr-separator svg),
+.big-card-content :global(.arr--read-time svg) {
+ fill: var(--arrow-c-light);
+}
+
+/* small card */
+.small-card-top-row {
+ display: flex;
+ justify-content: space-between;
+ padding-top: var(--arrow-spacing-m);
+ margin-bottom: var(--arrow-spacing-xs);
+}
+
+.small-card-content {
+ padding-right: var(--arrow-spacing-s);
+}
+
+.small-card :global(.arr--story-card) {
+ padding-bottom: var(--arrow-spacing-xs);
+}
+
+.small-card-hero-image {
+ flex: 0 0 88px;
+}
+
+/* column */
+.column {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ margin-bottom: var(--arrow-spacing-xl);
+}
+
+.ad-widget-container {
+ margin-bottom: var(--arrow-spacing-xl);
+ @media (--viewport-medium) {
+ order: 3;
+ margin-top: 55px;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStory/README.md b/app/isomorphic/arrow/components/Rows/TwoColFourStory/README.md
new file mode 100644
index 000000000..7ce6d0424
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStory/README.md
@@ -0,0 +1,33 @@
+# Two Column Four Stories
+
+The _TwoColFourStories_ component is a basic two column [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms).
+
+### Import
+```jsx
+import { TwoColFourStories } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStory/index.js b/app/isomorphic/arrow/components/Rows/TwoColFourStory/index.js
new file mode 100644
index 000000000..b8c1994dc
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStory/index.js
@@ -0,0 +1,184 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { generateNavigateSlug, getTextColor, navigateTo } from "../../../utils/utils";
+
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+import "./two-col-four-story.m.css";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+export const TwoColFourStories = ({ collection, config = {} }) => {
+ const items = collectionToStories(collection);
+ const {
+ border = "",
+ borderColor = "",
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+ const borderStyle = border === "bottom" ? "border-box" : "";
+ const firstCardBorderStyle = border === "bottom" ? "first-card-border-box" : "";
+
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {items.slice(1, 4).map((story, index) => {
+ if (index === 2) {
+ return (
+
+
+
+
+
+
+ );
+ }
+ return (
+
+
+
+
+
+
+ );
+ })}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+export default StateProvider(TwoColFourStories);
+
+TwoColFourStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStory/stories.js b/app/isomorphic/arrow/components/Rows/TwoColFourStory/stories.js
new file mode 100644
index 000000000..7690df43d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStory/stories.js
@@ -0,0 +1,68 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import TwoColFourStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "bottom",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Two Col Four Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStory/two-col-four-story.m.css b/app/isomorphic/arrow/components/Rows/TwoColFourStory/two-col-four-story.m.css
new file mode 100644
index 000000000..91cbc448b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStory/two-col-four-story.m.css
@@ -0,0 +1,64 @@
+@custom-media --viewport-medium (width >= 768px);
+.two-col-four-story {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+.first-card {
+ margin-bottom: 16px;
+ @media (--viewport-medium) {
+ margin: 0;
+ }
+}
+
+.first-card.border-box {
+ padding: 0;
+ margin-bottom: var(--arrow-fs-xs);
+ @media (--viewport-medium) {
+ margin: 0;
+ }
+}
+
+.border-box.dark {
+ border: 1px solid var(--dark-border);
+ padding: 16px 16px 0;
+}
+
+.border-box.light {
+ border: 1px solid var(--light-border);
+ padding: 16px 16px 0;
+}
+
+.storyCards > .card {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+.storyCards > :last-child {
+ margin-bottom: 0;
+}
+.border-box .card {
+ margin-bottom: var(--arrow-spacing-m);
+}
+.first-card-border-box {
+ padding: 4px 12px 16px 10px;
+}
+.two-col-four-story {
+ :global(.arr--story-card .arr--hero-image) {
+ flex-basis: 40%;
+ }
+ :global(.arr--story-card .arr--content-wrapper) {
+ flex-basis: 60%;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/README.md b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/README.md
new file mode 100644
index 000000000..c36e78dc7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/README.md
@@ -0,0 +1,35 @@
+# Two Column Four Story Highlight
+
+The _TwoColFourStoryHighlight_ component accepts collection of stories.
+
+### Import
+```jsx
+import { TwoColFourStoryHighlight } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ showBorder: true,
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ customBulletColor: "#ffffff",
+ showBullet: true,
+ bulletColorType: "default"
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/index.js b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/index.js
new file mode 100644
index 000000000..069c0366b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/index.js
@@ -0,0 +1,125 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { collectionToStories } from "@quintype/components";
+import { StateProvider } from "../../SharedContext";
+import { generateNavigateSlug, getTextColor, navigateTo, rgbToHex } from "../../../utils/utils";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { Headline } from "../../Atoms/Headline";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import "./two-col-four-story-highlight.m.css";
+
+export const TwoColFourStoryHighlight = ({ collection, config = {} }) => {
+ const items = collectionToStories(collection);
+ if (!items.length) {
+ return null;
+ }
+
+ const {
+ showBorder = true,
+ borderColor = "",
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ showBullet = true,
+ customBulletColor = "",
+ localizationConfig = {},
+ bulletColorType = "default",
+ } = config;
+ const textColor = getTextColor(theme);
+ const [firstStory, ...restStories] = items;
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+ const SectionTagBorderColor = rgbToHex(borderColor);
+
+ return (
+
+
+
+
+ {restStories.slice(0, 3).map((story, index) => (
+
+ {showBullet && (
+
+ )}
+
+
+
+
+ ))}
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+
+
+ );
+};
+
+export default StateProvider(TwoColFourStoryHighlight);
+
+TwoColFourStoryHighlight.propTypes = {
+ collection: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ showBorder: PropTypes.bool,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ borderColor: PropTypes.string,
+ showBullet: PropTypes.bool,
+ localizationConfig: PropTypes.object,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/stories.js b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/stories.js
new file mode 100644
index 000000000..aeae7fd7d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/stories.js
@@ -0,0 +1,48 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import TwoColFourStoryHighlight from "./index";
+import Readme from "./README.md";
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 4 });
+
+const bulletColorTypeTemplates = {
+ default: "Default",
+ custom: "Custom",
+};
+
+withStore(
+ "Rows/Two Col Four Story Highlight ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", "#3a9fdd"),
+ borderColor: color("Section Tag Border Color", "#3a9fdd"),
+ theme: color("color", "#ffffff"),
+ showBorder: boolean("Show Border", true),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("Show Section", true),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Show Time", true),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ showBullet: boolean("Show Bullet", true),
+ customBulletColor: color("Bullet Color", ""),
+ bulletColorType: optionalSelect("Bullet Color Type", bulletColorTypeTemplates),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/two-col-four-story-highlight.m.css b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/two-col-four-story-highlight.m.css
new file mode 100644
index 000000000..04ed2389d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColFourStoryHighlight/two-col-four-story-highlight.m.css
@@ -0,0 +1,117 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.componentWrapper:global(.arrow-component.full-width-with-padding) {
+ display: flex;
+ flex-direction: column-reverse;
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+ @media (min-width: mobile) and (max-width: desktop) {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-s) var(--arrow-spacing-xs) var(--arrow-spacing-s);
+ }
+}
+
+.bulletCardWrapper {
+ display: flex;
+ margin-bottom: var(--arrow-spacing-m);
+}
+
+.componentWrapper :global(.arr--button) {
+ margin: var(--arrow-spacing-m) 0 var(--arrow-spacing-xs);
+}
+
+.bullet {
+ position: relative;
+ width: var(--arrow-spacing-s);
+ margin-right: var(--arrow-spacing-s);
+ margin-left: 0;
+}
+
+.bullet:before,
+.bullet.dark:before {
+ content: "";
+ border-style: solid;
+ border-width: var(--arrow-spacing-xxs) var(--arrow-spacing-xxs) var(--arrow-spacing-xxs) var(--arrow-spacing-xs);
+ border-color: transparent transparent transparent var(--arrow-c-mono2);
+ position: absolute;
+ top: var(--arrow-spacing-xxs);
+}
+
+.bullet.light:before {
+ border-color: transparent transparent transparent var(--arrow-c-invert-mono2);
+}
+
+.bullet:before {
+ border-color: transparent transparent transparent currentColor;
+}
+
+.bulletStyle :global(.arr--button) {
+ margin: var(--arrow-spacing-m) 0 var(--arrow-spacing-xs) 22px;
+}
+
+.highlightWrapper :global(.arr--collection-name) {
+ @media (max-width: mobile) {
+ display: none;
+ }
+}
+
+.coverCardWrapper :global(.arr--collection-name) {
+ @media (min-width: mobile) {
+ display: none;
+ }
+
+ @media (max-width: mobile) {
+ padding: 0 var(--arrow-spacing-s);
+ }
+}
+
+.coverCardWrapper {
+ @media (max-width: mobile) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+
+.storyHighlightWrapper :global(.arr--story-card) {
+ width: 100%;
+}
+
+.highlightWrapper{
+ @media (max-width: mobile) {
+ padding: 0 var(--arrow-spacing-s) var(--arrow-spacing-m) var(--arrow-spacing-s);
+ }
+}
+
+.coverCardWrapper :global(.arr--story-card) > :nth-child(2) {
+ @media (max-width: mobile) {
+ padding: var(--arrow-spacing-m) var(--arrow-spacing-s);
+ }
+}
+
+.storyHighlightWrapper > :last-child :global(.arr--story-card) {
+ border-bottom: none;
+ @media (min-width: mobile) {
+ padding-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+html[dir="rtl"] {
+ .bullet {
+ transform: rotate(180deg);
+ margin-left: var(--arrow-spacing-s);
+ margin-right: 0;
+ }
+
+ .bullet::before {
+ top: unset;
+ bottom: var(--arrow-spacing-xxs);
+ }
+
+ .bulletStyle :global(.arr--button) {
+ margin: var(--arrow-spacing-m) 22px var(--arrow-spacing-xs) 0;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSevenStories/README.md b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/README.md
new file mode 100644
index 000000000..be7694388
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/README.md
@@ -0,0 +1,37 @@
+# Two Col Seven Stories
+
+The _TwoColSevenStories_ component is a [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with seven StoryCards. The last two cards combined is a configurable slot which can be replaced by an AD.
+
+## Usage
+
+### Import
+
+```jsx
+import { TwoColSevenStories } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "LoadMore",
+ showRowTitle: true,
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSevenStories/index.js b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/index.js
new file mode 100644
index 000000000..9e82be3b5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/index.js
@@ -0,0 +1,178 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { generateNavigateSlug, getTextColor, navigateTo, getSlot, rgbToHex } from "../../../utils/utils";
+import { Headline } from "../../Atoms/Headline";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { SectionTag } from "../../Atoms/SectionTag";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+import "./two-col-seven-stories.m.css";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+export const TwoColSevenStories = ({ collection, config = {} }) => {
+ const items = collectionToStories(collection);
+ const {
+ border = "",
+ borderColor = "",
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ footerButton = "",
+ slotConfig = [],
+ localizationConfig = {},
+ } = config;
+
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const isAdWidgetEnabled = type === "ad" || type === "widget";
+ const adWidgetSlot = isAdWidgetEnabled ? getSlot(type, component) : null;
+ const textColor = getTextColor(theme);
+ const sectionTagBorderColor = rgbToHex(borderColor);
+
+ return (
+
+
+
+
+
+
+
+ {items.slice(1, 5).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })}
+ {adWidgetSlot ? (
+
{adWidgetSlot}
+ ) : (
+ items.slice(5, 7).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })
+ )}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+
+
+ );
+};
+
+export default StateProvider(TwoColSevenStories);
+
+TwoColSevenStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
+
+TwoColSevenStories.defaultProps = {
+ theme: "#ffffff",
+ border: "",
+};
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSevenStories/stories.js b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/stories.js
new file mode 100644
index 000000000..719258572
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/stories.js
@@ -0,0 +1,100 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import TwoColSevenStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 10 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const configurableSlot = () => {
+ return ;
+};
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+withStore(
+ "Rows/Two Col Seven Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
+withStore(
+ "Rows/Two Col Seven Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("With AD", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSevenStories/two-col-seven-stories.m.css b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/two-col-seven-stories.m.css
new file mode 100644
index 000000000..d8468b5b9
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSevenStories/two-col-seven-stories.m.css
@@ -0,0 +1,175 @@
+@custom-media --viewport-small (width < 768px);
+@custom-media --viewport-medium (width >= 768px);
+@custom-media --viewport-desktop (width >= 1024px);
+
+.two-col-seven-story {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: var(--arrow-spacing-xl) var(--arrow-spacing-l);
+ }
+ @media (--viewport-desktop) {
+ padding: 0;
+ }
+ .wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-desktop) {
+ display: grid;
+ grid-template-columns: 8fr 0.1fr 4fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+ }
+ .divider.dark {
+ @media (--viewport-desktop) {
+ border-left: 1px solid var(--dark-border);
+ }
+ }
+
+ .divider.light {
+ @media (--viewport-desktop) {
+ border-left: 1px solid var(--light-border);
+ }
+ }
+ :global .arr--author-time span {
+ top: -3px;
+ }
+ :global .arr--read-time span:nth-child(2) {
+ position: relative;
+ top: 1px;
+ }
+ :global .arr--section-name .section-tag {
+ line-height: unset;
+ }
+ .first-card {
+ padding: 0;
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-desktop) {
+ margin-bottom: 0;
+ }
+ :global h2 {
+ @media (--viewport-medium) {
+ font-size: var(--arrow-fs-xl);
+ font-weight: var(--arrow-fw-bold);
+ line-height: normal;
+ }
+ }
+ :global .arr--story-card > div:nth-child(2) {
+ @media (--viewport-medium) {
+ margin: 0;
+ width: 100%;
+ background-color: unset;
+ padding: 0;
+ position: unset;
+ bottom: unset;
+ }
+ }
+ }
+ .storycard {
+ margin-bottom: var(--arrow-spacing-l);
+ :global .arr--read-time > span:nth-child(1) {
+ padding: 0 var(--arrow-spacing-xs) var(--arrow-spacing-xs);
+ @media (--viewport-desktop) {
+ padding: 0 var(--arrow-spacing-xs) 0 var(--arrow-spacing-xs);
+ }
+ }
+ :global .arr--read-time > span:nth-child(2) {
+ margin-bottom: var(--arrow-spacing-xxs);
+ @media (--viewport-desktop) {
+ margin-bottom: 0;
+ }
+ }
+ :global .read-time-wrapper {
+ align-items: center;
+ }
+ }
+
+ .border-full {
+ padding: var(--arrow-spacing-m);
+ }
+
+ .other-cards {
+ margin-top: var(--arrow-spacing-l);
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 2fr 2fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin-left: -8px;
+ }
+ @media (--viewport-desktop) {
+ margin-top: 0;
+ }
+ :global h6 {
+ font-size: var(--arrow-fs-xs);
+ @media (--viewport-desktop) {
+ font-size: var(--arrow-fs-tiny);
+ line-height: normal;
+ }
+ }
+ :global .arr--content-wrapper {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+ }
+
+ .first-card {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ @media (--viewport-small) {
+ height: 24px;
+ width: 24px;
+ }
+ }
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+ }
+
+ .other-cards {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 24px;
+ width: 24px;
+ position: absolute;
+ bottom: var(--arrow-spacing-xs);
+ left: var(--arrow-spacing-xs);
+ z-index: var(--z-index-1);
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+ }
+
+ @keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ .ad-wrapper {
+ grid-column: span 2;
+ margin: var(--arrow-spacing-l) 0;
+ @media (--viewport-desktop) {
+ margin: var(--arrow-spacing-l) var(--arrow-spacing-xl);
+ }
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSixStories/README.md b/app/isomorphic/arrow/components/Rows/TwoColSixStories/README.md
new file mode 100644
index 000000000..726383e3f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSixStories/README.md
@@ -0,0 +1,36 @@
+# Two Column Six Stories
+
+The _TwoColSixStories_ component is a two column six stories organism
+
+## Usage
+
+### Import
+```jsx
+import { TwoColSixStories } from "@quintype/arrow"
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ showRowTitle: true
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ slotConfig: [{ type: "story", component: () => }],
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSixStories/index.js b/app/isomorphic/arrow/components/Rows/TwoColSixStories/index.js
new file mode 100644
index 000000000..992f8bfd7
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSixStories/index.js
@@ -0,0 +1,118 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import { collectionToStories } from "@quintype/components";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { CollectionName } from "../../Atoms/CollectionName";
+import "./two-col-six-stories.m.css";
+import { generateNavigateSlug, getSlot, getTextColor, navigateTo, rgbToHex } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { StateProvider } from "../../SharedContext";
+import { Headline } from "../../Atoms/Headline";
+import { Subheadline } from "../../Atoms/Subheadline";
+import { AuthorWithTime } from "../../Atoms/AuthorWithTimestamp";
+import { SectionTag } from "../../Atoms/SectionTag";
+
+const TwoColSixStories = ({ collection, config = {} }) => {
+ const stories = collectionToStories(collection);
+
+ const {
+ collectionNameBorderColor = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ border = "fullBorder",
+ borderColor = "",
+ localizationConfig = {},
+ } = config;
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const textColor = getTextColor(theme);
+ const borderStyle = border === "fullBorder" ? "border" : "";
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+ const sectionTagBorderColor = rgbToHex(borderColor);
+
+ const [firstStory, ...otherStories] = stories || [];
+
+ const storySlot = () =>
+ otherStories.slice(3, 5).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ });
+
+ const slot = getSlot(type, component, storySlot);
+ return (
+
+
+
+
+
+
+
+ {otherStories.slice(0, 3).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })}
+
+
{slot}
+
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ template="NavigateToPage"
+ />
+
+ );
+};
+
+TwoColSixStories.propTypes = {
+ collection: PropTypes.object,
+ config: PropTypes.object,
+};
+
+export default StateProvider(TwoColSixStories);
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSixStories/stories.js b/app/isomorphic/arrow/components/Rows/TwoColSixStories/stories.js
new file mode 100644
index 000000000..2c3eca391
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSixStories/stories.js
@@ -0,0 +1,67 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import TwoColSixStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 6 });
+
+const label = "BG Color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const configurableSlot = () => {
+ return ;
+};
+
+const border = {
+ default: "fullBorder",
+ noBorder: "noBorder",
+};
+
+withStore(
+ "Rows/Two Col Six Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ slotConfig: [{ type: "story", component: configurableSlot }],
+ showSection: boolean("Section tag", true),
+ showAuthor: boolean("Author", true),
+ showTime: boolean("Timestamp", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ showButton: boolean("Show button", true),
+ buttonText: text("Button text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showReadTime: boolean("Read time", true),
+ border: optionalSelect("Border settings", border),
+ };
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColSixStories/two-col-six-stories.m.css b/app/isomorphic/arrow/components/Rows/TwoColSixStories/two-col-six-stories.m.css
new file mode 100644
index 000000000..0340c472e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColSixStories/two-col-six-stories.m.css
@@ -0,0 +1,136 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.two-col-six-stories {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+.wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (min-width: desktop) {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.first-card {
+ margin-bottom: var(--arrow-spacing-xs);
+ padding-bottom: var(--arrow-spacing-xs);
+ @media (min-width: desktop) {
+ margin: 0;
+ padding-right: var(--arrow-spacing-l);
+ padding-bottom: 0;
+ }
+}
+
+.first-card.border.light {
+ border-bottom: 1px solid var(--light-border);
+ @media (min-width: mobile) {
+ border-bottom: none;
+ }
+ @media (min-width: desktop) {
+ border-right: 1px solid var(--light-border);
+ }
+}
+
+.first-card.border.dark {
+ border-bottom: 1px solid var(--dark-border);
+ @media (min-width: mobile) {
+ border-bottom: none;
+ }
+ @media (min-width: desktop) {
+ border-right: 1px solid var(--dark-border);
+ }
+}
+
+.other-cards {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ }
+ @media (min-width: desktop) {
+ grid-template-columns: 1fr;
+ }
+}
+
+.card {
+ :global(.arr--hero-image) {
+ margin-top: var(--arrow-spacing-l);
+ }
+ margin-bottom: var(--arrow-spacing-xs);
+ padding-bottom: var(--arrow-spacing-xs);
+ @media (min-width: desktop) {
+ padding-bottom: var(--arrow-spacing-xs);
+ }
+}
+
+.card.border.light {
+ border-bottom: 1px solid var(--light-border);
+}
+
+.card.border.dark {
+ border-bottom: 1px solid var(--dark-border);
+}
+
+.first-card {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 64px;
+ width: 64px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: var(--z-index-1);
+ @media (--viewport-small) {
+ height: 24px;
+ width: 24px;
+ }
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+}
+
+.other-cards {
+ :global .arr--responsive-hero-image .story-icon,
+ :global .arr--fallback-hero-image .story-icon {
+ height: 20px;
+ width: 20px;
+ position: absolute;
+ bottom: var(--arrow-spacing-xxs);
+ left: var(--arrow-spacing-xxs);
+ z-index: var(--z-index-1);
+ }
+
+ :global .live-icon {
+ animation: blink 1.5s linear infinite;
+ vertical-align: middle;
+ height: 24px;
+ width: 24px;
+ }
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 0;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/README.md b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/README.md
new file mode 100644
index 000000000..2f77cba5e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/README.md
@@ -0,0 +1,34 @@
+# Two Column Ten Stories Sidebar
+
+The _TwoColTenStoriesSidebar_ component accepts collection of stories.
+
+### Import
+
+```jsx
+import { TwoColTenStoriesSidebar } from "@quintype/arrow";
+```
+
+### Setting the Row context
+
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showRowTitle: true,
+ showFooterButton: true,
+ buttonText: "Load More",
+ slotConfig: [{ type: "ad", component: () => }]
+};
+```
+
+### Use as a component
+
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/index.js b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/index.js
new file mode 100644
index 000000000..5fb76c4d0
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/index.js
@@ -0,0 +1,190 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { collectionToStories } from "@quintype/components";
+import { StateProvider } from "../../SharedContext";
+import { generateNavigateSlug, getTextColor, navigateTo, getSlot } from "../../../utils/utils";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StoryCardWithBulletPoint } from "../../Molecules/StoryCardWithBulletPoint";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+import "./two-col-ten-stories-sidebar.m.css";
+
+export const TwoColTenStoriesSidebar = ({ collection, config = {} }) => {
+ const childCollections = get(collection, ["items"], []).filter((collections) => collections.type === "collection");
+ if (childCollections.length < 2) return null;
+ // if number of collection is less than 2 return null
+
+ const firstCollectionStories = collectionToStories(childCollections[0]);
+ const sidebarCollectionStories = collectionToStories(childCollections[1]);
+
+ if (!firstCollectionStories.length && !sidebarCollectionStories.length) return null;
+
+ const {
+ border = "",
+ borderColor = "",
+ collectionNameBorderColor = "",
+ theme = "",
+ slotConfig = [],
+ collectionNameTemplate = "",
+ } = config;
+ const textColor = getTextColor(theme);
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const firstCardBorderStyle = border === "bottom" ? "first-card-border-box" : "";
+ const { type = "story", component } = get(slotConfig, [0], {});
+ const isAdWidgetEnabled = type === "ad" || type === "widget";
+ const adWidgetSlot = isAdWidgetEnabled ? getSlot(type, component) : null;
+ let storyCounter = 0;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {firstCollectionStories.slice(1, 5).map((story, index) => {
+ return (
+
+
+
+
+
+
+ );
+ })}
+
+
navigateTo(dispatch, generateNavigateSlug(childCollections[0], qtConfig))}
+ qtConfig={qtConfig}
+ />
+
+
+
+
+
+ {sidebarCollectionStories.slice(0, 4).map((story) => (
+
+
+
+ ))}
+
+
+
+
+
+ {adWidgetSlot ? (
+
{adWidgetSlot}
+ ) : (
+ sidebarCollectionStories.slice(5, 8).map((story) => (
+
+
+
+ ))
+ )}
+
+
+
navigateTo(dispatch, generateNavigateSlug(childCollections[1], qtConfig))}
+ qtConfig={qtConfig}
+ />
+
+
+ );
+};
+
+export default StateProvider(TwoColTenStoriesSidebar);
+
+TwoColTenStoriesSidebar.propTypes = {
+ collection: PropTypes.object.isRequired,
+ config: PropTypes.shape({
+ theme: PropTypes.string,
+ showBorder: PropTypes.bool,
+ collectionNameTemplate: PropTypes.string,
+ slotConfig: PropTypes.array,
+ collectionNameBorderColor: PropTypes.string,
+ borderColor: PropTypes.string,
+ localizationConfig: PropTypes.object,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/stories.js b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/stories.js
new file mode 100644
index 000000000..f74aa38df
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/stories.js
@@ -0,0 +1,45 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import { withStore, optionalSelect, sectionTagTemplates, collectionNameTemplates } from "../../../../storybook";
+import TwoColTenStoriesHighlight from "./index";
+import Readme from "./README.md";
+import { generateCollections } from "../../Fixture";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+const collection = generateCollections(2);
+
+const configurableSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Two Col Ten Stories Sidebar ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color("Collection Name Border Color", "#3a9fdd"),
+ borderColor: color("Section Tag Border Color", "#3a9fdd"),
+ theme: color("color", "#ffffff"),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("Show Section", true),
+ showAuthor: boolean("Show Author", true),
+ showTime: boolean("Show Time", true),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ slotConfig: [{ type: "ad", component: configurableSlot }],
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/two-col-ten-stories-sidebar.m.css b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/two-col-ten-stories-sidebar.m.css
new file mode 100644
index 000000000..eb284ec30
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColTenStoriesSidebar/two-col-ten-stories-sidebar.m.css
@@ -0,0 +1,53 @@
+/* eslint-disable scss/at-rule-no-unknown */
+@value viewports: "../../../../../assets/arrow/stylesheets/viewports.m.css";
+@value mobile from viewports;
+@value desktop from viewports;
+
+.component-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ padding: 0;
+ }
+}
+
+.collection-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ }
+}
+
+.sidebar-collection-wrapper {
+ padding: 0 var(--arrow-spacing-s);
+ @media (min-width: desktop) {
+ padding: 0;
+ padding-left: var(--arrow-spacing-m);
+ }
+}
+
+.sidebar-cards-container {
+ @media (min-width: mobile) {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-l) 0;
+ }
+ @media (min-width: desktop) {
+ display: block;
+ }
+}
+
+.story-cards > .card,
+.story-cards > .first-card {
+ margin-bottom: var(--arrow-spacing-s);
+ @media (min-width: mobile) {
+ margin-bottom: var(--arrow-spacing-m);
+ }
+}
+
+.sidebar-cards-container .card {
+ margin-bottom: var(--arrow-spacing-m);
+}
diff --git a/app/isomorphic/arrow/components/Rows/TwoColThreeStory/README.md b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/README.md
new file mode 100644
index 000000000..a53737c61
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/README.md
@@ -0,0 +1,40 @@
+# Two Col Three Stories
+
+
+The _TwoColThreeStories_ component is a basic [row](https://bradfrost.com/blog/post/atomic-web-design/#organisms) with three StoryCards.
+
+
+There are no configurable slots in this row.
+
+## Usage
+
+### Import
+
+```jsx
+import { TwoColThreeStories } from "@quintype/arrow"
+
+```
+
+### Setting the Row context
+```jsx
+const contextConfig = {
+ theme: "#ffffff",
+ border: "",
+ collectionNameTemplate: "default",
+ sectionTagTemplate: "default",
+ showSection: true,
+ showAuthor: true,
+ showTime: true,
+ showFooterButton: true,
+ buttonText: "LoadMore",
+ showRowTitle: true,
+ footerSlotConfig: { footerSlot: () => }
+};
+```
+
+### Use as a component
+```jsx
+
+```
+
+
diff --git a/app/isomorphic/arrow/components/Rows/TwoColThreeStory/index.js b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/index.js
new file mode 100644
index 000000000..605b22243
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/index.js
@@ -0,0 +1,148 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { collectionToStories } from "@quintype/components";
+import { CollectionName } from "../../Atoms/CollectionName";
+import { HeroImage } from "../../Atoms/HeroImage";
+import { StorycardContent } from "../../Molecules/StorycardContent";
+import { StoryCard } from "../../Molecules/StoryCard";
+import { StateProvider } from "../../SharedContext";
+import { generateNavigateSlug, getTextColor, navigateTo } from "../../../utils/utils";
+import { LoadmoreButton } from "../../Atoms/Loadmore";
+
+import "./two-col-three-story.m.css";
+import { useDispatch, useSelector } from "react-redux";
+import get from "lodash/get";
+
+export const TwoColThreeStories = ({ collection, config = {} }) => {
+ const items = collectionToStories(collection);
+ const {
+ border = "",
+ borderColor = "",
+ collectionNameBorderColor = "",
+ theme = "",
+ collectionNameTemplate = "",
+ footerSlotConfig = {},
+ footerButton = "",
+ } = config;
+ const { footerSlot } = footerSlotConfig;
+
+ if (items.length < 1) {
+ return null;
+ }
+
+ const dispatch = useDispatch();
+ const qtConfig = useSelector((state) => get(state, ["qt", "config"], {}));
+ const url = generateNavigateSlug(collection, qtConfig);
+
+ const textColor = getTextColor(theme);
+ const footerSlotComp = footerSlot ? footerSlot() : null;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {items.slice(1, 3).map((story, index) => {
+ if (index === 1) {
+ return (
+
+
+
+
+
+
+ );
+ } else
+ return (
+
+
+
+
+
+
+ );
+ })}
+
+
+
navigateTo(dispatch, url)}
+ qtConfig={qtConfig}
+ />
+ {footerSlotComp}
+
+
+ );
+};
+
+export default StateProvider(TwoColThreeStories);
+
+TwoColThreeStories.propTypes = {
+ /** collection is the array of objects which is returning by API */
+ collection: PropTypes.object.isRequired,
+ /** The config for the Row */
+ config: PropTypes.shape({
+ borderColor: PropTypes.string,
+ theme: PropTypes.string,
+ border: PropTypes.string,
+ footerButton: PropTypes.string,
+ collectionNameTemplate: PropTypes.string,
+ collectionNameBorderColor: PropTypes.string,
+ }),
+};
diff --git a/app/isomorphic/arrow/components/Rows/TwoColThreeStory/stories.js b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/stories.js
new file mode 100644
index 000000000..310175340
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/stories.js
@@ -0,0 +1,68 @@
+import React from "react";
+import { color, boolean, text } from "@storybook/addon-knobs";
+import {
+ withStore,
+ optionalSelect,
+ sectionTagTemplates,
+ collectionNameTemplates,
+ footerButton,
+} from "../../../../storybook";
+import TwoColThreeStories from "./index";
+import Readme from "./README.md";
+import { AdPlaceholder } from "../../Atoms/AdPlaceholder";
+
+import { generateCollection } from "../../Fixture";
+
+const collection = generateCollection({ stories: 8 });
+
+const label = "color";
+const defaultvalue = "#ffffff";
+
+const collectionNameDefaultValue = "#3a9fdd";
+const collectionNameBorderColorLabel = "Collection Name Border Color";
+
+const sectionTagBorderColor = "Section Tag Border Color";
+const sectionTagDefaultvalue = "#3a9fdd";
+
+const borderTemplate = {
+ default: "",
+ border: "full",
+};
+
+const footerSlot = () => {
+ return ;
+};
+
+withStore(
+ "Rows/Two Col Three Stories ",
+ {
+ qt: {
+ config: {
+ "cdn-image": "thumbor-stg.assettype.com",
+ },
+ },
+ },
+ Readme
+)
+ .addDecorator((story) => {story()}
)
+ .add("Default", () => {
+ const contextConfig = {
+ collectionNameBorderColor: color(collectionNameBorderColorLabel, collectionNameDefaultValue),
+ borderColor: color(sectionTagBorderColor, sectionTagDefaultvalue),
+ theme: color(label, defaultvalue),
+ border: optionalSelect("Border", borderTemplate),
+ collectionNameTemplate: optionalSelect("Collection Name Templates", collectionNameTemplates),
+ sectionTagTemplate: optionalSelect("Section Tag Templates", sectionTagTemplates),
+ showSection: boolean("section disable", true),
+ showAuthor: boolean("Author disable", true),
+ showTime: boolean("Timestamp disable", true),
+ footerButton: optionalSelect("Footer Button", footerButton),
+ buttonText: text("Footer text", "Read More"),
+ showRowTitle: boolean("Row title", true),
+ footerSlotConfig: { footerSlot: footerSlot },
+ showButton: boolean("Show button", true),
+ showReadTime: boolean("Read time", true),
+ };
+
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Rows/TwoColThreeStory/two-col-three-story.m.css b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/two-col-three-story.m.css
new file mode 100644
index 000000000..41cc167e2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Rows/TwoColThreeStory/two-col-three-story.m.css
@@ -0,0 +1,36 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.two-col-three-story {
+ padding: 0 var(--arrow-spacing-s);
+ @media (--viewport-medium) {
+ padding: 0;
+ }
+}
+.wrapper {
+ margin: var(--arrow-spacing-xs) 0;
+ @media (--viewport-medium) {
+ display: grid;
+ grid-template-columns: 9fr 3fr;
+ grid-column-gap: var(--arrow-spacing-l);
+ margin: var(--arrow-spacing-m) 0;
+ }
+}
+
+.first-card {
+ padding: 0;
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: 0;
+ }
+}
+
+.storycard {
+ margin-bottom: var(--arrow-spacing-m);
+ @media (--viewport-medium) {
+ margin-bottom: var(--arrow-spacing-l);
+ }
+}
+
+.border-full {
+ padding: var(--arrow-spacing-m);
+}
diff --git a/app/isomorphic/arrow/components/SharedContext.js b/app/isomorphic/arrow/components/SharedContext.js
new file mode 100644
index 000000000..89821aba6
--- /dev/null
+++ b/app/isomorphic/arrow/components/SharedContext.js
@@ -0,0 +1,14 @@
+import React, { createContext, useContext } from "react";
+
+export const StateContext = createContext();
+
+// eslint-disable-next-line react/display-name
+export const StateProvider = (RowComponent) => (props) =>
+ (
+ // eslint-disable-next-line react/prop-types
+
+
+
+ );
+
+export const useStateValue = () => useContext(StateContext);
diff --git a/app/isomorphic/arrow/components/Svgs/Loading/loading.js b/app/isomorphic/arrow/components/Svgs/Loading/loading.js
new file mode 100644
index 000000000..1fb520e4e
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/Loading/loading.js
@@ -0,0 +1,134 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "./loading.m.css";
+
+export const Loading = ({ width = 40, height = 30, iconColor = "#99B0CB" }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+Loading.propTypes = {
+ width: PropTypes.object,
+ height: PropTypes.object,
+ iconColor: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/Loading/loading.m.css b/app/isomorphic/arrow/components/Svgs/Loading/loading.m.css
new file mode 100644
index 000000000..7b2c0f8da
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/Loading/loading.m.css
@@ -0,0 +1,5 @@
+.loading-indicator {
+ position: absolute;
+ top: 50%;
+ left: calc(50% - 20px);
+}
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/facebook.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/facebook.js
new file mode 100644
index 000000000..05cdedb76
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/facebook.js
@@ -0,0 +1,16 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const Facebook = ({ color = "#3B5998" }) => {
+ return (
+
+
+
+ );
+};
+Facebook.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/instagram.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/instagram.js
new file mode 100644
index 000000000..16163c849
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/instagram.js
@@ -0,0 +1,26 @@
+import React from "react";
+
+export const Instagram = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/linkedin.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/linkedin.js
new file mode 100644
index 000000000..0c2b09506
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/linkedin.js
@@ -0,0 +1,16 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const LinkedIn = ({ color = "#0077B5" }) => {
+ return (
+
+
+
+ );
+};
+LinkedIn.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/pinterest.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/pinterest.js
new file mode 100644
index 000000000..de95b5f9b
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/pinterest.js
@@ -0,0 +1,12 @@
+import React from "react";
+
+export const Pinterest = () => {
+ return (
+
+
+
+ );
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/twitter.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/twitter.js
new file mode 100644
index 000000000..ffd784a64
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/twitter.js
@@ -0,0 +1,16 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const Twitter = ({ color = "#1CB7EB" }) => {
+ return (
+
+
+
+ );
+};
+Twitter.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/whatsapp.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/whatsapp.js
new file mode 100644
index 000000000..f9f1369bc
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/whatsapp.js
@@ -0,0 +1,18 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const WhatsApp = ({ color = "#4AC959" }) => {
+ return (
+
+
+
+ );
+};
+
+WhatsApp.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/SocialIcons/youtube.js b/app/isomorphic/arrow/components/Svgs/SocialIcons/youtube.js
new file mode 100644
index 000000000..3b812e160
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/SocialIcons/youtube.js
@@ -0,0 +1,17 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const Youtube = ({ color = "#F61C0D" }) => {
+ return (
+
+
+
+ );
+};
+Youtube.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/caret-right.svg b/app/isomorphic/arrow/components/Svgs/caret-right.svg
new file mode 100644
index 000000000..dc4d38619
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/caret-right.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/isomorphic/arrow/components/Svgs/clock-icon.js b/app/isomorphic/arrow/components/Svgs/clock-icon.js
new file mode 100644
index 000000000..db257b544
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/clock-icon.js
@@ -0,0 +1,18 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const ClockIcon = ({ color = "#333" }) => {
+ return (
+
+
+
+ );
+};
+
+ClockIcon.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/close-icon.js b/app/isomorphic/arrow/components/Svgs/close-icon.js
new file mode 100644
index 000000000..553ea311f
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/close-icon.js
@@ -0,0 +1,15 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const CloseIcon = ({ color = "#333" }) => {
+ return (
+
+
+
+
+
+ );
+};
+CloseIcon.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/crown.js b/app/isomorphic/arrow/components/Svgs/crown.js
new file mode 100644
index 000000000..b4462b32d
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/crown.js
@@ -0,0 +1,32 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const CrownIcon = ({ height, width, color = "#D8D8D8", positionTop }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+CrownIcon.propTypes = {
+ height: PropTypes.string,
+ width: PropTypes.string,
+ color: PropTypes.string,
+ positionTop: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/fallbackImage.js b/app/isomorphic/arrow/components/Svgs/fallbackImage.js
new file mode 100644
index 000000000..fabe2e795
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/fallbackImage.js
@@ -0,0 +1,42 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const ImageFallbackIcon = ({ width = 40, height = 30, iconColor = "#99B0CB" }) => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+ImageFallbackIcon.propTypes = {
+ width: PropTypes.object,
+ height: PropTypes.object,
+ iconColor: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/hyperlink.js b/app/isomorphic/arrow/components/Svgs/hyperlink.js
new file mode 100644
index 000000000..94c6728ac
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/hyperlink.js
@@ -0,0 +1,18 @@
+import React from "react";
+
+export const HyperLinkIcon = () => {
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/app/isomorphic/arrow/components/Svgs/key.js b/app/isomorphic/arrow/components/Svgs/key.js
new file mode 100644
index 000000000..de8bb6bf2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/key.js
@@ -0,0 +1,35 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const KeyIcon = ({ height, width, color = "#D5D5D5", positionTop }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+KeyIcon.propTypes = {
+ height: PropTypes.string,
+ width: PropTypes.string,
+ color: PropTypes.string,
+ positionTop: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/left-arrow.js b/app/isomorphic/arrow/components/Svgs/left-arrow.js
new file mode 100644
index 000000000..b24fb7aa3
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/left-arrow.js
@@ -0,0 +1,17 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const LeftArrow = ({ color = "#333" }) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+LeftArrow.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/liveicon.js b/app/isomorphic/arrow/components/Svgs/liveicon.js
new file mode 100644
index 000000000..aef20513a
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/liveicon.js
@@ -0,0 +1,14 @@
+import React from "react";
+
+function LiveIcon() {
+ return (
+
+
+
+ );
+}
+
+export default LiveIcon;
diff --git a/app/isomorphic/arrow/components/Svgs/lock.js b/app/isomorphic/arrow/components/Svgs/lock.js
new file mode 100644
index 000000000..57dbbd5c1
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/lock.js
@@ -0,0 +1,32 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const LockIcon = ({ height, width, color = "#D5D5D5", positionTop }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+LockIcon.propTypes = {
+ height: PropTypes.string,
+ width: PropTypes.string,
+ color: PropTypes.string,
+ positionTop: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/right-arrow.js b/app/isomorphic/arrow/components/Svgs/right-arrow.js
new file mode 100644
index 000000000..3b89a3295
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/right-arrow.js
@@ -0,0 +1,17 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const RightArrow = ({ color = "#333" }) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+RightArrow.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/share-icon.js b/app/isomorphic/arrow/components/Svgs/share-icon.js
new file mode 100644
index 000000000..47b111b52
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/share-icon.js
@@ -0,0 +1,18 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const ShareIcon = ({ color = "#0D0D0D" }) => {
+ return (
+
+
+
+ );
+};
+
+ShareIcon.propTypes = {
+ color: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/star.js b/app/isomorphic/arrow/components/Svgs/star.js
new file mode 100644
index 000000000..285c2b812
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/star.js
@@ -0,0 +1,30 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const StarIcon = ({ width, height, color = "#D5D5D5", positionTop = "" }) => {
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+StarIcon.propTypes = {
+ height: PropTypes.string,
+ width: PropTypes.string,
+ color: PropTypes.string,
+ positionTop: PropTypes.string,
+};
diff --git a/app/isomorphic/arrow/components/Svgs/user-fallback-icon.js b/app/isomorphic/arrow/components/Svgs/user-fallback-icon.js
new file mode 100644
index 000000000..a134dc0e2
--- /dev/null
+++ b/app/isomorphic/arrow/components/Svgs/user-fallback-icon.js
@@ -0,0 +1,19 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+export const UserFallbackIcon = ({ width = 120, height = 120 }) => {
+ return (
+
+
+
+ );
+};
+
+UserFallbackIcon.propTypes = {
+ width: PropTypes.object,
+ height: PropTypes.object,
+};
diff --git a/app/isomorphic/arrow/components/Typography/README.md b/app/isomorphic/arrow/components/Typography/README.md
new file mode 100644
index 000000000..f4e416152
--- /dev/null
+++ b/app/isomorphic/arrow/components/Typography/README.md
@@ -0,0 +1,7 @@
+# Typography for Arrow
+
+A modular typescale is maintained by the library as a set of standard defaults for any type related styling.
+
+The following are the default implementations by Arrow. This can be bypassed by overriding the values in the project that imports `@quintype/arrow`.
+
+
diff --git a/app/isomorphic/arrow/components/Typography/index.js b/app/isomorphic/arrow/components/Typography/index.js
new file mode 100644
index 000000000..9bfecf0e5
--- /dev/null
+++ b/app/isomorphic/arrow/components/Typography/index.js
@@ -0,0 +1,47 @@
+import React from "react";
+import "./style.m.css";
+
+export const Typography = () => {
+ return (
+
+
Typscale
+
+
+ title-h1
+ These violent delights have violent ends.
+
+
+ title-h2
+ These violent delights have violent ends.
+
+
+ title-h3
+ These violent delights have violent ends.
+
+
+ title-h4
+ These violent delights have violent ends.
+
+
+ paragraph
+ These violent delights have violent ends.
+
+
+ paragraph-alt
+ These violent delights have violent ends.
+
+
+ author name
+ These violent delights have violent ends.
+
+
+ section-tag
+ These violent delights have violent ends.
+
+
+ timestamp
+ These violent delights have violent ends.
+
+
+ );
+};
diff --git a/app/isomorphic/arrow/components/Typography/stories.js b/app/isomorphic/arrow/components/Typography/stories.js
new file mode 100644
index 000000000..fe2c39a56
--- /dev/null
+++ b/app/isomorphic/arrow/components/Typography/stories.js
@@ -0,0 +1,10 @@
+import React from "react";
+import { withStore } from "../../../storybook";
+import { Typography } from "./index";
+import Readme from "./README.md";
+
+withStore("Introduction", {}, Readme)
+ .addDecorator((story) => {story()}
)
+ .add("Typography", () => {
+ return ;
+ });
diff --git a/app/isomorphic/arrow/components/Typography/style.m.css b/app/isomorphic/arrow/components/Typography/style.m.css
new file mode 100644
index 000000000..31b616343
--- /dev/null
+++ b/app/isomorphic/arrow/components/Typography/style.m.css
@@ -0,0 +1,11 @@
+@custom-media --viewport-medium (width >= 768px);
+
+.wrapper {
+ display: grid;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 25px;
+ @media (--viewport-medium) {
+ display: flex;
+ }
+}
diff --git a/app/isomorphic/arrow/entry-points.js b/app/isomorphic/arrow/entry-points.js
new file mode 100644
index 000000000..e6e2ba4f6
--- /dev/null
+++ b/app/isomorphic/arrow/entry-points.js
@@ -0,0 +1,360 @@
+/**
+ * #### IMPORTANT NOTE ####
+ * - be careful while changing the 'name' of a component
+ * - rollup uses this name to create the folder in which the CSS file for that component is placed
+ * - so if you change the name, it basically will change Arrow's API
+ * Example:
+ * - if you change {name: "Text"} to {name: "Text123"},
+ * - arrow users will have to change `import "@quintype/arrow/Text/styles.arrow.css"` to `import "@quintype/arrow/Text123/styles.arrow.css"` everywhere.
+ * ----x----x----x----x----
+ *
+ * Every component that arrow exports should export its CSS separately
+ * Entries here have to match with components exported in src/index.js
+ *
+ */
+
+const path = require("path");
+
+function getEntryPoints() {
+ const entryPoints = [
+ {
+ // this is the main bundle containing all components. It's name must be "index"
+ name: "index",
+ path: path.resolve(path.join(__dirname, "/index.js")),
+ },
+ ...storyElementsEntryPoints(),
+ ...atomsEntryPoints(),
+ ...moleculesEntryPoints(),
+ ...rowsEntryPoints(),
+ ];
+ return entryPoints.reduce((accumulator, currentBundle) => {
+ accumulator[currentBundle.name] = currentBundle.path;
+ return accumulator;
+ }, {});
+}
+
+function storyElementsEntryPoints() {
+ const basePath = path.resolve(path.join(__dirname, "/components/Atoms/StoryElements"));
+ return [
+ {
+ name: "Text",
+ path: `${basePath}/Text`,
+ },
+ {
+ name: "Quote",
+ path: `${basePath}/Quote`,
+ },
+ {
+ name: "Blurb",
+ path: `${basePath}/Blurb`,
+ },
+ {
+ name: "AlsoRead",
+ path: `${basePath}/AlsoRead`,
+ },
+ {
+ name: "Summary",
+ path: `${basePath}/Summary`,
+ },
+ {
+ name: "BlockQuote",
+ path: `${basePath}/BlockQuote`,
+ },
+ {
+ name: "Image",
+ path: `${basePath}/Image`,
+ },
+ {
+ name: "Video",
+ path: `${basePath}/Video`,
+ },
+ {
+ name: "BigFact",
+ path: `${basePath}/BigFact`,
+ },
+ {
+ name: "StoryElement",
+ path: `${basePath}/StoryElement`,
+ },
+ {
+ name: "QuestionAnswer",
+ path: `${basePath}/QuestionAnswer`,
+ },
+ {
+ name: "ImageGallery",
+ path: `${basePath}/imageGallery`,
+ },
+ {
+ name: "Reference",
+ path: `${basePath}/Reference`,
+ },
+ {
+ name: "Attachment",
+ path: `${basePath}/Attachment`,
+ },
+ {
+ name: "ImageSlideshow",
+ path: `${basePath}/ImageSlideshow`,
+ },
+ ];
+}
+
+function atomsEntryPoints() {
+ const basePath = path.resolve(path.join(__dirname, "/components/Atoms"));
+ return [
+ {
+ name: "Author",
+ path: `${basePath}/Author`,
+ },
+ {
+ name: "AuthorWithTime",
+ path: `${basePath}/AuthorWithTimestamp`,
+ },
+ {
+ name: "CollectionName",
+ path: `${basePath}/CollectionName`,
+ },
+ {
+ name: "Headline",
+ path: `${basePath}/Headline`,
+ },
+ {
+ name: "HeroImage",
+ path: `${basePath}/HeroImage`,
+ },
+ {
+ name: "LoadmoreButton",
+ path: `${basePath}/Loadmore`,
+ },
+ {
+ name: "SectionTag",
+ path: `${basePath}/SectionTag`,
+ },
+ {
+ name: "Subheadline",
+ path: `${basePath}/Subheadline`,
+ },
+ {
+ name: "TimeStamp",
+ path: `${basePath}/TimeStamp`,
+ },
+ {
+ name: "ScrollSnap",
+ path: `${basePath}/ScrollSnap`,
+ },
+ ];
+}
+
+function moleculesEntryPoints() {
+ const basePath = path.resolve(path.join(__dirname, "/components/Molecules"));
+ return [
+ {
+ name: "StoryCard",
+ path: `${basePath}/StoryCard`,
+ },
+ {
+ name: "StorycardContent",
+ path: `${basePath}/StorycardContent`,
+ },
+ {
+ name: "StoryElementCard",
+ path: `${basePath}/StoryElementCard`,
+ },
+ {
+ name: "KeyEvents",
+ path: `${basePath}/KeyEvents`,
+ },
+ {
+ name: "SocialShareTemplate",
+ path: `${basePath}/SocialShareTemplate`,
+ },
+ {
+ name: "PageIntroductionCard",
+ path: `${basePath}/PageIntroductionCard`,
+ },
+ ];
+}
+
+function rowsEntryPoints() {
+ const basePath = path.resolve(path.join(__dirname, "/components/Rows"));
+ return [
+ {
+ name: "FourColGrid",
+ path: `${basePath}/FourColGrid`,
+ },
+ {
+ name: "OneColStoryList",
+ path: `${basePath}/OneColStoryList`,
+ },
+ {
+ name: "ThreeColGrid",
+ path: `${basePath}/ThreeColGrid`,
+ },
+ {
+ name: "TwoColFourStories",
+ path: `${basePath}/TwoColFourStory`,
+ },
+ {
+ name: "TwoColThreeStories",
+ path: `${basePath}/TwoColThreeStory`,
+ },
+ {
+ name: "TwoColSevenStories",
+ path: `${basePath}/TwoColSevenStories`,
+ },
+ {
+ name: "ThreeColSixStories",
+ path: `${basePath}/ThreeColSixStories`,
+ },
+ {
+ name: "ThreeColSevenStory",
+ path: `${basePath}/ThreeColSevenStory`,
+ },
+ {
+ name: "ElevenStories",
+ path: `${basePath}/ElevenStories`,
+ },
+ {
+ name: "CollectionFilter",
+ path: `${basePath}/CollectionFilter`,
+ },
+ {
+ name: "HalfScreenSlider",
+ path: `${basePath}/HalfScreenSlider`,
+ },
+ {
+ name: "FourStorySlider",
+ path: `${basePath}/FourStorySlider`,
+ },
+ {
+ name: "FourStorySliderPortrait",
+ path: `${basePath}/FourStorySliderPortrait`,
+ },
+ {
+ name: "FullScreenSlider",
+ path: `${basePath}/FullScreenSlider`,
+ },
+ {
+ name: "ThreeColFourteenStories",
+ path: `${basePath}/ThreeColFourteenStory`,
+ },
+ {
+ name: "FourColTwelveStories",
+ path: `${basePath}/FourColTwelveStory`,
+ },
+ {
+ name: "TagIntroductionCard",
+ path: `${basePath}/TagIntroductionCard`,
+ },
+ {
+ name: "AuthorIntroductionCard",
+ path: `${basePath}/AuthorIntroductionCard`,
+ },
+ {
+ name: "AsideCollection",
+ path: `${basePath}/AsideCollection`,
+ },
+ {
+ name: "StoryCollection",
+ path: `${basePath}/AsideCollection`,
+ },
+ {
+ name: "VideoStoryTemplate",
+ path: `${basePath}/StoryTemplates/VideoStoryTemplates`,
+ },
+ {
+ name: "TextStoryTemplate",
+ path: `${basePath}/StoryTemplates/TextStoryTemplates`,
+ },
+ {
+ name: "LiveBlogStoryTemplate",
+ path: `${basePath}/StoryTemplates/LiveBlogStorytemplates`,
+ },
+ {
+ name: "ListicleStoryTemplate",
+ path: `${basePath}/StoryTemplates/ListicleStoryTemplates`,
+ },
+ {
+ name: "ListComponent",
+ path: `${basePath}/ListComponent`,
+ },
+ {
+ name: "MagazineEditions",
+ path: `${basePath}/MagazineEditions`,
+ },
+ {
+ name: "MagazineWidget",
+ path: `${basePath}/MagazineWidget`,
+ },
+ {
+ name: "MagazineHeaderCard",
+ path: `${basePath}/MagazineHeaderCard`,
+ },
+ {
+ name: "PhotoStoryTemplate",
+ path: `${basePath}/StoryTemplates/PhotoStoryTemplates`,
+ },
+ {
+ name: "AuthorsList",
+ path: `${basePath}/AuthorsList`,
+ },
+ {
+ name: "FourColSixteenStories",
+ path: `${basePath}/FourColSixteenStories`,
+ },
+ {
+ name: "FourColFiveStories",
+ path: `${basePath}/FourColFiveStories`,
+ },
+ {
+ name: "TwoColSixStories",
+ path: `${basePath}/TwoColSixStories`,
+ },
+ {
+ name: "TwoColFourStoryHighlight",
+ path: `${basePath}/TwoColFourStoryHighlight`,
+ },
+ {
+ name: "OpinionCollection",
+ path: `${basePath}/OpinionCollection`,
+ },
+ {
+ name: "ThreeColFlexStories",
+ path: `${basePath}/ThreeColFlexStories`,
+ },
+ {
+ name: "SixColSixStories",
+ path: `${basePath}/SixColSixStories`,
+ },
+ {
+ name: "TwoColTenStoriesSidebar",
+ path: `${basePath}/TwoColTenStoriesSidebar`,
+ },
+ {
+ name: "ThreeColTwelveStories",
+ path: `${basePath}/ThreeColTwelveStories`,
+ },
+ {
+ name: "FourTabbedBigStorySlider",
+ path: `${basePath}/FourTabbedBigStorySlider`,
+ },
+ {
+ name: "AlternateCollectionFilter",
+ path: `${basePath}/AlternateCollectionFilter`,
+ },
+ {
+ name: "Listicles",
+ path: `${basePath}/Listicles`,
+ },
+ {
+ name: "FourColPortraitStories",
+ path: `${basePath}/FourColPortraitStories`,
+ },
+ {
+ name: "AstrologyCollection",
+ path: `${basePath}/AstrologyCollection`,
+ },
+ ];
+}
+
+module.exports = { getEntryPoints };
diff --git a/app/isomorphic/arrow/guide/stories.js b/app/isomorphic/arrow/guide/stories.js
new file mode 100644
index 000000000..9081e2c9d
--- /dev/null
+++ b/app/isomorphic/arrow/guide/stories.js
@@ -0,0 +1,18 @@
+import React from "react";
+import { addReadme } from "storybook-readme";
+import readme from "../../README.md";
+import { storiesOf } from "@storybook/react";
+
+storiesOf("Introduction", module)
+ .addDecorator(addReadme)
+ .addDecorator((story) => {story()}
)
+ .addParameters({
+ options: {
+ showPanel: false,
+ },
+ readme: {
+ content: readme,
+ codeTheme: "a11y-dark",
+ },
+ })
+ .add("Getting Started", () =>
);
diff --git a/app/isomorphic/arrow/hydration-component/index.js b/app/isomorphic/arrow/hydration-component/index.js
new file mode 100644
index 000000000..0e158e034
--- /dev/null
+++ b/app/isomorphic/arrow/hydration-component/index.js
@@ -0,0 +1,37 @@
+import React, { useEffect, useRef } from "react";
+import ReactDOM from "react-dom";
+import useNearScreen from "./useNearScreen";
+
+import PropTypes from "prop-types";
+
+const EMPTY_HTML = { __html: "" };
+const isServer = typeof window === "undefined";
+
+export const ProgressiveHydration = (props) => {
+ const { children } = props;
+ const ref = useRef(null);
+ const isNearScreen = useNearScreen({ ref });
+
+ useEffect(() => {
+ const { current: el } = ref;
+ // CLIENT:
+ // If we want to force the hydration OR the element is near screen
+ // then we hydrate the content to get the functionality ready
+ if (isNearScreen) {
+ const action = el.hasChildNodes() ? "hydrate" : "render";
+ ReactDOM[action](children, el);
+ }
+ }, [children, isNearScreen]);
+
+ // SERVER: Just render the content as usual
+ if (isServer) {
+ return {children}
;
+ }
+
+ // CLIENT: Avoid hydration until we say so
+ return
;
+};
+
+ProgressiveHydration.propTypes = {
+ children: PropTypes.element,
+};
diff --git a/app/isomorphic/arrow/hydration-component/useNearScreen.js b/app/isomorphic/arrow/hydration-component/useNearScreen.js
new file mode 100644
index 000000000..d5fc0f14c
--- /dev/null
+++ b/app/isomorphic/arrow/hydration-component/useNearScreen.js
@@ -0,0 +1,56 @@
+import { useState, useEffect } from "react";
+
+let observer;
+const isClient = typeof window !== "undefined";
+
+const hasIntersectionObserverSupport = () => typeof window.IntersectionObserver !== "undefined";
+
+// import intersection observer polyfill only if on client
+// and if not supported by browser
+const getIntersectionObserver = () => {
+ return Promise.resolve(isClient && !hasIntersectionObserverSupport() && import("intersection-observer"));
+};
+
+// options for intersection observer in order to improve the effect
+const intersectionObserverOptions = {
+ root: null, // windows viewport will be used
+ rootMargin: "200px 0px 0px 0px",
+};
+
+const handleIntersect = (entries, observer) => {
+ entries
+ .filter((entry) => entry.isIntersecting)
+ .forEach((entry) => {
+ const { target } = entry;
+ target._onIntersect(observer);
+ });
+};
+
+// get observer from the cache or create a new one
+const getObserver = () =>
+ getIntersectionObserver().then((_) => {
+ if (observer) return observer;
+ if (isClient) {
+ return new window.IntersectionObserver(handleIntersect, intersectionObserverOptions);
+ }
+ });
+
+export default ({ ref }) => {
+ const [show, setShow] = useState(false);
+
+ useEffect(() => {
+ const { current } = ref;
+ if (!current) return;
+ // get observer async and only when needed
+ getObserver().then((observer) => observer.observe(current));
+ // mutate the current target to add a method to be executed when intersecting
+ current._onIntersect = (observer) => {
+ setShow(true);
+ observer.unobserve(current);
+ };
+ // clean observed element when unmounted
+ return () => (current._isSubscribed = false);
+ }, [ref]);
+
+ return show;
+};
diff --git a/app/isomorphic/arrow/index.js b/app/isomorphic/arrow/index.js
new file mode 100644
index 000000000..f2bc3256c
--- /dev/null
+++ b/app/isomorphic/arrow/index.js
@@ -0,0 +1,91 @@
+/**
+ * IMPORTANT NOTE:
+ * Please make sure to make a corresponding entry in entry-points.js
+ * every export here needs to have a corresponding entry in entry-points.js
+ */
+
+// ATOMS
+export { Author } from "./components/Atoms/Author";
+export { AuthorWithTime } from "./components/Atoms/AuthorWithTimestamp";
+export { CollectionName } from "./components/Atoms/CollectionName";
+export { Headline } from "./components/Atoms/Headline";
+export { HeroImage } from "./components/Atoms/HeroImage";
+export { LoadmoreButton } from "./components/Atoms/Loadmore";
+export { ExtendedLoadMore } from "./components/Atoms/ExtendedLoadMore";
+export { SectionTag } from "./components/Atoms/SectionTag";
+export { Subheadline } from "./components/Atoms/Subheadline";
+export { TimeStamp } from "./components/Atoms/TimeStamp";
+export { LoadMoreTarget } from "./components/Atoms/LoadMoreTarget";
+export { ScrollSnap } from "./components/Atoms/ScrollSnap";
+
+// STORY ELEMENTS
+export { Text } from "./components/Atoms/StoryElements/Text";
+export { Quote } from "./components/Atoms/StoryElements/Quote";
+export { Blurb } from "./components/Atoms/StoryElements/Blurb";
+export { AlsoRead } from "./components/Atoms/StoryElements/AlsoRead";
+export { Summary } from "./components/Atoms/StoryElements/Summary";
+export { BlockQuote } from "./components/Atoms/StoryElements/BlockQuote";
+export { Image } from "./components/Atoms/StoryElements/Image";
+export { Video } from "./components/Atoms/StoryElements/Video";
+export { BigFact } from "./components/Atoms/StoryElements/BigFact";
+export { StoryElement } from "./components/Atoms/StoryElements/StoryElement";
+export { QuestionAnswer } from "./components/Atoms/StoryElements/QuestionAnswer";
+export { ImageGallery } from "./components/Atoms/StoryElements/imageGallery";
+export { Reference } from "./components/Atoms/StoryElements/Reference";
+export { Attachment } from "./components/Atoms/StoryElements/Attachment";
+export { ImageSlideshow } from "./components/Atoms/StoryElements/ImageSlideshow";
+
+// MOLECULES
+export { StoryCard } from "./components/Molecules/StoryCard";
+export { StorycardContent } from "./components/Molecules/StorycardContent";
+export { StoryElementCard } from "./components/Molecules/StoryElementCard";
+export { SocialShareTemplate } from "./components/Molecules/SocialShareTemplate";
+export { default as KeyEvents } from "./components/Molecules/KeyEvents";
+export { PageIntroductionCard } from "./components/Molecules/PageIntroductionCard";
+export { default as PortraitStoryCard } from "./components/Molecules/PortraitStoryCard";
+
+// ROWS
+export { default as FourColGrid } from "./components/Rows/FourColGrid";
+export { default as OneColStoryList } from "./components/Rows/OneColStoryList";
+export { default as ThreeColGrid } from "./components/Rows/ThreeColGrid";
+export { default as TwoColFourStories } from "./components/Rows/TwoColFourStory";
+export { default as TwoColThreeStories } from "./components/Rows/TwoColThreeStory";
+export { default as TwoColSevenStories } from "./components/Rows/TwoColSevenStories";
+export { default as ThreeColSixStories } from "./components/Rows/ThreeColSixStories";
+export { default as ThreeColSevenStory } from "./components/Rows/ThreeColSevenStory";
+export { default as ElevenStories } from "./components/Rows/ElevenStories";
+export { default as CollectionFilter } from "./components/Rows/CollectionFilter";
+export { default as HalfScreenSlider } from "./components/Rows/HalfScreenSlider";
+export { default as FourStorySlider } from "./components/Rows/FourStorySlider";
+export { default as FourStorySliderPortrait } from "./components/Rows/FourStorySliderPortrait";
+export { default as FullScreenSlider } from "./components/Rows/FullScreenSlider";
+export { default as ThreeColFourteenStories } from "./components/Rows/ThreeColFourteenStory";
+export { default as FourColTwelveStories } from "./components/Rows/FourColTwelveStory";
+export { default as TagIntroductionCard } from "./components/Rows/TagIntroductionCard";
+export { default as AuthorIntroductionCard } from "./components/Rows/AuthorIntroductionCard";
+export { default as AuthorsList } from "./components/Rows/AuthorsList";
+export { default as AsideCollection } from "./components/Rows/AsideCollection";
+export { StoryCollection } from "./components/Rows/AsideCollection";
+export { default as VideoStoryTemplate } from "./components/Rows/StoryTemplates/VideoStoryTemplates";
+export { default as TextStoryTemplate } from "./components/Rows/StoryTemplates/TextStoryTemplates";
+export { default as LiveBlogStoryTemplate } from "./components/Rows/StoryTemplates/LiveBlogStorytemplates";
+export { default as ListicleStoryTemplate } from "./components/Rows/StoryTemplates/ListicleStoryTemplates";
+export { default as ListComponent } from "./components/Rows/ListComponent";
+export { default as MagazineEditions } from "./components/Rows/MagazineEditions";
+export { default as MagazineWidget } from "./components/Rows/MagazineWidget";
+export { default as MagazineHeaderCard } from "./components/Rows/MagazineHeaderCard";
+export { default as PhotoStoryTemplates } from "./components/Rows/StoryTemplates/PhotoStoryTemplates";
+export { default as FourColSixteenStories } from "./components/Rows/FourColSixteenStories";
+export { default as FourColFiveStories } from "./components/Rows/FourColFiveStories";
+export { default as TwoColSixStories } from "./components/Rows/TwoColSixStories";
+export { default as TwoColFourStoryHighlight } from "./components/Rows/TwoColFourStoryHighlight";
+export { default as OpinionCollection } from "./components/Rows/OpinionCollection";
+export { default as ThreeColFlexStories } from "./components/Rows/ThreeColFlexStories";
+export { default as SixColSixStories } from "./components/Rows/SixColSixStories";
+export { default as TwoColTenStoriesSidebar } from "./components/Rows/TwoColTenStoriesSidebar";
+export { default as ThreeColTwelveStories } from "./components/Rows/ThreeColTwelveStories";
+export { default as FourTabbedBigStorySlider } from "./components/Rows/FourTabbedBigStorySlider";
+export { default as AlternateCollectionFilter } from "./components/Rows/AlternateCollectionFilter";
+export { default as FourColPortraitStories } from "./components/Rows/FourColPortraitStories";
+export { default as AstrologyCollection } from "./components/Rows/AstrologyCollection";
+export { default as Listicles } from "./components/Rows/Listicles";
diff --git a/app/isomorphic/arrow/utils/testing-utils.js b/app/isomorphic/arrow/utils/testing-utils.js
new file mode 100644
index 000000000..602d12d55
--- /dev/null
+++ b/app/isomorphic/arrow/utils/testing-utils.js
@@ -0,0 +1,8 @@
+export function mockIntersectionObserver() {
+ const mockIntersectionObserver = jest.fn();
+ mockIntersectionObserver.mockReturnValue({
+ observe: jest.fn().mockReturnValue(null),
+ unobserve: jest.fn().mockReturnValue(null),
+ });
+ window.IntersectionObserver = mockIntersectionObserver;
+}
diff --git a/app/isomorphic/arrow/utils/utils.js b/app/isomorphic/arrow/utils/utils.js
new file mode 100644
index 000000000..c9394ddc6
--- /dev/null
+++ b/app/isomorphic/arrow/utils/utils.js
@@ -0,0 +1,330 @@
+import get from "lodash/get";
+import PropTypes from "prop-types";
+import upperCase from "lodash/upperCase";
+import Timeago from "react-timeago";
+import React from "react";
+import lowerCase from "lodash/lowerCase";
+import { format, utcToZonedTime } from "date-fns-tz";
+
+const canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement);
+
+export const sharePageUrl = canUseDOM && window.location.href;
+
+export function truncate(string = "", limit = 60, ellipsis = true) {
+ const putEllipsis = ellipsis ? " ..." : "";
+ return typeof string === "string" && string.length > limit ? string.substring(0, limit) + putEllipsis : string;
+}
+
+export const timestampToFormat = (value, unit, suffix, timestamp, config = {}, languageCode = "en") => {
+ const {
+ showTime = false,
+ isTimeFirst = false,
+ isUpperCase = false,
+ disableMeridiem = false,
+ timeFormat = "12hours",
+ localizedZeroToPad,
+ } = config;
+ const monthList = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+ const dateTime = new Date(timestamp);
+ const date = dateTime.getDate().toString().padStart(2, 0);
+
+ const month = monthList[dateTime.getMonth()];
+ let localizedMonth = month;
+ if (isUpperCase) {
+ localizedMonth = upperCase(month);
+ }
+
+ const year = dateTime.getFullYear();
+ const { localizedMeridiem, localizedMonths, direction = "ltr" } = config;
+ const time = formatAMPM(timestamp, timeFormat, localizedZeroToPad, languageCode);
+ let timeWithLocalizedMeridiem = time;
+ let timetoShow;
+
+ if (localizedMonths) {
+ localizedMonth = get(localizedMonths, [lowerCase(month)]) || month;
+ }
+ const localizedDate = Number(date).toLocaleString(languageCode);
+ const localizedYear = year.toLocaleString(languageCode).replace(/,/g, "");
+
+ if (localizedMeridiem) {
+ const isAm = time.includes("am");
+ const { am = "am", pm = "pm" } = localizedMeridiem;
+ timeWithLocalizedMeridiem = (isAm ? time.replace("am", am) : time.replace("pm", pm)) || time;
+ }
+
+ if (direction === "rtl") {
+ timetoShow = showTime ? `${timeWithLocalizedMeridiem}` : "";
+ return `${localizedDate} ${localizedMonth}, ${localizedYear} ${timetoShow} `;
+ } else {
+ if (disableMeridiem) {
+ return `${localizedMonth} ${localizedDate}, ${localizedYear}`;
+ } else if (isTimeFirst) {
+ return `${timeWithLocalizedMeridiem}, ${localizedDate} ${localizedMonth} ${localizedYear}`;
+ } else {
+ timetoShow = showTime ? `, ${timeWithLocalizedMeridiem}` : "";
+ return `${localizedDate} ${localizedMonth}, ${localizedYear}${timetoShow}`;
+ }
+ }
+};
+
+export function formatter(value, unit, suffix, date, config, languageCode) {
+ const showDateForUnits = ["day", "week", "month", "year"];
+ // suffix here refers to ago
+ // unit here refers to seconds, minute, minutes,...
+ const localizedSuffix = get(config, ["localizedPublishTime", suffix]) || suffix;
+ let localizedUnitOfTime;
+
+ if ((unit === "hour" && value > 23) || showDateForUnits.indexOf(unit) !== -1) {
+ return timestampToFormat(value, unit, suffix, date, config, languageCode);
+ } else if (value > 1) {
+ localizedUnitOfTime = get(config, ["localizedPublishTime", `${unit}s`]) || `${unit}s`;
+ return `${value.toLocaleString(languageCode)} ${localizedUnitOfTime} ${localizedSuffix}`;
+ }
+ localizedUnitOfTime = get(config, ["localizedPublishTime", unit]) || unit;
+ return `${value.toLocaleString(languageCode)} ${localizedUnitOfTime} ${localizedSuffix} `;
+}
+
+function formatAMPM(timestamp, timeFormat = "12hours", localizedZeroToPad, languageCode) {
+ const dateTime = new Date(timestamp);
+ let hours = dateTime.getHours();
+ let minutes = dateTime.getMinutes();
+ const ampm = hours >= 12 ? "pm" : "am";
+ hours = hours % 12;
+ hours = hours || 12;
+ const padZero = languageCode === "en" ? "0" : localizedZeroToPad;
+ minutes = minutes < 10 ? padZero + minutes.toLocaleString(languageCode) : minutes.toLocaleString(languageCode);
+ const strTime = hours.toLocaleString(languageCode) + ":" + minutes + " " + ampm.toLocaleString(languageCode);
+ if (timeFormat === "24hours") {
+ return dateTime.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
+ }
+ return strTime;
+}
+
+export const hexToRGB = (hex) => {
+ const r = parseInt(hex.slice(1, 3), 16);
+ const g = parseInt(hex.slice(3, 5), 16);
+ const b = parseInt(hex.slice(5, 7), 16);
+ return { r, g, b };
+};
+
+export const getLuminanceOfColor = (hex) => {
+ const { r, g, b } = hexToRGB(hex);
+ return r * 0.299 + g * 0.587 + b * 0.114;
+};
+
+export const rgbToHex = (color = "rgba(255, 255, 255, 0)") => {
+ const rgba = color.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
+ return rgba && rgba.length === 4
+ ? "#" +
+ ("0" + parseInt(rgba[1], 10).toString(16)).slice(-2) +
+ ("0" + parseInt(rgba[2], 10).toString(16)).slice(-2) +
+ ("0" + parseInt(rgba[3], 10).toString(16)).slice(-2)
+ : color;
+};
+
+const clientWidths = {
+ mobile: 767,
+ tablet: 1024,
+};
+
+export const clientWidth = (device) => {
+ const foundDeviceWidth = get(clientWidths, device);
+ if (foundDeviceWidth) {
+ return get(global, "document.documentElement.clientWidth") <= foundDeviceWidth;
+ }
+ return false;
+};
+
+export const getTextColor = (theme = "rgb(255,255,255)") => {
+ const isRegexCheck = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(theme);
+ const getHexValue = !isRegexCheck ? rgbToHex(theme) : theme;
+ const textColor = getLuminanceOfColor(getHexValue) < 155 ? "light" : "dark";
+ return textColor;
+};
+
+export function getNumberOfStoriesToShow(numberOfStoriesToShow) {
+ return numberOfStoriesToShow ? parseInt(numberOfStoriesToShow) : 1;
+}
+
+export const getSlot = (type, component, storyCallback, collectionCallback) => {
+ switch (type) {
+ case "story":
+ return storyCallback();
+ case "collection":
+ return collectionCallback();
+ default:
+ return component ? component() : null;
+ }
+};
+
+export const getSlug = (collection, config = {}) => {
+ const { magazineSlug = "", isArchivePage = false } = config;
+ if (isArchivePage) {
+ return `/magazine/archive/${magazineSlug}`;
+ }
+ if (magazineSlug) {
+ return `/magazine/${magazineSlug}/${collection.slug}`;
+ }
+ const isSection = collection.template === "section";
+ if (isSection) {
+ const sectionId = get(collection, ["metadata", "section", "0", "id"], "");
+ const { sections } = config;
+ const section = sections && sections.find((section) => section.id === sectionId);
+ return section ? section["section-url"] : "/";
+ }
+ return `/collection/${collection.slug}`;
+};
+
+export function isExternalStory(story = {}) {
+ const { "story-template": storyTemplate = "", metadata = {} } = story;
+ return storyTemplate === "news-elsewhere" ? get(metadata, ["reference-url"], "") : "";
+}
+
+// Adds target='_blank' attribute to anchor tags if not present
+export const updateContentLinks = (content) => {
+ let updatedContent = content;
+ const anchorTagsList = content.match(/()/g);
+ anchorTagsList &&
+ anchorTagsList.forEach((anchorTag) => {
+ const isTargetPresent = /target=/g.test(anchorTag);
+ if (!isTargetPresent) {
+ updatedContent = updatedContent.replace(
+ anchorTag,
+ ` {
+ return (
+ value == null || // From standard.js: Always use === - but value == null is allowed to check null || undefined
+ (typeof value === "object" && Object.entries(value).length === 0 && value.constructor === Object) ||
+ (typeof value === "string" && value.trim().length === 0)
+ );
+};
+
+export const shapeConfig = PropTypes.shape({
+ "sketches-host": PropTypes.string,
+ "cdn-name": PropTypes.string,
+ "cdn-image": PropTypes.string,
+});
+
+export const shapeStory = PropTypes.shape({
+ headline: PropTypes.string,
+ "last-published-at": PropTypes.number,
+ subheadline: PropTypes.string,
+});
+
+export const getStoryUrl = (story, defaultValue = "") => {
+ return get(story, ["story-template"]) === "news-elsewhere"
+ ? get(story, ["metadata", "reference-url"], "")
+ : story.url || defaultValue;
+};
+
+export const detectComponent = (component, adComponent, widgetComp) => {
+ const { type } = component;
+ switch (type) {
+ case "ad":
+ return adComponent(component);
+ case "widget":
+ return widgetComp(component);
+ default:
+ return null;
+ }
+};
+
+export const facebookMobileVideoResizeFix = () => {
+ if (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(max-width: 767px)").matches) {
+ const applyFixAfterTime = 3000;
+ const aspectRatioAdjustmentFactor = 0.7;
+ setTimeout(() => {
+ document
+ .querySelectorAll(".story-element-jsembed.story-element-jsembed-facebook-post span")
+ .forEach((element) => {
+ if (!element.classList.contains("fixed-mobile-fb-video")) {
+ element.style.height = `${Math.ceil(element.offsetHeight * aspectRatioAdjustmentFactor)}px`;
+ element.classList.add("fixed-mobile-fb-video");
+ }
+ });
+ }, applyFixAfterTime);
+ }
+};
+
+export const navigateTo = (dispatch, url = "") => {
+ global.app.navigateToPage(dispatch, url);
+};
+
+export const generateNavigateSlug = (collection = {}, config = {}, customUrlPath) => {
+ if (customUrlPath) {
+ return customUrlPath;
+ }
+ const { magazineSlug = "", isArchivePage = false } = config;
+ if (isArchivePage) {
+ return `/magazine/archive/${magazineSlug}`;
+ }
+ if (magazineSlug) {
+ return `/magazine/${magazineSlug}/${collection.slug}`;
+ }
+ if (collection) {
+ return getSlug(collection, config);
+ }
+ return "/";
+};
+
+export const getCollectionData = async (collectionSlug, mountAtPrefix = "") => {
+ try {
+ const result = await (
+ await fetch(`${mountAtPrefix}/api/v1/collections/${collectionSlug}?item-type=story&limit=6`)
+ ).json();
+ return result;
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ }
+};
+
+export function getAuthorTwitterUrl(author) {
+ const twitterUrl = get(author, ["social", "twitter", "url"], null);
+ const twitterHandle = get(author, ["social", "twitter", "handle"], null);
+
+ if (!author) return "";
+
+ if (twitterUrl) {
+ return twitterUrl;
+ }
+
+ if (twitterHandle && twitterHandle.startsWith("@")) {
+ return `https://www.twitter.com/${twitterHandle.slice(1)}`;
+ }
+
+ if (twitterHandle) {
+ return `https://www.twitter.com/${twitterHandle}`;
+ }
+
+ return "";
+}
+
+export const getTimeStamp = (date, formatter, config = {}, languageCode, template, timezone = null) => {
+ if (timezone && template === "story") {
+ const zonedTime = timezone && utcToZonedTime(date, timezone);
+ const formatZonedTime = zonedTime && format(zonedTime, "yyyy-MM-dd'T'HH:mm:ssXXX", { timeZone: timezone });
+ const timeStamp = timestampToFormat("", "", "", date, config);
+ return {timeStamp} ;
+ }
+
+ return (
+ formatter(value, unit, suffix, date, config, languageCode)}
+ />
+ );
+};
+
+export function removeHtmlTags(str) {
+ if (str === null || str === "") return false;
+ else str = str.toString();
+
+ return str.replace(/(<([^>]+)>)/gi, "");
+}
diff --git a/app/isomorphic/arrow/utils/utils.test.js b/app/isomorphic/arrow/utils/utils.test.js
new file mode 100644
index 000000000..afd72702f
--- /dev/null
+++ b/app/isomorphic/arrow/utils/utils.test.js
@@ -0,0 +1,108 @@
+import { updateContentLinks, isEmpty, sharePageUrl, getAuthorTwitterUrl } from "./utils";
+
+describe("updateContentLinks", () => {
+ it("should add target='_blank' to anchor tags if target attribute not present", () => {
+ const content =
+ "Coronavirus in India News Live Updates: India has recorded the highest single-day spike in fresh cases of novel coronavirus with the number crossing 1,300.";
+ const updatedContent = updateContentLinks(content);
+ expect(updatedContent).toBe(
+ "
Coronavirus in India News Live Updates: India has recorded the highest single-day spike in fresh cases of novel coronavirus with the number crossing 1,300."
+ );
+ });
+});
+
+describe("getAuthorTwitterUrl", () => {
+ const author = {
+ id: 123981,
+ name: "Ravigopal Kesari",
+ slug: "ravigopal-kesari",
+ };
+ const socialData = {
+ social: {
+ twitter: {
+ url: "https://www.twitter.com/sabqorg",
+ handle: "elonmusk",
+ },
+ },
+ };
+ it("should return empty string when the author object doesn't have 'social' key", () => {
+ const authorTwitterLink = getAuthorTwitterUrl(author);
+ expect(authorTwitterLink).toBe("");
+ });
+
+ it("should return twitter url when both url and handle are present", () => {
+ const modifiedAuthor = Object.assign({}, author, socialData);
+ const authorTwitterLink = getAuthorTwitterUrl(modifiedAuthor);
+ expect(authorTwitterLink).toBe("https://www.twitter.com/sabqorg");
+ });
+
+ it("should return twitter handle link when url is not present", () => {
+ const socialData = {
+ social: {
+ twitter: {
+ handle: "elonmusk",
+ },
+ },
+ };
+
+ const modifiedAuthor = Object.assign({}, author, socialData);
+ const authorTwitterLink = getAuthorTwitterUrl(modifiedAuthor);
+ expect(authorTwitterLink).toBe("https://www.twitter.com/elonmusk");
+ });
+
+ it("should strip @ in twitter handle and construct link", () => {
+ const socialData = {
+ social: {
+ twitter: {
+ handle: "@elonmusk",
+ },
+ },
+ };
+ const modifiedAuthor = Object.assign({}, author, socialData);
+ const authorTwitterLink = getAuthorTwitterUrl(modifiedAuthor);
+ expect(authorTwitterLink).toBe("https://www.twitter.com/elonmusk");
+ });
+});
+
+describe("isEmpty", () => {
+ it("should return true if the value is null/ undefined", () => {
+ const value = null;
+ const result = isEmpty(value);
+ expect(result).toBe(true);
+ });
+
+ it("should return true if the value is an empty string", () => {
+ const value = "";
+ const result = isEmpty(value);
+ expect(result).toBe(true);
+ });
+
+ it("should return true if the value is an empty object", () => {
+ const value = {};
+ const result = isEmpty(value);
+ expect(result).toBe(true);
+ });
+
+ it("should return false if the value is not null/ undefined", () => {
+ const value = 6;
+ const result = isEmpty(value);
+ expect(result).toBe(false);
+ });
+
+ it("should return false if the value is not an empty string", () => {
+ const value = "value";
+ const result = isEmpty(value);
+ expect(result).toBe(false);
+ });
+
+ it("should return false if the value is not an empty object", () => {
+ const value = { data: "read" };
+ const result = isEmpty(value);
+ expect(result).toBe(false);
+ });
+
+ it("should return window if the page url is defined", () => {
+ const result = sharePageUrl;
+ expect(result).toBe(window.location.href);
+ });
+});
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/eleven-stories/index.js b/app/isomorphic/components/collection-templates/arrow-rows/eleven-stories/index.js
index f08d78658..a50d6f414 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/eleven-stories/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/eleven-stories/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { ElevenStories } from "@quintype/arrow";
+import ElevenStories from "../../../../arrow/components/Rows/ElevenStories";
import { object } from "prop-types";
export const ArrowElevenStories = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowElevenStories.propTypes = {
- collection: object
+ collection: object,
};
ArrowElevenStories.storyLimit = 11;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/four-col-12-stories/index.js b/app/isomorphic/components/collection-templates/arrow-rows/four-col-12-stories/index.js
index c1a3f71f3..d6441c6e4 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/four-col-12-stories/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/four-col-12-stories/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { FourColTwelveStories } from "@quintype/arrow";
+import FourColTwelveStories from "../../../../arrow/components/Rows/FourColTwelveStory";
import { object } from "prop-types";
export const ArrowFourColTwelveStories = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowFourColTwelveStories.propTypes = {
- collection: object
+ collection: object,
};
ArrowFourColTwelveStories.storyLimit = 12;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/four-col-grid/index.js b/app/isomorphic/components/collection-templates/arrow-rows/four-col-grid/index.js
index 94862090b..18d63145b 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/four-col-grid/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/four-col-grid/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { FourColGrid } from "@quintype/arrow";
+import FourColGrid from "../../../../arrow/components/Rows/FourColGrid";
import { object } from "prop-types";
export const ArrowFourColGrid = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowFourColGrid.propTypes = {
- collection: object
+ collection: object,
};
ArrowFourColGrid.storyLimit = 12;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/full-screen-slider/index.js b/app/isomorphic/components/collection-templates/arrow-rows/full-screen-slider/index.js
index e2a8f7d04..88c809533 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/full-screen-slider/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/full-screen-slider/index.js
@@ -1,18 +1,18 @@
import React from "react";
-import { FullScreenSlider } from "@quintype/arrow";
+import FullScreenSlider from "../../../../arrow/components/Rows/FullScreenSlider";
import { object } from "prop-types";
export const ArrowFullScreenSlider = ({ collection }) => {
const contextConfig = {
numberOfStoriesToShow: 5,
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowFullScreenSlider.propTypes = {
- collection: object
+ collection: object,
};
ArrowFullScreenSlider.storyLimit = 6;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/one-col-story-list/index.js b/app/isomorphic/components/collection-templates/arrow-rows/one-col-story-list/index.js
index 228a0905f..d1d35036b 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/one-col-story-list/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/one-col-story-list/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { OneColStoryList } from "@quintype/arrow";
+import OneColStoryList from "../../../../arrow/components/Rows/OneColStoryList";
import { object } from "prop-types";
export const ArrowOneColStoryList = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowOneColStoryList.propTypes = {
- collection: object
+ collection: object,
};
ArrowOneColStoryList.storyLimit = 12;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/three-col-grid/index.js b/app/isomorphic/components/collection-templates/arrow-rows/three-col-grid/index.js
index d35e2b9ea..851c656b5 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/three-col-grid/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/three-col-grid/index.js
@@ -1,19 +1,18 @@
import React from "react";
-import { ThreeColGrid } from "@quintype/arrow";
+import ThreeColGrid from "../../../../arrow/components/Rows/ThreeColGrid";
import { object } from "prop-types";
-
import "./style.m.css";
export const ArrowThreeColGrid = ({ collection }) => {
const config = {
buttonText: `${collection.name} News`,
- showImagePlaceholder: true
+ showImagePlaceholder: true,
};
return ;
};
ArrowThreeColGrid.propTypes = {
- collection: object
+ collection: object,
};
ArrowThreeColGrid.storyLimit = 6;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/three-col-seven-stories/index.js b/app/isomorphic/components/collection-templates/arrow-rows/three-col-seven-stories/index.js
index 43434845b..357f53a39 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/three-col-seven-stories/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/three-col-seven-stories/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { ThreeColSevenStory } from "@quintype/arrow";
+import ThreeColSevenStory from "../../../../arrow/components/Rows/ThreeColSevenStory";
import { object } from "prop-types";
export const ArrowThreeColSevenStories = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowThreeColSevenStories.propTypes = {
- collection: object
+ collection: object,
};
ArrowThreeColSevenStories.storyLimit = 7;
diff --git a/app/isomorphic/components/collection-templates/arrow-rows/two-col-four-stories/index.js b/app/isomorphic/components/collection-templates/arrow-rows/two-col-four-stories/index.js
index e4c5ec13a..760893d5f 100644
--- a/app/isomorphic/components/collection-templates/arrow-rows/two-col-four-stories/index.js
+++ b/app/isomorphic/components/collection-templates/arrow-rows/two-col-four-stories/index.js
@@ -1,16 +1,16 @@
import React from "react";
-import { TwoColFourStories } from "@quintype/arrow";
+import TwoColFourStories from "../../../../arrow/components/Rows/TwoColFourStory";
import { object } from "prop-types";
export const ArrowTwoColFourStories = ({ collection }) => {
const config = {
- buttonText: `${collection.name} News`
+ buttonText: `${collection.name} News`,
};
return ;
};
ArrowTwoColFourStories.propTypes = {
- collection: object
+ collection: object,
};
ArrowTwoColFourStories.storyLimit = 4;
diff --git a/app/isomorphic/components/pages/author-page/author.m.css b/app/isomorphic/components/pages/author-page/author.m.css
index fbdfbe690..427f7d166 100644
--- a/app/isomorphic/components/pages/author-page/author.m.css
+++ b/app/isomorphic/components/pages/author-page/author.m.css
@@ -1,5 +1,5 @@
:global {
- @import "../../../../../node_modules/@quintype/arrow/AuthorIntroductionCard/styles.arrow.css";
+ @import "../../../arrow/components/Rows/AuthorIntroductionCard/author-intro.m.css";
}
.wrapper {
:global .arr--author-intro-card {
diff --git a/app/isomorphic/components/pages/author-page/index.js b/app/isomorphic/components/pages/author-page/index.js
index abee9fa6f..fe75bc449 100644
--- a/app/isomorphic/components/pages/author-page/index.js
+++ b/app/isomorphic/components/pages/author-page/index.js
@@ -2,26 +2,27 @@ import React, { useState } from "react";
import { object, shape } from "prop-types";
import { useSelector } from "react-redux";
import get from "lodash/get";
-import { AuthorIntroductionCard, ThreeColGrid } from "@quintype/arrow";
+import AuthorIntroductionCard from "../../../arrow/components/Rows/AuthorIntroductionCard";
+import ThreeColGrid from "../../../arrow/components/Rows/ThreeColGrid";
import { getLoadMoreStories } from "../../utils";
import { DfpComponent } from "../../ads/dfp-component";
import "./author.m.css";
-export const AuthorPage = props => {
- const adConfig = useSelector(state => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
+export const AuthorPage = (props) => {
+ const adConfig = useSelector((state) => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
const authorCollection = get(props, ["data", "authorCollection"], {});
const [storiesToRender, setStoriesToRender] = useState(6);
const [authorPageStories, setStories] = useState(authorCollection.items);
const authorCollectionStories = {
- items: authorPageStories.slice(0, storiesToRender)
+ items: authorPageStories.slice(0, storiesToRender),
};
const authorIntrCardConfig = {
enableBio: true,
- enableSocialLinks: true
+ enableSocialLinks: true,
};
const getMoreStories = async (offset, limit) => {
@@ -33,7 +34,7 @@ export const AuthorPage = props => {
setStories: setStories,
storiesToRender: storiesToRender,
setStoriesToRender: setStoriesToRender,
- stories: authorPageStories
+ stories: authorPageStories,
});
};
@@ -64,6 +65,6 @@ export const AuthorPage = props => {
AuthorPage.propTypes = {
data: shape({
author: object,
- authorCollection: object
- })
+ authorCollection: object,
+ }),
};
diff --git a/app/isomorphic/components/pages/home.js b/app/isomorphic/components/pages/home.js
index 30629f99c..0ae48a992 100644
--- a/app/isomorphic/components/pages/home.js
+++ b/app/isomorphic/components/pages/home.js
@@ -4,7 +4,7 @@ import { LazyCollection, LazyLoadImages, replaceAllStoriesInCollection, WithPrev
import { getCollectionTemplate } from "../get-collection-template";
-export const HomePage = props => (
+export const HomePage = (props) => (
@@ -14,12 +14,12 @@ export const HomePage = props => (
HomePage.propTypes = {
data: shape({
- collection: object
- })
+ collection: object,
+ }),
};
export const HomePagePreview = WithPreview(HomePage, (data, story) =>
Object.assign({}, data, {
- collection: replaceAllStoriesInCollection(data.collection, story)
+ collection: replaceAllStoriesInCollection(data.collection, story),
})
);
diff --git a/app/isomorphic/components/pages/search.js b/app/isomorphic/components/pages/search.js
index 4a6922765..d4d85ecef 100644
--- a/app/isomorphic/components/pages/search.js
+++ b/app/isomorphic/components/pages/search.js
@@ -2,19 +2,19 @@ import React, { useState } from "react";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
import get from "lodash/get";
-import { OneColStoryList } from "@quintype/arrow";
+import OneColStoryList from "../../arrow/components/Rows/OneColStoryList";
import { DfpComponent } from "../ads/dfp-component";
import { getLoadMoreStories } from "../utils";
-const SearchPage = props => {
+const SearchPage = (props) => {
// will be getting initially 9 stories, but showing only 8 for loadmore functionality
- const adConfig = useSelector(state => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
+ const adConfig = useSelector((state) => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
const [storiesToRender, setStoriesToRender] = useState(8);
const [stories, setStories] = useState(props.data.stories);
const collection = {
- items: stories.slice(0, storiesToRender)
+ items: stories.slice(0, storiesToRender),
};
const getMoreStories = async (offset, limit) => {
@@ -26,7 +26,7 @@ const SearchPage = props => {
setStories: setStories,
storiesToRender: storiesToRender,
setStoriesToRender: setStoriesToRender,
- stories: stories
+ stories: stories,
});
};
@@ -53,7 +53,7 @@ const SearchPage = props => {
};
SearchPage.propTypes = {
- data: PropTypes.object
+ data: PropTypes.object,
};
export { SearchPage };
diff --git a/app/isomorphic/components/pages/section.js b/app/isomorphic/components/pages/section.js
index 9d95a5120..b53e54838 100644
--- a/app/isomorphic/components/pages/section.js
+++ b/app/isomorphic/components/pages/section.js
@@ -3,16 +3,16 @@ import { string, shape, object } from "prop-types";
import get from "lodash/get";
import { collectionToStories, LazyCollection } from "@quintype/components";
import { useSelector } from "react-redux";
-import { OneColStoryList } from "@quintype/arrow";
+import OneColStoryList from "../../arrow/components/Rows/OneColStoryList";
import { getCollectionTemplate } from "../get-collection-template";
import { DfpComponent } from "../ads/dfp-component";
import { getLoadMoreStories } from "../utils";
-const SectionPage = props => {
+const SectionPage = (props) => {
// will be getting initially 9 items, but showing only 8 for loadmore functionality
- const adConfig = useSelector(state => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
- const shouldUseCollection = useSelector(state =>
+ const adConfig = useSelector((state) => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
+ const shouldUseCollection = useSelector((state) =>
get(state, ["qt", "config", "publisher-attributes", "should_use_collection"])
);
const [storiesToRender, setStoriesToRender] = useState(8);
@@ -29,11 +29,11 @@ const SectionPage = props => {
}
const childCollections = (get(props, ["data", "collection", "items"]) || []).filter(
- item => item.type === "collection" && item.items.length > 0
+ (item) => item.type === "collection" && item.items.length > 0
);
const collection = {
- items: !childCollections.length ? sectionPageStories.slice(0, storiesToRender) : props.data.collection.items
+ items: !childCollections.length ? sectionPageStories.slice(0, storiesToRender) : props.data.collection.items,
};
const getMoreStories = async (offset, limit) => {
@@ -47,7 +47,7 @@ const SectionPage = props => {
storiesToRender: storiesToRender,
setStoriesToRender: setStoriesToRender,
stories: sectionPageStories,
- isSectionPage: true
+ isSectionPage: true,
});
};
@@ -81,8 +81,8 @@ SectionPage.propTypes = {
pageType: string,
data: shape({
collection: object,
- section: string
- })
+ section: string,
+ }),
};
export { SectionPage };
diff --git a/app/isomorphic/components/pages/story.js b/app/isomorphic/components/pages/story.js
index fc605d02d..a729b33a6 100644
--- a/app/isomorphic/components/pages/story.js
+++ b/app/isomorphic/components/pages/story.js
@@ -4,56 +4,76 @@ import React from "react";
import { InfiniteStoryBase, WithPreview } from "@quintype/components";
import { number, object, shape, any } from "prop-types";
-import { BlankStory } from "../story-templates/blank";
-
+import TextStory from "../story-templates/text-story";
+import ListicleStory from "../story-templates/listicle-story";
+import PhotoStory from "../story-templates/photo-story";
+import LiveBlogStory from "../story-templates/live-blog";
+import VideoStory from "../story-templates/video-story";
function StoryPageBase({ index, story, otherProp }) {
// Can switch to a different template based story-template, or only show a spoiler if index > 0
- return ;
+ const storyTemplate = story["story-template"];
+
+ switch (storyTemplate) {
+ case "text":
+ return ;
+ case "video":
+ return ;
+ case "photo":
+ return ;
+ case "listicle":
+ return ;
+ case "live-blog":
+ return ;
+ default:
+ return ;
+ }
}
StoryPageBase.propTypes = {
index: number,
story: object,
- otherProp: any
+ otherProp: any,
};
const FIELDS =
- "id,headline,slug,url,hero-image-s3-key,hero-image-metadata,first-published-at,last-published-at,alternative,published-at,author-name,author-id,sections,story-template,cards";
+ "id,headline,slug,url,hero-image-s3-key,hero-image-metadata,first-published-at,last-published-at,alternative,published-at,author-name,author-id,sections,authors,story-template,cards";
function storyPageLoadItems(pageNumber) {
return global
.wretch("/api/v1/stories")
.query({
fields: FIELDS,
limit: 5,
- offset: 5 * pageNumber
+ offset: 5 * pageNumber,
})
.get()
- .json(response => response.stories.map(story => ({ story, otherProp: "value" })));
+ .json((response) => response.stories.map((story) => ({ story, otherProp: "value" })));
}
export function StoryPage(props) {
return (
-
- app.registerPageView({ pageType: "story-page", data: { story: item.story } }, `/${item.story.slug}`)
- }
- onItemFocus={item => console.log(`Story In View: ${item.story.headline}`)}
- />
+
+
+ app.registerPageView({ pageType: "story-page", data: { story: item.story } }, `/${item.story.slug}`)
+ }
+ onItemFocus={(item) => console.log(`Story In View: ${item.story.headline}`)}
+ />
+
);
}
StoryPage.propTypes = {
data: shape({
- story: object
- })
+ story: object,
+ }),
};
export const StoryPagePreview = WithPreview(StoryPage, (data, story) =>
Object.assign({}, data, {
story,
- relatedStories: Array(5).fill(story)
+ relatedStories: Array(5).fill(story),
})
);
diff --git a/app/isomorphic/components/pages/tag.js b/app/isomorphic/components/pages/tag.js
index 582369b99..3722cb188 100644
--- a/app/isomorphic/components/pages/tag.js
+++ b/app/isomorphic/components/pages/tag.js
@@ -2,19 +2,19 @@ import get from "lodash/get";
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
-import { OneColStoryList } from "@quintype/arrow";
+import OneColStoryList from "../../arrow/components/Rows/OneColStoryList";
import { DfpComponent } from "../ads/dfp-component";
import { getLoadMoreStories } from "../utils";
-const TagPage = props => {
+const TagPage = (props) => {
// will be getting initially 9 stories, but showing only 8 for loadmore functionality
- const adConfig = useSelector(state => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
+ const adConfig = useSelector((state) => get(state, ["qt", "config", "ads-config", "slots", "listing_page_ads"], {}));
const [storiesToRender, setStoriesToRender] = useState(8);
const [tagPageStories, setStories] = useState(props.data.stories);
const collection = {
- items: tagPageStories.slice(0, storiesToRender)
+ items: tagPageStories.slice(0, storiesToRender),
};
const getMoreStories = async (offset, limit) => {
@@ -26,7 +26,7 @@ const TagPage = props => {
setStories: setStories,
storiesToRender: storiesToRender,
setStoriesToRender: setStoriesToRender,
- stories: tagPageStories
+ stories: tagPageStories,
});
};
@@ -52,5 +52,5 @@ const TagPage = props => {
export { TagPage };
TagPage.propTypes = {
- data: PropTypes.object
+ data: PropTypes.object,
};
diff --git a/app/isomorphic/components/story-grid.js b/app/isomorphic/components/story-grid.js
index 73734671c..31b94bda5 100644
--- a/app/isomorphic/components/story-grid.js
+++ b/app/isomorphic/components/story-grid.js
@@ -1,13 +1,13 @@
import React from "react";
import { useSelector } from "react-redux";
-import get from "lodash.get";
+import get from "lodash/get";
import { Link, ResponsiveImage } from "@quintype/components";
import { shape, string, object, integer, arrayOf } from "prop-types";
import "./story-grid.m.css";
function StoryGridStoryItem(props) {
- const showImagePlaceholder = useSelector(state => get(state, ["qt", "config", "showPlaceholder"]));
+ const showImagePlaceholder = useSelector((state) => get(state, ["qt", "config", "showPlaceholder"]));
const getPlaceholderStyleName = showImagePlaceholder ? "placeholder" : "";
return (
@@ -36,12 +36,12 @@ const storyPropType = shape({
"hero-image-s3-key": string,
"hero-image-metadata": object,
headline: string,
- "author-name": string
+ "author-name": string,
});
StoryGridStoryItem.propTypes = {
story: storyPropType,
- position: integer
+ position: integer,
};
export function StoryGrid({ stories = [] }) {
@@ -59,5 +59,5 @@ export function StoryGrid({ stories = [] }) {
}
StoryGrid.propTypes = {
- stories: arrayOf(storyPropType)
+ stories: arrayOf(storyPropType),
};
diff --git a/app/isomorphic/components/story-templates/index.js b/app/isomorphic/components/story-templates/index.js
new file mode 100644
index 000000000..fb401dfe0
--- /dev/null
+++ b/app/isomorphic/components/story-templates/index.js
@@ -0,0 +1,7 @@
+import TextStory from "./text-story";
+import LiveBlogStory from "./live-blog";
+import ListicleStory from "./listicle-story";
+import PhotoStory from "./photo-story";
+import VideoStory from "./video-story";
+
+export { TextStory, LiveBlogStory, ListicleStory, PhotoStory, VideoStory };
diff --git a/app/isomorphic/components/story-templates/listicle-story/index.js b/app/isomorphic/components/story-templates/listicle-story/index.js
new file mode 100644
index 000000000..961139dd8
--- /dev/null
+++ b/app/isomorphic/components/story-templates/listicle-story/index.js
@@ -0,0 +1,13 @@
+import React from "react";
+import ListicleStoryTemplate from "../../../arrow/components/Rows/StoryTemplates/ListicleStoryTemplates";
+import { object } from "prop-types";
+
+const ListicleStory = ({ story }) => {
+ return ;
+};
+
+ListicleStory.propTypes = {
+ story: object,
+};
+
+export default React.memo(ListicleStory);
diff --git a/app/isomorphic/components/story-templates/live-blog/index.js b/app/isomorphic/components/story-templates/live-blog/index.js
new file mode 100644
index 000000000..5c167d1ce
--- /dev/null
+++ b/app/isomorphic/components/story-templates/live-blog/index.js
@@ -0,0 +1,13 @@
+import React from "react";
+import LiveBlogStoryTemplate from "../../../arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates";
+import { object } from "prop-types";
+
+const LiveBlogStory = ({ story }) => {
+ return ;
+};
+
+LiveBlogStory.propTypes = {
+ story: object,
+};
+
+export default React.memo(LiveBlogStory);
diff --git a/app/isomorphic/components/story-templates/photo-story/index.js b/app/isomorphic/components/story-templates/photo-story/index.js
new file mode 100644
index 000000000..5ee90e8f0
--- /dev/null
+++ b/app/isomorphic/components/story-templates/photo-story/index.js
@@ -0,0 +1,13 @@
+import React from "react";
+import PhotoStoryTemplates from "../../../arrow/components/Rows/StoryTemplates/PhotoStoryTemplates";
+import { object } from "prop-types";
+
+const PhotoStory = ({ story }) => {
+ return ;
+};
+
+PhotoStory.propTypes = {
+ story: object,
+};
+
+export default React.memo(PhotoStory);
diff --git a/app/isomorphic/components/story-templates/text-story/index.js b/app/isomorphic/components/story-templates/text-story/index.js
new file mode 100644
index 000000000..f054dce11
--- /dev/null
+++ b/app/isomorphic/components/story-templates/text-story/index.js
@@ -0,0 +1,13 @@
+import React from "react";
+import TextStoryTemplate from "../../../arrow/components/Rows/StoryTemplates/TextStoryTemplates";
+import { object } from "prop-types";
+
+const TextStory = ({ story }) => {
+ return ;
+};
+
+TextStory.propTypes = {
+ story: object,
+};
+
+export default React.memo(TextStory);
diff --git a/app/isomorphic/components/story-templates/video-story/index.js b/app/isomorphic/components/story-templates/video-story/index.js
new file mode 100644
index 000000000..da69311e0
--- /dev/null
+++ b/app/isomorphic/components/story-templates/video-story/index.js
@@ -0,0 +1,13 @@
+import React from "react";
+import VideoStoryTemplate from "../../../arrow/components/Rows/StoryTemplates/VideoStoryTemplates";
+import { object } from "prop-types";
+
+const VideoStory = ({ story }) => {
+ return ;
+};
+
+VideoStory.propTypes = {
+ story: object,
+};
+
+export default React.memo(VideoStory);
diff --git a/app/server/handlers/render-layout.js b/app/server/handlers/render-layout.js
index a0404d606..984b3daa7 100644
--- a/app/server/handlers/render-layout.js
+++ b/app/server/handlers/render-layout.js
@@ -10,7 +10,7 @@ import fontFace from "../font";
import { BreakingNewsView } from "../../isomorphic/components/breaking-news-view";
import { TopAd } from "../../isomorphic/components/ads/top-ad";
import { getConfig, extractor, getCriticalCss, getArrowCss, getStyleTags } from "../helpers";
-import get from "lodash.get";
+import get from "lodash/get";
const cssContent = assetPath("app.css") ? readAsset("app.css") : "";
const fontJsContent = assetPath("font.js") ? readAsset("font.js") : "";
diff --git a/app/server/helpers/index.js b/app/server/helpers/index.js
index 4d0ab6e33..33bbfbed8 100644
--- a/app/server/helpers/index.js
+++ b/app/server/helpers/index.js
@@ -14,14 +14,39 @@ const statsFile = path.resolve("stats.json");
export async function getArrowCss(state, { qtAssetHelpers = require("@quintype/framework/server/asset-helper") } = {}) {
const layout = get(state, ["qt", "data", "collection", "items", 0, "associated-metadata", "layout"], null);
const pageType = get(state, ["qt", "pageType"], "");
+ const storyTemplate = get(state, ["qt", "data", "story", "story-template"], "");
+
+ const storyTemplateTypes = {
+ text: "TextStory",
+ photo: "PhotoStory",
+ video: "VideoStory",
+ "live-blog": "LiveBlogStory",
+ listicle: "ListicleStory",
+ };
+
const extractor = (entryPoint) => {
const getExtractor = new ChunkExtractor({ statsFile, entrypoints: [entryPoint] });
return getExtractor.getCssString();
};
+
switch (pageType) {
case "author-page":
return await extractor("authorPage");
}
+
+ switch (storyTemplateTypes[storyTemplate]) {
+ case "TextStory":
+ return getAsset("arrowTextStoryCssChunk.css", qtAssetHelpers);
+ case "ListicleStory":
+ return getAsset("arrowListicleStoryCssChunk.css", qtAssetHelpers);
+ case "LiveBlogStory":
+ return getAsset("arrowLiveBlogStoryCssChunk.css", qtAssetHelpers);
+ case "PhotoStory":
+ return getAsset("arrowPhotoStoryCssChunk.css", qtAssetHelpers);
+ case "VideoStory":
+ return getAsset("arrowVideoStoryCssChunk.css", qtAssetHelpers);
+ }
+
switch (layout) {
case "ArrowElevenStories":
return getAsset("arrowElevenStoriesCssChunk.css", qtAssetHelpers);
diff --git a/package-lock.json b/package-lock.json
index 92ef1efe5..903de0c05 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,20 +11,22 @@
"dependencies": {
"@loadable/component": "^5.14.1",
"@loadable/server": "^5.14.2",
- "@quintype/arrow": "^3.0.1",
"@quintype/bridgekeeper-js": "^2.8.1",
- "@quintype/components": "^3.0.0",
+ "@quintype/components": "^3.0.2",
"@quintype/framework": "^7.7.7",
"@quintype/seo": "^1.40.10",
"axios": "^0.24.0",
"fontfaceobserver": "^2.1.0",
+ "intersection-observer": "^0.12.2",
"lodash": "^4.17.21",
"query-string": "^7.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-formio": "^4.3.0",
+ "react-image-lightbox": "^5.1.4",
"react-modal": "^3.12.1",
"react-redux": "^7.2.5",
+ "react-timeago": "^4.4.0",
"redux": "^4.0.1",
"serialize-javascript": "^6.0.0",
"wretch": "^1.4.1"
@@ -52,8 +54,10 @@
"lint-staged": "^11.2.3",
"nodemon": "^2.0.20",
"postcss": "^8.3.11",
+ "postcss-custom-media": "^8.0.2",
"prettier": "2.4.1",
"react-test-renderer": "^17.0.2",
+ "sass": "^1.54.5",
"stylelint": "^14.0.0",
"stylelint-config-css-modules": "^2.2.0",
"stylelint-config-recommended-scss": "^5.0.0",
@@ -3401,6 +3405,7 @@
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -4953,40 +4958,6 @@
"react-dom": "^16.12.0"
}
},
- "node_modules/@quintype/arrow": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@quintype/arrow/-/arrow-3.0.1.tgz",
- "integrity": "sha512-2gnI5OKHvc6LySlYFcYDBHl6xhx2vYU/Mwdtp/KqbuNMj1vrZsfoVbS+Gg0CoVnTLl8oPIe6xxnEyEcKbBMlAw==",
- "dependencies": {
- "@quintype/components": "^3.0.0",
- "axios": "^0.19.2",
- "intersection-observer": "^0.11.0",
- "lodash.camelcase": "^4.3.0",
- "lodash.get": "^4.4.2",
- "lodash.kebabcase": "^4.1.1",
- "react-image-lightbox": "^5.1.1",
- "react-timeago": "^4.4.0"
- },
- "engines": {
- "node": ">=12.0.0",
- "npm": "^6.0.0"
- },
- "peerDependencies": {
- "lodash.get": "^4.4.2",
- "react": ">= 16.8.0",
- "react-dom": ">= 16.8.0",
- "react-redux": ">= 7.0.0"
- }
- },
- "node_modules/@quintype/arrow/node_modules/axios": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
- "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
- "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
- "dependencies": {
- "follow-redirects": "1.5.10"
- }
- },
"node_modules/@quintype/backend": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@quintype/backend/-/backend-2.3.1.tgz",
@@ -5272,9 +5243,9 @@
}
},
"node_modules/@quintype/components": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@quintype/components/-/components-3.0.0.tgz",
- "integrity": "sha512-Q01CakmbrKFSFH1xRwKDoV09JCuYs89DgM1BpK2RxOh8mvt2GF1Wz1xB87Ybel0FolVVBRdaaeCLsWStvMg6Kw==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@quintype/components/-/components-3.0.2.tgz",
+ "integrity": "sha512-QhfDexSof8oU3yenG9m4SB9qKuph/qKTtz7a2dDroPDKecul/Ig/jrwRNiOpZd/Mcicwyhb2LMigIoaJf0OmWw==",
"dependencies": {
"@babel/runtime": "^7.16.3",
"atob": "^2.1.2",
@@ -9387,7 +9358,7 @@
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
- "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true
},
"node_modules/core-js-compat": {
@@ -13397,6 +13368,7 @@
"version": "3.16.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.3.tgz",
"integrity": "sha512-lM3GftxzHNtPNUJg0v4pC2RC6puwMd6VZA7vXUczi+SKmCWSf4JwO89VJGMqbzmB7jlK7B5hr3S64PqwFL49cA==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -14756,6 +14728,12 @@
"url": "https://opencollective.com/immer"
}
},
+ "node_modules/immutable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+ "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+ "dev": true
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -15035,9 +15013,9 @@
}
},
"node_modules/intersection-observer": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.11.0.tgz",
- "integrity": "sha512-KZArj2QVnmdud9zTpKf279m2bbGfG+4/kn16UU0NL3pTVl52ZHiJ9IRNSsnn6jaHrL9EGLFM5eWjTx2fz/+zoQ=="
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz",
+ "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg=="
},
"node_modules/intl-messageformat": {
"version": "4.4.0",
@@ -19110,11 +19088,6 @@
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
- "node_modules/lodash.kebabcase": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
- "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY="
- },
"node_modules/lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
@@ -22392,41 +22365,22 @@
}
},
"node_modules/postcss-custom-media": {
- "version": "7.0.8",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
- "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz",
+ "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==",
"dev": true,
"dependencies": {
- "postcss": "^7.0.14"
+ "postcss-value-parser": "^4.2.0"
},
"engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/postcss-custom-media/node_modules/postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "dev": true,
- "dependencies": {
- "picocolors": "^0.2.1",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=6.0.0"
+ "node": "^12 || ^14 || >=16"
},
"funding": {
"type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- }
- },
- "node_modules/postcss-custom-media/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
}
},
"node_modules/postcss-custom-properties": {
@@ -24124,6 +24078,18 @@
"url": "https://opencollective.com/postcss/"
}
},
+ "node_modules/postcss-preset-env/node_modules/postcss-custom-media": {
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
+ "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
+ "dev": true,
+ "dependencies": {
+ "postcss": "^7.0.14"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/postcss-preset-env/node_modules/postcss-nesting": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz",
@@ -24504,9 +24470,9 @@
}
},
"node_modules/postcss-value-parser": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
- "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/postcss-values-parser": {
"version": "2.0.1",
@@ -25425,6 +25391,7 @@
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/raven/-/raven-2.6.4.tgz",
"integrity": "sha512-6PQdfC4+DQSFncowthLf+B6Hr0JpPsFBgTVYTAOq7tCmx/kR4SXbeawtPch20+3QfUcQDoJBLjWW1ybvZ4kXTw==",
+ "deprecated": "Please upgrade to @sentry/node. See the migration guide https://bit.ly/3ybOlo7",
"dev": true,
"dependencies": {
"cookie": "0.3.1",
@@ -25577,6 +25544,7 @@
"version": "3.16.4",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.4.tgz",
"integrity": "sha512-Tq4GVE6XCjE+hcyW6hPy0ofN3hwtLudz5ZRdrlCnsnD/xkm/PWQRudzYHiKgZKUcefV6Q57fhDHjZHJP5dpfSg==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -26414,6 +26382,23 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "node_modules/sass": {
+ "version": "1.54.5",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.5.tgz",
+ "integrity": "sha512-p7DTOzxkUPa/63FU0R3KApkRHwcVZYC0PLnLm5iyZACyp15qSi32x7zVUhRdABAATmkALqgGrjCJAcWvobmhHw==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/sass-graph": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
@@ -27521,6 +27506,7 @@
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
"dev": true
},
"node_modules/stack-trace": {
@@ -36378,31 +36364,6 @@
"styled-components": "^5.1.1"
}
},
- "@quintype/arrow": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@quintype/arrow/-/arrow-3.0.1.tgz",
- "integrity": "sha512-2gnI5OKHvc6LySlYFcYDBHl6xhx2vYU/Mwdtp/KqbuNMj1vrZsfoVbS+Gg0CoVnTLl8oPIe6xxnEyEcKbBMlAw==",
- "requires": {
- "@quintype/components": "^3.0.0",
- "axios": "^0.19.2",
- "intersection-observer": "^0.11.0",
- "lodash.camelcase": "^4.3.0",
- "lodash.get": "^4.4.2",
- "lodash.kebabcase": "^4.1.1",
- "react-image-lightbox": "^5.1.1",
- "react-timeago": "^4.4.0"
- },
- "dependencies": {
- "axios": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
- "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
- "requires": {
- "follow-redirects": "1.5.10"
- }
- }
- }
- },
"@quintype/backend": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@quintype/backend/-/backend-2.3.1.tgz",
@@ -36617,9 +36578,9 @@
}
},
"@quintype/components": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@quintype/components/-/components-3.0.0.tgz",
- "integrity": "sha512-Q01CakmbrKFSFH1xRwKDoV09JCuYs89DgM1BpK2RxOh8mvt2GF1Wz1xB87Ybel0FolVVBRdaaeCLsWStvMg6Kw==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@quintype/components/-/components-3.0.2.tgz",
+ "integrity": "sha512-QhfDexSof8oU3yenG9m4SB9qKuph/qKTtz7a2dDroPDKecul/Ig/jrwRNiOpZd/Mcicwyhb2LMigIoaJf0OmWw==",
"requires": {
"@babel/runtime": "^7.16.3",
"atob": "^2.1.2",
@@ -44184,6 +44145,12 @@
"integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==",
"dev": true
},
+ "immutable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+ "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+ "dev": true
+ },
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -44404,9 +44371,9 @@
"dev": true
},
"intersection-observer": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.11.0.tgz",
- "integrity": "sha512-KZArj2QVnmdud9zTpKf279m2bbGfG+4/kn16UU0NL3pTVl52ZHiJ9IRNSsnn6jaHrL9EGLFM5eWjTx2fz/+zoQ=="
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz",
+ "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg=="
},
"intl-messageformat": {
"version": "4.4.0",
@@ -47454,11 +47421,6 @@
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
- "lodash.kebabcase": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
- "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY="
- },
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
@@ -50029,30 +49991,12 @@
}
},
"postcss-custom-media": {
- "version": "7.0.8",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
- "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz",
+ "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==",
"dev": true,
"requires": {
- "postcss": "^7.0.14"
- },
- "dependencies": {
- "postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "dev": true,
- "requires": {
- "picocolors": "^0.2.1",
- "source-map": "^0.6.1"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "postcss-value-parser": "^4.2.0"
}
},
"postcss-custom-properties": {
@@ -51290,6 +51234,15 @@
"source-map": "^0.6.1"
}
},
+ "postcss-custom-media": {
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
+ "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.14"
+ }
+ },
"postcss-nesting": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz",
@@ -51566,9 +51519,9 @@
}
},
"postcss-value-parser": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
- "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"postcss-values-parser": {
"version": "2.0.1",
@@ -53104,6 +53057,17 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "sass": {
+ "version": "1.54.5",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.5.tgz",
+ "integrity": "sha512-p7DTOzxkUPa/63FU0R3KApkRHwcVZYC0PLnLm5iyZACyp15qSi32x7zVUhRdABAATmkALqgGrjCJAcWvobmhHw==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
"sass-graph": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
diff --git a/package.json b/package.json
index 4fbc8eba1..eb92acad7 100644
--- a/package.json
+++ b/package.json
@@ -13,20 +13,22 @@
"dependencies": {
"@loadable/component": "^5.14.1",
"@loadable/server": "^5.14.2",
- "@quintype/arrow": "^3.0.1",
"@quintype/bridgekeeper-js": "^2.8.1",
- "@quintype/components": "^3.0.0",
+ "@quintype/components": "^3.0.2",
"@quintype/framework": "^7.7.7",
"@quintype/seo": "^1.40.10",
"axios": "^0.24.0",
"fontfaceobserver": "^2.1.0",
+ "intersection-observer": "^0.12.2",
"lodash": "^4.17.21",
"query-string": "^7.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-formio": "^4.3.0",
+ "react-image-lightbox": "^5.1.4",
"react-modal": "^3.12.1",
"react-redux": "^7.2.5",
+ "react-timeago": "^4.4.0",
"redux": "^4.0.1",
"serialize-javascript": "^6.0.0",
"wretch": "^1.4.1"
@@ -54,8 +56,10 @@
"lint-staged": "^11.2.3",
"nodemon": "^2.0.20",
"postcss": "^8.3.11",
+ "postcss-custom-media": "^8.0.2",
"prettier": "2.4.1",
"react-test-renderer": "^17.0.2",
+ "sass": "^1.54.5",
"stylelint": "^14.0.0",
"stylelint-config-css-modules": "^2.2.0",
"stylelint-config-recommended-scss": "^5.0.0",
diff --git a/quintype-build.config.js b/quintype-build.config.js
index f4ece6bbf..4dd1d31a5 100644
--- a/quintype-build.config.js
+++ b/quintype-build.config.js
@@ -6,17 +6,22 @@ const loadableConfigObj = {
topbar: "./app/isomorphic/components/layouts/header",
navbar: "./app/isomorphic/components/layouts/header/nav-bar",
footer: "./app/isomorphic/components/layouts/footer",
- arrowElevenStoriesCssChunk: "@quintype/arrow/ElevenStories/styles.arrow.css",
- arrowFourColGridCssChunk: "@quintype/arrow/FourColGrid/styles.arrow.css",
- arrowFourColTwelveStoriesCssChunk: "@quintype/arrow/FourColTwelveStories/styles.arrow.css",
- arrowFullScreenSliderCssChunk: "@quintype/arrow/FullScreenSlider/styles.arrow.css",
- arrowOneColStoryListCssChunk: "@quintype/arrow/OneColStoryList/styles.arrow.css",
- arrowThreeColGridCssChunk: "@quintype/arrow/ThreeColGrid/styles.arrow.css",
- arrowThreeColSevenStoryCssChunk: "@quintype/arrow/ThreeColSevenStory/styles.arrow.css",
- arrowTwoColFourStoriesCssChunk: "@quintype/arrow/TwoColFourStories/styles.arrow.css",
- authorPage: "./app/isomorphic/components/pages/author-page/index.js"
- }
- }
+ arrowElevenStoriesCssChunk: "./app/isomorphic/arrow/components/Rows/ElevenStories",
+ arrowFourColGridCssChunk: "./app/isomorphic/arrow/components/Rows/FourColGrid",
+ arrowFourColTwelveStoriesCssChunk: "./app/isomorphic/arrow/components/Rows/FourColTwelveStory",
+ arrowFullScreenSliderCssChunk: "./app/isomorphic/arrow/components/Rows/FullScreenSlider",
+ arrowOneColStoryListCssChunk: "./app/isomorphic/arrow/components/Rows/OneColStoryList",
+ arrowThreeColGridCssChunk: "./app/isomorphic/arrow/components/Rows/ThreeColGrid",
+ arrowThreeColSevenStoryCssChunk: "./app/isomorphic/arrow/components/Rows/ThreeColSevenStory",
+ arrowTwoColFourStoriesCssChunk: "./app/isomorphic/arrow/components/Rows/TwoColFourStory",
+ authorPage: "./app/isomorphic/components/pages/author-page/index.js",
+ arrowTextStoryCssChunk: "./app/isomorphic/arrow/components/Rows/StoryTemplates/TextStoryTemplates",
+ arrowVideoStoryCssChunk: "./app/isomorphic/arrow/components/Rows/StoryTemplates/VideoStoryTemplates",
+ arrowPhotoStoryCssChunk: "./app/isomorphic/arrow/components/Rows/StoryTemplates/PhotoStoryTemplates",
+ arrowListicleStoryCssChunk: "./app/isomorphic/arrow/components/Rows/StoryTemplates/ListicleStoryTemplates",
+ arrowLiveBlogStoryCssChunk: "./app/isomorphic/arrow/components/Rows/StoryTemplates/LiveBlogStoryTemplates",
+ },
+ },
};
const modifiedBuildConfig = { ...quintypeBuildConfig, ...loadableConfigObj };
diff --git a/views/pages/layout.ejs b/views/pages/layout.ejs
index 9bca3d1ec..a7cd92d9f 100644
--- a/views/pages/layout.ejs
+++ b/views/pages/layout.ejs
@@ -68,6 +68,11 @@
<%_ if(isOnesignalEnable) { _%>
<%- oneSignalScript -%>
<%_ } _%>
+
+
+
<%_ if(!isProduction) { _%>