From 48ef16cc66e861fc336144cbcc5562289de2081a Mon Sep 17 00:00:00 2001 From: Padmaja <52911293+padms@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:19:29 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Iframe=20carousel=20(#1455)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- studio/schemas/documents/page.ts | 1 + .../editors/titleEditorContentType.tsx | 1 - studio/schemas/objects/iframe.tsx | 113 ++---------------- .../objects/iframe/sharedIframeFields.tsx | 108 +++++++++++++++++ studio/schemas/objects/iframeCarousel.tsx | 92 ++++++++++++++ studio/schemas/schema.js | 3 +- web/lib/queries/common/pageContentFields.ts | 4 + web/lib/queries/iframeCarouselFields.ts | 9 ++ .../shared/SharedPageContent.tsx | 4 + .../HorizontalScroll/HorizontalScroll.tsx | 29 ++++- .../shared/IframeCarousel/IframeCarousel.tsx | 97 +++++++++++++++ web/pageComponents/shared/iframe/IFrame.tsx | 2 +- web/types/types.ts | 24 ++++ 13 files changed, 379 insertions(+), 108 deletions(-) create mode 100644 studio/schemas/objects/iframe/sharedIframeFields.tsx create mode 100644 studio/schemas/objects/iframeCarousel.tsx create mode 100644 web/lib/queries/iframeCarouselFields.ts create mode 100644 web/pageComponents/shared/IframeCarousel/IframeCarousel.tsx diff --git a/studio/schemas/documents/page.ts b/studio/schemas/documents/page.ts index c7dd9df4d..db441976f 100644 --- a/studio/schemas/documents/page.ts +++ b/studio/schemas/documents/page.ts @@ -66,6 +66,7 @@ export default { { type: 'anchorLink' }, Flags.HAS_MUX && { type: 'video' }, Flags.IS_DEV && { type: 'imageCarousel' }, + Flags.IS_DEV && { type: 'iframeCarousel' }, ].filter((e) => e), }, ].filter((e) => e), diff --git a/studio/schemas/editors/titleEditorContentType.tsx b/studio/schemas/editors/titleEditorContentType.tsx index 9e9bf1458..0c754b9bf 100644 --- a/studio/schemas/editors/titleEditorContentType.tsx +++ b/studio/schemas/editors/titleEditorContentType.tsx @@ -6,7 +6,6 @@ import type { BlockFieldType } from '../../types/schemaTypes' // TODO: Add relevant styles for titles (i.e. highlighted text) export const configureTitleBlockContent = (): BlockFieldType => { - // const { strong, emphasis, strikethrough } = options return { type: 'block', styles: [], diff --git a/studio/schemas/objects/iframe.tsx b/studio/schemas/objects/iframe.tsx index a47296e5a..74ab5913d 100644 --- a/studio/schemas/objects/iframe.tsx +++ b/studio/schemas/objects/iframe.tsx @@ -5,25 +5,12 @@ import React from 'react' import { code } from '@equinor/eds-icons' import { EdsIcon } from '../../icons' import { Colors } from '../../helpers/ColorListValues' -import { configureTitleBlockContent, configureBlockContent } from '../editors' -import CompactBlockEditor from '../components/CompactBlockEditor' +import { configureBlockContent } from '../editors' import CharCounterEditor from '../components/CharCounterEditor' import blocksToText from '../../helpers/blocksToText' -import type { Rule, ValidationContext, Block } from '@sanity/types' +import type { Rule, Block } from '@sanity/types' import type { ColorListValue } from 'sanity-plugin-color-list' - -const titleContentType = configureTitleBlockContent() - -const descriptionContentType = configureBlockContent({ - h1: false, - h2: false, - h3: false, - h4: false, - attachment: false, - internalLink: false, - externalLink: false, - lists: false, -}) +import { title, frameTitle, description, cookiePolicy, aspectRatio, url, height } from './iframe/sharedIframeFields' const ingressContentType = configureBlockContent({ h1: false, @@ -68,14 +55,7 @@ export default { }, ], fields: [ - { - name: 'title', - type: 'array', - title: 'Title', - description: 'The (optional) title/heading shown above the iframe.', - inputComponent: CompactBlockEditor, - of: [titleContentType], - }, + title, { name: 'ingress', title: 'Ingress', @@ -83,84 +63,12 @@ export default { inputComponent: CharCounterEditor, of: [ingressContentType], }, - { - name: 'frameTitle', - type: 'string', - title: 'Frame title', - fieldset: 'iframe', - - description: 'The title of the iframe. This value is not visible on the page but is required for accessibility.', - validation: (Rule: Rule) => - Rule.custom((value: string, context: ValidationContext) => { - const { parent } = context as { parent: IFrame } - return parent?.url && value === undefined ? 'Required' : true - }), - }, - { - name: 'url', - type: 'url', - title: 'Frame URL', - description: - 'Link to the content to be loaded inside the iframe. Any URL must be whitelisted to load. Please make sure to verify that the iframe loads correctly before publishing, otherwise contact dev team for whitelisting.', - fieldset: 'iframe', - validation: (Rule: Rule) => - Rule.custom((value: any, context: ValidationContext) => { - const { parent } = context as { parent: IFrame } - return (parent?.title || parent?.frameTitle) && value === undefined ? 'Required' : true - }), - }, - { - name: 'cookiePolicy', - type: 'string', - title: 'Cookie policy', - description: 'Select which cookie policy applies to this iframe.', - fieldset: 'iframe', - - options: { - list: [ - { title: 'None', value: 'none' }, - { title: 'Marketing', value: 'marketing' }, - { title: 'Statistics', value: 'statistics' }, - ], - layout: 'dropdown', - }, - initialValue: 'none', - validation: (Rule: Rule) => Rule.required(), - }, - - { - name: 'aspectRatio', - type: 'string', - title: 'Aspect ratio', - options: { - list: [ - { title: '16:9', value: '16:9' }, - { title: '4:3', value: '4:3' }, - { title: '1:1', value: '1:1' }, - ], - layout: 'dropdown', - }, - fieldset: 'iframe', - initialValue: '16:9', - validation: (Rule: Rule) => Rule.required(), - }, - { - name: 'height', - type: 'number', - title: 'Height', - description: 'Set a fixed height in pixels for the iframe. Note: this will override the aspect ratio setting.', - fieldset: 'iframe', - validation: (Rule: Rule) => Rule.positive().greaterThan(0).precision(0), - }, - - { - name: 'description', - title: 'Description/caption', - description: `Here you can write a short description of the iframes content. This text will show up as a caption text right below the iframe.`, - type: 'array', - inputComponent: CharCounterEditor, - of: [descriptionContentType], - }, + frameTitle, + url, + cookiePolicy, + aspectRatio, + height, + description, { name: 'action', title: 'Link/action', @@ -169,7 +77,6 @@ export default { of: [{ type: 'linkSelector', title: 'Link' }], validation: (Rule: Rule) => Rule.max(1), }, - { title: 'Background', description: 'Pick a colour for the background. Default is white.', diff --git a/studio/schemas/objects/iframe/sharedIframeFields.tsx b/studio/schemas/objects/iframe/sharedIframeFields.tsx new file mode 100644 index 000000000..66951173c --- /dev/null +++ b/studio/schemas/objects/iframe/sharedIframeFields.tsx @@ -0,0 +1,108 @@ +import { configureTitleBlockContent, configureBlockContent } from '../../editors' +import CompactBlockEditor from '../../components/CompactBlockEditor' +import CharCounterEditor from '../../components/CharCounterEditor' +import type { Rule, ValidationContext, Block } from '@sanity/types' +import type { IFrame } from '../iframe' + +const titleContentType = configureTitleBlockContent() + +const descriptionContentType = configureBlockContent({ + h1: false, + h2: false, + h3: false, + h4: false, + attachment: false, + internalLink: false, + externalLink: false, + lists: false, +}) + +export const title = { + name: 'title', + type: 'array', + title: 'Title', + description: 'The (optional) title/heading shown above the iframe.', + inputComponent: CompactBlockEditor, + of: [titleContentType], +} + +export const frameTitle = { + name: 'frameTitle', + type: 'string', + title: 'Frame title', + fieldset: 'iframe', + + description: 'The title of the iframe. This value is not visible on the page but is required for accessibility.', + validation: (Rule: Rule) => + Rule.custom((value: string, context: ValidationContext) => { + const { parent } = context as { parent: IFrame } + return parent?.url && value === undefined ? 'Required' : true + }), +} + +export const url = { + name: 'url', + type: 'url', + title: 'Frame URL', + description: + 'Link to the content to be loaded inside the iframe. Any URL must be whitelisted to load. Please make sure to verify that the iframe loads correctly before publishing, otherwise contact dev team for whitelisting.', + fieldset: 'iframe', + validation: (Rule: Rule) => + Rule.custom((value: any, context: ValidationContext) => { + const { parent } = context as { parent: IFrame } + return (parent?.title || parent?.frameTitle) && value === undefined ? 'Required' : true + }), +} + +export const cookiePolicy = { + name: 'cookiePolicy', + type: 'string', + title: 'Cookie policy', + description: 'Select which cookie policy applies to this iframe.', + fieldset: 'iframe', + + options: { + list: [ + { title: 'None', value: 'none' }, + { title: 'Marketing', value: 'marketing' }, + { title: 'Statistics', value: 'statistics' }, + ], + layout: 'dropdown', + }, + initialValue: 'none', + validation: (Rule: Rule) => Rule.required(), +} + +export const aspectRatio = { + name: 'aspectRatio', + type: 'string', + title: 'Aspect ratio', + options: { + list: [ + { title: '16:9', value: '16:9' }, + { title: '4:3', value: '4:3' }, + { title: '1:1', value: '1:1' }, + ], + layout: 'dropdown', + }, + fieldset: 'iframe', + initialValue: '16:9', + validation: (Rule: Rule) => Rule.required(), +} +export const height = { + name: 'height', + type: 'number', + title: 'Height', + description: 'Set a fixed height in pixels for the iframe. Note: this will override the aspect ratio setting.', + fieldset: 'iframe', + validation: (Rule: Rule) => Rule.positive().greaterThan(0).precision(0), +} + +export const description = { + name: 'description', + title: 'Description/caption', + description: `Here you can write a short description of the iframes content. This text will show up as a caption text right below the iframe.`, + type: 'array', + inputComponent: CharCounterEditor, + of: [descriptionContentType], +} diff --git a/studio/schemas/objects/iframeCarousel.tsx b/studio/schemas/objects/iframeCarousel.tsx new file mode 100644 index 000000000..f8ebceac4 --- /dev/null +++ b/studio/schemas/objects/iframeCarousel.tsx @@ -0,0 +1,92 @@ +import { Colors } from '../../helpers/ColorListValues' +import { EdsIcon } from '../../icons' +import { library_image } from '@equinor/eds-icons' +import blocksToText from '../../helpers/blocksToText' +import type { Rule } from '@sanity/types' +import { title, frameTitle, description, cookiePolicy, aspectRatio, url, height } from './iframe/sharedIframeFields' + +const carouselItemFields = [title, frameTitle, description, cookiePolicy, aspectRatio, url, height] +export default { + name: 'iframeCarousel', + title: 'Iframe carousel', + type: 'object', + fieldsets: [ + { + title: 'Design options', + name: 'design', + description: 'Some options for design', + options: { + collapsible: true, + collapsed: false, + }, + }, + ], + fields: [ + title, + { + type: 'array', + name: 'items', + description: 'Add iframes for the carousel', + title: 'Carousel items', + of: [ + { + title: 'Iframe carousel item', + type: 'object', + fieldsets: [ + { + title: 'IFrame settings', + name: 'iframe', + options: { + collapsible: true, + collapsed: false, + }, + }, + { + title: 'Design options', + name: 'design', + description: 'Some options for design', + options: { + collapsible: true, + collapsed: false, + }, + }, + ], + fields: [...carouselItemFields], + }, + ], + validation: (Rule: Rule) => Rule.required().min(2), + }, + { + title: 'Background', + description: 'Pick a colour for the background. Default is white.', + name: 'background', + type: 'colorlist', + options: { + borderradius: { + outer: '100%', + inner: '100%', + }, + tooltip: false, + list: Colors, + }, + fieldset: 'design', + initialValue: Colors[0], + }, + ], + preview: { + select: { + title: 'title', + items: 'items', + }, + prepare(selection: any) { + const { title, items } = selection + const length = items ? items.length : 0 + + return { + title: title ? blocksToText(title) : 'Untitled iframe carousel', + subtitle: `Iframe carousel with ${length} items`, + media: EdsIcon(library_image), + } + }, + }, +} diff --git a/studio/schemas/schema.js b/studio/schemas/schema.js index d5357a747..bd41b55ab 100644 --- a/studio/schemas/schema.js +++ b/studio/schemas/schema.js @@ -84,6 +84,7 @@ import twitterEmbed from './objects/twitterEmbed' import video from './objects/video' import videoFile from './objects/videoFile' import imageCarousel from './objects/imageCarousel' +import iframeCarousel from './objects/iframeCarousel' const routeSchemas = languages.map(({ name, title }) => { return route(name, title) @@ -172,6 +173,6 @@ export default createSchema({ ...NewsSchemas, ...NewsRoomSchema, ...RemainingSchemas, - ...(Flags.IS_DEV ? [imageCarousel] : []), + ...(Flags.IS_DEV ? [imageCarousel,iframeCarousel] : []), ]), }) diff --git a/web/lib/queries/common/pageContentFields.ts b/web/lib/queries/common/pageContentFields.ts index c8d1bf983..4c1923343 100644 --- a/web/lib/queries/common/pageContentFields.ts +++ b/web/lib/queries/common/pageContentFields.ts @@ -1,3 +1,4 @@ +import { iframeCarouselFields } from '../iframeCarouselFields' import downloadableFileFields from './actions/downloadableFileFields' import downloadableImageFields from './actions/downloadableImageFields' import linkSelectorFields, { linkReferenceFields } from './actions/linkSelectorFields' @@ -504,6 +505,9 @@ const pageContentFields = /* groq */ ` _type == "imageCarousel" =>{ ${imageCarouselFields} + }, + _type == "iframeCarousel" =>{ + ${iframeCarouselFields} } ` diff --git a/web/lib/queries/iframeCarouselFields.ts b/web/lib/queries/iframeCarouselFields.ts new file mode 100644 index 000000000..0d45108e8 --- /dev/null +++ b/web/lib/queries/iframeCarouselFields.ts @@ -0,0 +1,9 @@ +export const iframeCarouselFields = /* groq */ ` + "id": _key, + "type": _type, + title, + items, + "designOptions": { + "background": coalesce(background.title, 'none'), + }, +` diff --git a/web/pageComponents/pageTemplates/shared/SharedPageContent.tsx b/web/pageComponents/pageTemplates/shared/SharedPageContent.tsx index 556b2aca0..90fb77eb1 100644 --- a/web/pageComponents/pageTemplates/shared/SharedPageContent.tsx +++ b/web/pageComponents/pageTemplates/shared/SharedPageContent.tsx @@ -16,6 +16,7 @@ import CookieDeclaration from '../../topicPages/CookieDeclaration' import TwitterEmbed from '../../topicPages/TwitterEmbed' import Video from '../../topicPages/Video' import ImageCarousel from '../../shared/ImageCarousel/ImageCarousel' +import IframeCarousel from '../../shared/IframeCarousel/IframeCarousel' import { AnchorLinkData, TopicPageSchema, @@ -39,6 +40,7 @@ import { VideoData, CookieDeclarationData, ImageCarouselData, + IframeCarouselData, } from '../../../types/types' // How could we do this for several different component types? @@ -108,6 +110,8 @@ export const PageContent = ({ data }: PageContentProps) => { return