feat(SW-3173): Added support for one or two columns for the list inside the RTE

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-09-01 07:52:32 +00:00
parent c7f00aca4d
commit ec66a5647a
5 changed files with 64 additions and 111 deletions

View File

@@ -60,11 +60,8 @@
.ul,
.ol {
display: grid;
gap: var(--Space-x1);
padding: 0;
margin-top: var(--Space-x2);
margin-bottom: var(--Space-x2);
margin: var(--Space-x2) 0;
}
.ol > li::marker {
@@ -73,16 +70,7 @@
.li {
margin-left: var(--Space-x3);
}
.heart > .li::before,
.li:has(.heart)::before {
content: url('/_static/icons/heart.svg');
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Space-x1);
margin-left: calc(var(--Space-x3) * -1);
margin-bottom: var(--Space-x05);
}
.heart > .li,
@@ -90,16 +78,27 @@
.li:has(.check),
.li:has(.heart) {
list-style: none;
margin-left: 0;
}
.heart > .li::before,
.li:has(.heart)::before,
.check > .li::before,
.li:has(.check)::before {
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Space-x1);
}
.check > .li::before,
.li:has(.check)::before {
content: url('/_static/icons/check-ring.svg');
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Space-x1);
margin-left: calc(var(--Space-x3) * -1);
}
.heart > .li::before,
.li:has(.heart)::before {
content: url('/_static/icons/heart.svg');
}
.li > p {
@@ -127,17 +126,24 @@
}
@media screen and (min-width: 768px) {
.ol:has(li:nth-last-child(n + 5)),
.ul:has(li:nth-last-child(n + 5)) {
grid-template-columns: 1fr 1fr;
grid-auto-flow: column;
.ol,
.ul {
&.two-column,
&.two-columns,
&:has(.two-column, .two-columns) {
column-count: 2;
column-gap: var(--Space-x3);
}
}
}
@container sidebar (max-width: 360px) {
.ol,
.ul {
display: flex;
flex-direction: column;
&.two-column,
&.two-columns,
&:has(.two-column, .two-columns) {
column-count: 1;
}
}
}

View File

@@ -9,13 +9,13 @@ import Table from '../Table'
import { Typography } from '../Typography'
import {
extractAvailableListClassNames,
hasAvailableParagraphFormat,
hasAvailableULFormat,
makeCssModuleCompatibleClassName,
} from './utils'
import styles from './jsontohtml.module.css'
import { insertResponseToImageVaultAsset } from './insertResponseToImageVaultAsset'
import type { EmbedByUid } from './JsonToHtml'
import type { Attributes, RTEImageVaultAttrs } from './types/rte/attrs'
import {
@@ -33,7 +33,6 @@ import {
type RTETextNode,
} from './types/rte/node'
import type { RenderOptions } from './types/rte/option'
import { insertResponseToImageVaultAsset } from './insertResponseToImageVaultAsset'
function noNestedLinksOrReferences(node: RTENode) {
if ('type' in node) {
@@ -255,15 +254,12 @@ export const renderOptions: RenderOptions = {
fullRenderOptions: RenderOptions
) => {
const { className, ...props } = extractPossibleAttributes(node.attrs)
const compatibleClassName = makeCssModuleCompatibleClassName(
className,
'ul'
)
const compatibleClassNames = extractAvailableListClassNames(className)
return (
<li
key={node.uid}
{...props}
className={cx(styles.li, compatibleClassName)}
className={cx(styles.li, compatibleClassNames)}
>
{next(node.children, embeds, fullRenderOptions)}
</li>
@@ -277,26 +273,11 @@ export const renderOptions: RenderOptions = {
fullRenderOptions: RenderOptions
) => {
const { className, ...props } = extractPossibleAttributes(node.attrs)
// Set the number of rows dynamically to create even rows for each column. We want the li:s
// to flow with the column, so therefore this is needed.
let numberOfRows: number | undefined
if (node.children.length > 4) {
const half = node.children.length / 2
numberOfRows = Math.ceil(half)
}
const compatibleClassNames = extractAvailableListClassNames(className)
return (
<Typography key={node.uid} variant="Body/Paragraph/mdRegular">
<ol
className={cx(styles.ol, className)}
{...props}
style={
numberOfRows
? { gridTemplateRows: `repeat(${numberOfRows}, auto)` }
: {}
}
>
<ol className={cx(styles.ol, compatibleClassNames)} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</ol>
</Typography>
@@ -340,8 +321,9 @@ export const renderOptions: RenderOptions = {
let propsClassName = className
if (className) {
if (hasAvailableULFormat(className)) {
propsClassName = styles[className]
const availableClassNames = extractAvailableListClassNames(className)
if (availableClassNames.length) {
propsClassName = cx(availableClassNames)
}
}
@@ -361,8 +343,9 @@ export const renderOptions: RenderOptions = {
let propsClassName = className
if (className) {
if (hasAvailableULFormat(className)) {
propsClassName = styles[className]
const availableClassNames = extractAvailableListClassNames(className)
if (availableClassNames.length) {
propsClassName = cx(availableClassNames)
}
}
@@ -656,32 +639,11 @@ export const renderOptions: RenderOptions = {
fullRenderOptions: RenderOptions
) => {
const { className, ...props } = extractPossibleAttributes(node.attrs)
const compatibleClassName = makeCssModuleCompatibleClassName(
className,
'ul'
)
// Set the number of rows dynamically to create even rows for each column. We want the li:s
// to flow with the column, so therefore this is needed.
let numberOfRows: number | undefined
if (node.children.length > 4) {
const half = node.children.length / 2
numberOfRows = Math.ceil(half)
}
const compatibleClassNames = extractAvailableListClassNames(className)
return (
<Typography key={node.uid} variant="Body/Paragraph/mdRegular">
<ul
className={cx(styles.ul, compatibleClassName)}
{...props}
style={
numberOfRows
? {
gridTemplateRows: `repeat(${numberOfRows}, auto)`,
}
: {}
}
>
<ul className={cx(styles.ul, compatibleClassNames)} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</ul>
</Typography>
@@ -747,8 +709,9 @@ export const renderOptions: RenderOptions = {
}
if (className) {
if (hasAvailableULFormat(className)) {
propsClassName = styles[className]
const availableClassNames = extractAvailableListClassNames(className)
if (availableClassNames.length) {
propsClassName = cx(availableClassNames)
}
}

View File

@@ -0,0 +1,6 @@
export const AVAILABLE_LIST_FORMATS = [
'heart',
'check',
'two-column',
'two-columns',
]

View File

@@ -63,11 +63,6 @@ export enum AvailableParagraphFormatEnum {
'subtitle-2' = 'subtitle-2',
}
export enum AvailableULFormatEnum {
'heart' = 'heart',
'check' = 'check',
}
export type ContentBlockType =
| 'AccountPage'
| 'CampaignOverviewPage'

View File

@@ -4,13 +4,11 @@ import { renderOptions } from './renderOptions'
import styles from './jsontohtml.module.css'
import type { Node, Embeds } from './JsonToHtml'
import type { Embeds, Node } from './JsonToHtml'
import {
AvailableParagraphFormatEnum,
AvailableULFormatEnum,
RTETypeEnum,
} from './types/rte/enums'
import { EmbedByUid } from './JsonToHtml'
import { AVAILABLE_LIST_FORMATS } from './types/rte/constants'
import { AvailableParagraphFormatEnum, RTETypeEnum } from './types/rte/enums'
import {
RTEMarkType,
type RTENode,
@@ -19,7 +17,6 @@ import {
type RTETextNode,
} from './types/rte/node'
import type { RenderOptions } from './types/rte/option'
import { EmbedByUid } from './JsonToHtml'
export function groupEmbedsByUid(embedsArray: Node<Embeds>[]) {
const embedsByUid = embedsArray.reduce<EmbedByUid>((acc, embed) => {
@@ -109,11 +106,14 @@ export function hasAvailableParagraphFormat(className?: string) {
return Object.keys(AvailableParagraphFormatEnum).includes(className)
}
export function hasAvailableULFormat(className?: string) {
export function extractAvailableListClassNames(className?: string) {
if (!className) {
return false
return []
}
return Object.keys(AvailableULFormatEnum).includes(className)
const classNames = className.split(' ')
return classNames
.filter((name) => AVAILABLE_LIST_FORMATS.includes(name))
.map((item) => styles[item] || item)
}
export function nodeToHtml(
@@ -165,20 +165,3 @@ export function nodesToHtml(
)
})
}
export function makeCssModuleCompatibleClassName(
className: string | undefined,
formatType: 'ul'
): string {
if (!className) return ''
if (formatType === 'ul' && hasAvailableULFormat(className)) {
// TODO: REMOVE
// @ats-expect-error: We want to set css modules classNames even if it does not correspond
// to an existing class in the module style sheet. Due to our css modules plugin for
// typescript, we cannot do this without the ts-ignore
return styles[className] || className
}
return className
}