@crtobiasdelsud/portal-ui 1.0.32 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/package.json +2 -1
- package/src/components/ArticleHero/variants/V0/V0.jsx +2 -1
- package/src/components/ArticleHero/variants/V0Desktop/V0Desktop.jsx +2 -1
- package/src/components/ArticleHero/variants/V0Tablet/V0Tablet.jsx +2 -1
- package/src/components/ArticleHero/variants/V1/V1.jsx +2 -1
- package/src/components/ArticleHeroFull/ArticleHeroFull.jsx +2 -1
- package/src/components/Blocks/BlockColumns/BlockColumns.module.scss +5 -5
- package/src/components/Cabezal/CardCabezal/variants/Amp/Amp.jsx +3 -1
- package/src/components/Cabezal/CardCabezal/variants/Carrusel/Carrusel.jsx +7 -5
- package/src/components/Cabezal/CardCabezal/variants/Compact/Compact.jsx +6 -3
- package/src/components/Cabezal/CardCabezal/variants/Default/Default.jsx +6 -3
- package/src/components/Cabezal/CardCabezal/variants/Featured/Featured.jsx +7 -4
- package/src/components/Cabezal/CardCabezal/variants/FeaturedDuo/FeaturedDuo.jsx +8 -4
- package/src/components/Cabezal/CardCabezal/variants/FeaturedHorizontal/FeaturedHorizontal.jsx +8 -4
- package/src/components/Cabezal/CardCabezal/variants/Medium/Medium.jsx +6 -3
- package/src/components/Cabezal/CardCabezal/variants/Ranked/Ranked.jsx +2 -1
- package/src/components/Cabezal/variants/Carrusel/Carrusel.jsx +1 -1
- package/src/components/Cabezal/variants/Categoria/Categoria.jsx +1 -1
- package/src/components/Cabezal/variants/CategoriaDos/CategoriaDos.jsx +1 -1
- package/src/components/Cabezal/variants/Compact/Compact.jsx +1 -1
- package/src/components/Cabezal/variants/Default/Default.jsx +1 -1
- package/src/components/Cabezal/variants/Desktop/Desktop.jsx +1 -1
- package/src/components/Cabezal/variants/Duo/Duo.jsx +1 -1
- package/src/components/Cabezal/variants/DuoSinCopete/DuoSinCopete.jsx +1 -1
- package/src/components/Cabezal/variants/Horizontal/Horizontal.jsx +1 -1
- package/src/components/Cabezal/variants/LeeAdemas/LeeAdemas.jsx +2 -1
- package/src/components/Cabezal/variants/LoQueSeLee/LoQueSeLee.jsx +9 -4
- package/src/components/Cabezal/variants/Medium/Medium.jsx +1 -1
- package/src/components/Cabezal/variants/Mobile/Mobile.jsx +2 -2
- package/src/components/Cabezal/variants/Ranking/Ranking.jsx +1 -1
- package/src/components/Cabezal/variants/Tablet/Tablet.jsx +1 -1
- package/src/components/Cabezal/variants/Tres/Tres.jsx +1 -1
- package/src/components/Cabezal/variants/UnaDetallada/UnaDetallada.jsx +1 -1
- package/src/components/Cards/ArticleCard/ArticleCard.jsx +20 -14
- package/src/components/Cards/Bajada/variants/V1/V1.jsx +2 -1
- package/src/components/Cards/Bajada/variants/V2/V2.jsx +2 -1
- package/src/components/Carousel/Carousel.jsx +246 -68
- package/src/components/Carousel/Carousel.module.scss +30 -2
- package/src/components/Clima/ClimaView.jsx +1 -1
- package/src/components/EditorOutput/EditorOutput.jsx +35 -22
- package/src/components/EditorOutputFull/EditorOutputFull.jsx +32 -19
- package/src/components/Feed/variants/V1/V1.jsx +1 -1
- package/src/components/Footers/FooterSimple/FooterSimple.jsx +2 -2
- package/src/components/Headers/HeaderSimple/CategoriesBar/CategoriesBar.jsx +1 -1
- package/src/components/Headers/HeaderSimple/HeaderSimpleAmp/HeaderSimpleAmp.jsx +2 -2
- package/src/components/Headers/HeaderSimple/MenuDrawer/MenuDrawer.jsx +2 -2
- package/src/components/Hero/variants/V1/V1.jsx +14 -7
- package/src/components/Hero/variants/V2/V2.jsx +15 -8
- package/src/components/Hero/variants/V2/V2.module.scss +16 -34
- package/src/components/Hero/variants/V3/V3.jsx +15 -6
- package/src/components/Hero/variants/V3/V3.module.scss +10 -4
- package/src/utils/authorDisplay.js +48 -0
- package/src/utils/imageVariants.js +7 -2
- package/src/utils/imageVariants.test.js +22 -0
- package/src/utils/sanitizeHtml.js +275 -0
- package/src/utils/sanitizeHtml.test.js +53 -0
- package/src/utils/volanta.js +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased] — 2026-06-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
Soporte de "Publicar como organización" en los bylines de tarjetas, heros y
|
|
8
|
+
carruseles. Hasta ahora solo `AuthorBlock` (detalle) contemplaba `publicarComoOrg`;
|
|
9
|
+
el resto mostraba el byline desde `autor.nombre` y quedaba **vacío** en las notas
|
|
10
|
+
publicadas como organización (el backend manda `autor: null`).
|
|
11
|
+
|
|
12
|
+
- **`utils/authorDisplay.js`** (nuevo): helper `resolveAuthorDisplay()` (puro) +
|
|
13
|
+
hook `useAuthorDisplay(autor, publicarComoOrg)`. Centraliza la lógica de
|
|
14
|
+
`AuthorBlock`: con organización (o sin autor) devuelve el nombre del sitio como
|
|
15
|
+
`displayName` y su logo como `avatarSrc`, sin enlazar a `/autor/`. Lee
|
|
16
|
+
`siteName`/`iconUrl` de `SiteConfigContext` (con default seguro si no hay provider).
|
|
17
|
+
- **Bylines consistentes**: `Cards/ArticleCard`, `Cabezal/CardCabezal/variants/`
|
|
18
|
+
`{Default, Compact, Featured, FeaturedDuo, FeaturedHorizontal, Medium, Carrusel}`,
|
|
19
|
+
`Cabezal/variants/LoQueSeLee` y `Hero/variants/{V1, V2, V3}` ahora muestran
|
|
20
|
+
"Por {nombre del sitio}" (y el logo como avatar donde corresponde) en notas de
|
|
21
|
+
organización, en vez de no mostrar autor. `Ranked`/`Amp` (sin byline) no cambian.
|
|
22
|
+
- **Sin cambios en la API pública**: los componentes ya recibían el `article`
|
|
23
|
+
(con `publicarComoOrg`); no cambian props ni shape. `AuthorBlock` no se tocó.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
Correcciones de marcado semántico, accesibilidad y validez de enlaces (HTML
|
|
28
|
+
correcto para indexación e intérpretes de pantalla). Sin cambios en la API
|
|
29
|
+
pública (mismos exports, mismos nombres y shape de props); sólo cambia el
|
|
30
|
+
markup/atributos que renderizan los componentes. Las clases de estilo se
|
|
31
|
+
preservan en todos los casos.
|
|
32
|
+
|
|
33
|
+
- **Headings del Hero**: `Hero/variants/V1` renderizaba el título como `<p>` y
|
|
34
|
+
`Hero/variants/V2` como `<span>`; ahora ambos usan `<h2>` (igual que V3),
|
|
35
|
+
conservando las clases `headline`/`headlineWrap`.
|
|
36
|
+
- **`alt` de imágenes de contenido**: en `EditorOutput` y `EditorOutputFull`, las
|
|
37
|
+
imágenes del cuerpo caen al epígrafe como texto alternativo antes de quedar con
|
|
38
|
+
`alt=""`, evitando dejar imágenes informativas sin descripción.
|
|
39
|
+
- **Enlace de `ArticleCard`**: se eliminó el atributo `rel="canonical"` del `<a>`
|
|
40
|
+
de navegación (inválido en anclas; el canonical sólo corresponde al `<link>` del
|
|
41
|
+
head).
|
|
42
|
+
- **Rutas de enlaces**: `ArticleCard`, `Feed/variants/V1`, `HeaderSimpleAmp` y
|
|
43
|
+
`MenuDrawer` ahora normalizan el `href` a `/${slug}` (con guardia a `#` cuando no
|
|
44
|
+
hay slug), alineándose con el patrón del resto de la librería.
|
|
45
|
+
- **"Lee además" como encabezado**: en `EditorOutput` y `EditorOutputFull` el
|
|
46
|
+
título de relacionados pasó de `<p>` a `<h3>`.
|
|
47
|
+
- **Enlace "VER MÁS" descriptivo**: las 15 variantes de `Cabezal` agregan
|
|
48
|
+
`aria-label="Ver más de <título de sección>"` al enlace, manteniendo el texto
|
|
49
|
+
visible "VER MÁS".
|
|
50
|
+
- **`alt` del clima**: los iconos horarios de `ClimaView` usaban la URL/código del
|
|
51
|
+
icono como `alt`; ahora usan la condición meteorológica (o `""` si no hay).
|
|
52
|
+
- **AMP `CardCabezal`**: el título se envuelve en `<h3>` (manteniendo el `<a>` y su
|
|
53
|
+
clase) y el copete se renderiza como texto plano vía `stripHtml` en lugar de
|
|
54
|
+
inyectar HTML.
|
|
55
|
+
|
|
56
|
+
### Performance
|
|
57
|
+
|
|
58
|
+
Optimización de LCP del destacado de portada. Sin cambios en la API pública
|
|
59
|
+
(mismos exports y shape de props); sólo se pasan props que `AspectImage` ya
|
|
60
|
+
acepta.
|
|
61
|
+
|
|
62
|
+
- **Imágenes responsive en `Hero`**: `Hero/variants/V1`, `V2` y `V3` ahora pasan
|
|
63
|
+
`variants={article.imagen?.variants ?? null}` y `sizes="(max-width: 768px) 100vw, 66vw"`
|
|
64
|
+
a `AspectImage`, de modo que el destacado de portada sirve `srcset` y el
|
|
65
|
+
navegador baja la resolución justa según el viewport (antes bajaba siempre la
|
|
66
|
+
variante `large`).
|
|
67
|
+
- **Prioridad de carga condicionada**: las tres variantes usan
|
|
68
|
+
`priority={important}` para emitir `fetchpriority=high` sólo en el destacado
|
|
69
|
+
principal. `V1` no tenía prioridad (ahora la recibe cuando es `important`); `V2`
|
|
70
|
+
y `V3` emitían `priority` fijo (siempre `true`), lo que producía múltiples
|
|
71
|
+
`fetchpriority=high` en una misma página con varios Hero; ahora queda acotado al
|
|
72
|
+
destacado principal.
|
|
73
|
+
|
|
3
74
|
## [Unreleased] — 2026-05-18
|
|
4
75
|
|
|
5
76
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crtobiasdelsud/portal-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Componentes compartidos entre el portal (Next) y el CMS (Vite) — widgets, views, providers para adapters y article pool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"CHANGELOG.md"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
+
"test": "node --test src/**/*.test.js",
|
|
19
20
|
"release:patch": "npm version patch && npm publish --access public",
|
|
20
21
|
"release:minor": "npm version minor && npm publish --access public",
|
|
21
22
|
"release:major": "npm version major && npm publish --access public"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shared from '../../ArticleHero.module.scss'
|
|
2
2
|
import s from './V0.module.scss'
|
|
3
|
+
import { sanitizeInlineHtml } from '../../../../utils/sanitizeHtml.js'
|
|
3
4
|
|
|
4
5
|
export default function V0({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl, ExtrasEl, imgWrapClass, noImgMod }) {
|
|
5
6
|
const VolantaEl = volanta
|
|
@@ -9,7 +10,7 @@ export default function V0({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl,
|
|
|
9
10
|
const CopeteEl = copete
|
|
10
11
|
? <div
|
|
11
12
|
className={isAmp ? 'article-hero__copete' : `${shared.copete} ${s.copete}`}
|
|
12
|
-
dangerouslySetInnerHTML={{ __html: copete }}
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }}
|
|
13
14
|
/>
|
|
14
15
|
: null
|
|
15
16
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shared from '../../ArticleHero.module.scss'
|
|
2
2
|
import s from './V0Desktop.module.scss'
|
|
3
|
+
import { sanitizeInlineHtml } from '../../../../utils/sanitizeHtml.js'
|
|
3
4
|
|
|
4
5
|
export default function V0Desktop({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl, ExtrasEl, imgWrapClass, noImgMod }) {
|
|
5
6
|
const VolantaEl = volanta
|
|
@@ -9,7 +10,7 @@ export default function V0Desktop({ isAmp, inlineStyle, titulo, volanta, copete,
|
|
|
9
10
|
const CopeteEl = copete
|
|
10
11
|
? <div
|
|
11
12
|
className={isAmp ? 'article-hero__copete' : `${shared.copete} ${s.copete}`}
|
|
12
|
-
dangerouslySetInnerHTML={{ __html: copete }}
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }}
|
|
13
14
|
/>
|
|
14
15
|
: null
|
|
15
16
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shared from '../../ArticleHero.module.scss'
|
|
2
2
|
import s from './V0Tablet.module.scss'
|
|
3
|
+
import { sanitizeInlineHtml } from '../../../../utils/sanitizeHtml.js'
|
|
3
4
|
|
|
4
5
|
export default function V0Tablet({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl, ExtrasEl, imgWrapClass, noImgMod }) {
|
|
5
6
|
const VolantaEl = volanta
|
|
@@ -9,7 +10,7 @@ export default function V0Tablet({ isAmp, inlineStyle, titulo, volanta, copete,
|
|
|
9
10
|
const CopeteEl = copete
|
|
10
11
|
? <div
|
|
11
12
|
className={isAmp ? 'article-hero__copete' : `${shared.copete} ${s.copete}`}
|
|
12
|
-
dangerouslySetInnerHTML={{ __html: copete }}
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }}
|
|
13
14
|
/>
|
|
14
15
|
: null
|
|
15
16
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shared from '../../ArticleHero.module.scss'
|
|
2
2
|
import s from './V1.module.scss'
|
|
3
|
+
import { sanitizeInlineHtml } from '../../../../utils/sanitizeHtml.js'
|
|
3
4
|
|
|
4
5
|
export default function V1({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl, ExtrasEl, imgWrapClass, noImgMod }) {
|
|
5
6
|
const VolantaEl = volanta
|
|
@@ -9,7 +10,7 @@ export default function V1({ isAmp, inlineStyle, titulo, volanta, copete, ImgEl,
|
|
|
9
10
|
const CopeteEl = copete
|
|
10
11
|
? <div
|
|
11
12
|
className={isAmp ? 'article-hero__copete' : `${shared.copete} ${s.copete}`}
|
|
12
|
-
dangerouslySetInnerHTML={{ __html: copete }}
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }}
|
|
13
14
|
/>
|
|
14
15
|
: null
|
|
15
16
|
|
|
@@ -4,6 +4,7 @@ import styles from './ArticleHeroFull.module.scss'
|
|
|
4
4
|
import { useSiteConfig } from '../../context/SiteConfigContext.jsx'
|
|
5
5
|
import Carousel from '../Carousel/Carousel.jsx'
|
|
6
6
|
import AspectImage from '../UI/AspectImage/AspectImage.jsx'
|
|
7
|
+
import { sanitizeInlineHtml } from '../../utils/sanitizeHtml.js'
|
|
7
8
|
|
|
8
9
|
export default function ArticleHeroFull({ titulo, copete, imagen, imagenes, imagenEpigrafe, focalPoint, categoria }) {
|
|
9
10
|
const { config } = useSiteConfig()
|
|
@@ -65,7 +66,7 @@ export default function ArticleHeroFull({ titulo, copete, imagen, imagenes, imag
|
|
|
65
66
|
</div>
|
|
66
67
|
)}
|
|
67
68
|
<h1 className={styles.titulo}>{titulo}</h1>
|
|
68
|
-
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: copete }} />}
|
|
69
|
+
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }} />}
|
|
69
70
|
</div>
|
|
70
71
|
</div>
|
|
71
72
|
)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
display: grid;
|
|
10
10
|
gap: 20px;
|
|
11
11
|
|
|
12
|
-
@
|
|
12
|
+
@include respond(desktop) {
|
|
13
13
|
gap: 30px;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"recommended"
|
|
21
21
|
"feed";
|
|
22
22
|
|
|
23
|
-
@
|
|
23
|
+
@include respond(desktop) {
|
|
24
24
|
grid-template-columns: repeat(4, 1fr);
|
|
25
25
|
grid-template-areas: "recommended hero hero feed";
|
|
26
26
|
}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"fantasma"
|
|
36
36
|
"feed";
|
|
37
37
|
|
|
38
|
-
@
|
|
38
|
+
@include respond(desktop) {
|
|
39
39
|
grid-template-columns: repeat(4, 1fr);
|
|
40
40
|
grid-template-areas:
|
|
41
41
|
"hero hero hero hero"
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"recommended"
|
|
50
50
|
"feed";
|
|
51
51
|
|
|
52
|
-
@
|
|
52
|
+
@include respond(desktop) {
|
|
53
53
|
gap: 15px;
|
|
54
54
|
grid-template-columns: repeat(4, 1fr);
|
|
55
55
|
grid-template-areas: "recommended feed hero hero";
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
flex-direction: column;
|
|
64
64
|
padding: 10px 0;
|
|
65
65
|
|
|
66
|
-
@
|
|
66
|
+
@include respond(desktop) {
|
|
67
67
|
padding: 0;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
2
|
+
|
|
1
3
|
export default function Amp({ article, rank }) {
|
|
2
4
|
const { titulo, volanta, copete, imagen, slug } = article
|
|
3
5
|
const href = slug ? `/${slug}` : '#'
|
|
@@ -10,7 +12,7 @@ export default function Amp({ article, rank }) {
|
|
|
10
12
|
</a>
|
|
11
13
|
)}
|
|
12
14
|
<div className="card-cabezal__body">
|
|
13
|
-
{volanta && <span className="card-cabezal__volanta">{volanta}
|
|
15
|
+
{volanta && <span className="card-cabezal__volanta">{volantaWithStop(volanta)}</span>}
|
|
14
16
|
{titulo && <a href={href} className="card-cabezal__titulo">{titulo}</a>}
|
|
15
17
|
{copete && <div className="card-cabezal__copete" dangerouslySetInnerHTML={{ __html: copete }} />}
|
|
16
18
|
</div>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
2
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
2
3
|
import styles from './Carrusel.module.scss'
|
|
3
4
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
4
5
|
|
|
5
6
|
export default function CarruselCard({ article }) {
|
|
6
7
|
|
|
7
8
|
const { Link } = useAdapters()
|
|
8
|
-
const { titulo, imagen, slug, autor } = article
|
|
9
|
+
const { titulo, imagen, slug, autor, publicarComoOrg } = article
|
|
9
10
|
const href = slug ? `/${slug}` : '#'
|
|
11
|
+
const { displayName, avatarSrc } = useAuthorDisplay(autor, publicarComoOrg)
|
|
10
12
|
|
|
11
13
|
return (
|
|
12
14
|
<Tooltip text={titulo}>
|
|
@@ -22,13 +24,13 @@ export default function CarruselCard({ article }) {
|
|
|
22
24
|
)}
|
|
23
25
|
<div className={styles.body}>
|
|
24
26
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
25
|
-
{
|
|
27
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
26
28
|
</div>
|
|
27
|
-
{
|
|
29
|
+
{avatarSrc && (
|
|
28
30
|
<div className={styles.authorAvatarWrap}>
|
|
29
31
|
<img
|
|
30
|
-
src={
|
|
31
|
-
alt={
|
|
32
|
+
src={avatarSrc}
|
|
33
|
+
alt={displayName ?? ''}
|
|
32
34
|
className={styles.authorAvatar}
|
|
33
35
|
/>
|
|
34
36
|
</div>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
3
4
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
5
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
6
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
7
|
|
|
6
8
|
export default function Compact({ article }) {
|
|
7
9
|
|
|
8
10
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, imagen, slug, autor, focalPoint } = article
|
|
11
|
+
const { titulo, volanta, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
12
|
const href = slug ? `/${slug}` : '#'
|
|
13
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<Tooltip text={titulo}>
|
|
@@ -25,10 +28,10 @@ export default function Compact({ article }) {
|
|
|
25
28
|
)}
|
|
26
29
|
<div className={styles.body}>
|
|
27
30
|
<div className={styles.header}>
|
|
28
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
31
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
29
32
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
30
33
|
</div>
|
|
31
|
-
{
|
|
34
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
32
35
|
</div>
|
|
33
36
|
</article>
|
|
34
37
|
</Tooltip>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
3
4
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
5
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
6
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
7
|
|
|
6
8
|
export default function Default({ article }) {
|
|
7
9
|
|
|
8
10
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, imagen, slug, autor, focalPoint } = article
|
|
11
|
+
const { titulo, volanta, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
12
|
const href = slug ? `/${slug}` : '#'
|
|
13
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<Tooltip text={titulo}>
|
|
@@ -24,10 +27,10 @@ export default function Default({ article }) {
|
|
|
24
27
|
)}
|
|
25
28
|
<div className={styles.body}>
|
|
26
29
|
<div className={styles.header}>
|
|
27
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
30
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
28
31
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
29
32
|
</div>
|
|
30
|
-
{
|
|
33
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
31
34
|
</div>
|
|
32
35
|
</article>
|
|
33
36
|
</Tooltip>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
3
4
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
5
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
6
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
7
|
|
|
6
8
|
export default function Featured({ article, large }) {
|
|
7
9
|
|
|
8
10
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, copete, imagen, slug, autor, focalPoint } = article
|
|
11
|
+
const { titulo, volanta, copete, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
12
|
const href = slug ? `/${slug}` : '#'
|
|
13
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<Tooltip text={titulo}>
|
|
@@ -25,10 +28,10 @@ export default function Featured({ article, large }) {
|
|
|
25
28
|
)}
|
|
26
29
|
<div className={styles.body}>
|
|
27
30
|
<div className={styles.header}>
|
|
28
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
31
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
29
32
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
30
|
-
</div>
|
|
31
|
-
{
|
|
33
|
+
</div>
|
|
34
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
32
35
|
</div>
|
|
33
36
|
</article>
|
|
34
37
|
</Tooltip>
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
4
|
+
import { sanitizeInlineHtml } from '../../../../../utils/sanitizeHtml.js'
|
|
3
5
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
6
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
7
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
8
|
|
|
6
9
|
export default function FeaturedDuo({ article }) {
|
|
7
10
|
|
|
8
11
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, copete, imagen, slug, autor, focalPoint } = article
|
|
12
|
+
const { titulo, volanta, copete, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
13
|
const href = slug ? `/${slug}` : '#'
|
|
14
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
15
|
|
|
12
16
|
return (
|
|
13
17
|
<Tooltip text={titulo}>
|
|
@@ -24,11 +28,11 @@ export default function FeaturedDuo({ article }) {
|
|
|
24
28
|
)}
|
|
25
29
|
<div className={styles.body}>
|
|
26
30
|
<div className={styles.header}>
|
|
27
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
31
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
28
32
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
29
33
|
</div>
|
|
30
|
-
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: copete }} />}
|
|
31
|
-
{
|
|
34
|
+
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }} />}
|
|
35
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
32
36
|
</div>
|
|
33
37
|
</article>
|
|
34
38
|
</Tooltip>
|
package/src/components/Cabezal/CardCabezal/variants/FeaturedHorizontal/FeaturedHorizontal.jsx
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
4
|
+
import { sanitizeInlineHtml } from '../../../../../utils/sanitizeHtml.js'
|
|
3
5
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
6
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
7
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
8
|
|
|
6
9
|
export default function FeaturedHorizontal({ article }) {
|
|
7
10
|
|
|
8
11
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, copete, imagen, slug, autor, focalPoint } = article
|
|
12
|
+
const { titulo, volanta, copete, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
13
|
const href = slug ? `/${slug}` : '#'
|
|
14
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
15
|
|
|
12
16
|
return (
|
|
13
17
|
<Tooltip text={titulo}>
|
|
@@ -24,11 +28,11 @@ export default function FeaturedHorizontal({ article }) {
|
|
|
24
28
|
)}
|
|
25
29
|
<div className={styles.body}>
|
|
26
30
|
<div className={styles.header}>
|
|
27
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
31
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
28
32
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
29
33
|
</div>
|
|
30
|
-
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: copete }} />}
|
|
31
|
-
{
|
|
34
|
+
{copete && <div className={styles.copete} dangerouslySetInnerHTML={{ __html: sanitizeInlineHtml(copete) }} />}
|
|
35
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
32
36
|
</div>
|
|
33
37
|
</article>
|
|
34
38
|
</Tooltip>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import styles from '../../CardCabezal.module.scss'
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
|
+
import { useAuthorDisplay } from '../../../../../utils/authorDisplay.js'
|
|
3
4
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
5
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
6
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
7
|
|
|
6
8
|
export default function Medium({ article }) {
|
|
7
9
|
|
|
8
10
|
const { Link } = useAdapters()
|
|
9
|
-
const { titulo, volanta, imagen, slug, autor, focalPoint } = article
|
|
11
|
+
const { titulo, volanta, imagen, slug, autor, publicarComoOrg, focalPoint } = article
|
|
10
12
|
const href = slug ? `/${slug}` : '#'
|
|
13
|
+
const { displayName } = useAuthorDisplay(autor, publicarComoOrg)
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<Tooltip text={titulo}>
|
|
@@ -24,10 +27,10 @@ export default function Medium({ article }) {
|
|
|
24
27
|
)}
|
|
25
28
|
<div className={styles.body}>
|
|
26
29
|
<div className={styles.header}>
|
|
27
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
30
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
28
31
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
29
32
|
</div>
|
|
30
|
-
{
|
|
33
|
+
{displayName && <span className={styles.autor}>Por {displayName}</span>}
|
|
31
34
|
</div>
|
|
32
35
|
</article>
|
|
33
36
|
</Tooltip>
|
|
@@ -2,6 +2,7 @@ import styles from '../../CardCabezal.module.scss'
|
|
|
2
2
|
import { useAdapters } from '../../../../../adapters/AdaptersContext.jsx'
|
|
3
3
|
import AspectImage from '../../../../UI/AspectImage/AspectImage.jsx'
|
|
4
4
|
import Tooltip from '../../../../UI/ToolTip/ToolTip.jsx'
|
|
5
|
+
import { volantaWithStop } from '../../../../../utils/volanta.js'
|
|
5
6
|
|
|
6
7
|
export default function Ranked({ article, rank, rankVariant }) {
|
|
7
8
|
|
|
@@ -25,7 +26,7 @@ export default function Ranked({ article, rank, rankVariant }) {
|
|
|
25
26
|
)}
|
|
26
27
|
<div className={styles.body}>
|
|
27
28
|
<div className={styles.header}>
|
|
28
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
29
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)}</span>}
|
|
29
30
|
{titulo && <Link href={href} className={styles.titulo}>{titulo}</Link>}
|
|
30
31
|
</div>
|
|
31
32
|
{rank != null && <span className={styles.rank}>{rank}</span>}
|
|
@@ -130,7 +130,7 @@ export default function Carrusel({ titulo, verMasUrl, articles, getSlotProps })
|
|
|
130
130
|
{titulo && (
|
|
131
131
|
<div className={styles.header}>
|
|
132
132
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
133
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
133
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
134
134
|
</div>
|
|
135
135
|
)}
|
|
136
136
|
|
|
@@ -12,7 +12,7 @@ export default function Categoria({ titulo, verMasUrl, articles, getSlotProps })
|
|
|
12
12
|
{titulo && (
|
|
13
13
|
<div className={styles.header}>
|
|
14
14
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
15
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
15
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
16
16
|
</div>
|
|
17
17
|
)}
|
|
18
18
|
{articles.length > 0 && (
|
|
@@ -12,7 +12,7 @@ export default function CategoriaDos({ titulo, verMasUrl, articles, getSlotProps
|
|
|
12
12
|
{titulo && (
|
|
13
13
|
<div className={styles.header}>
|
|
14
14
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
15
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
15
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
16
16
|
</div>
|
|
17
17
|
)}
|
|
18
18
|
{articles.length > 0 && (
|
|
@@ -7,7 +7,7 @@ export default function Compact({ titulo, verMasUrl, articles, getSlotProps }) {
|
|
|
7
7
|
{titulo && (
|
|
8
8
|
<div className={styles.header}>
|
|
9
9
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
10
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
10
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
11
11
|
</div>
|
|
12
12
|
)}
|
|
13
13
|
{articles.length > 0 && (
|
|
@@ -7,7 +7,7 @@ export default function Default({ titulo, verMasUrl, articles, tipo, getSlotProp
|
|
|
7
7
|
{titulo && (
|
|
8
8
|
<div className={styles.header}>
|
|
9
9
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
10
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
10
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
11
11
|
</div>
|
|
12
12
|
)}
|
|
13
13
|
{articles.length > 0 && (
|
|
@@ -28,7 +28,7 @@ export default function Desktop({ titulo, verMasUrl, articles, tipo }) {
|
|
|
28
28
|
{titulo && (
|
|
29
29
|
<div className={styles.header}>
|
|
30
30
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
31
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
31
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
32
32
|
</div>
|
|
33
33
|
)}
|
|
34
34
|
{articles.length > 0 && (
|
|
@@ -7,7 +7,7 @@ export default function Duo({ titulo, verMasUrl, articles, getSlotProps }) {
|
|
|
7
7
|
{titulo && (
|
|
8
8
|
<div className={styles.header}>
|
|
9
9
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
10
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
10
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
11
11
|
</div>
|
|
12
12
|
)}
|
|
13
13
|
{articles.length > 0 && (
|
|
@@ -7,7 +7,7 @@ export default function DuoSinCopete({ titulo, verMasUrl, articles, getSlotProps
|
|
|
7
7
|
{titulo && (
|
|
8
8
|
<div className={styles.header}>
|
|
9
9
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
10
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
10
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
11
11
|
</div>
|
|
12
12
|
)}
|
|
13
13
|
{articles.length > 0 && (
|
|
@@ -7,7 +7,7 @@ export default function Horizontal({ titulo, verMasUrl, articles, getSlotProps }
|
|
|
7
7
|
{titulo && (
|
|
8
8
|
<div className={styles.header}>
|
|
9
9
|
<h2 className={styles.titulo}>{titulo}</h2>
|
|
10
|
-
{verMasUrl && <a href={verMasUrl} className={styles.verMas}>VER MÁS</a>}
|
|
10
|
+
{verMasUrl && <a href={verMasUrl} className={styles.verMas} aria-label={`Ver más de ${titulo}`}>VER MÁS</a>}
|
|
11
11
|
</div>
|
|
12
12
|
)}
|
|
13
13
|
{articles.length > 0 && (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useAdapters } from '../../../../adapters/AdaptersContext.jsx'
|
|
2
2
|
import AspectImage from '../../../UI/AspectImage/AspectImage.jsx'
|
|
3
|
+
import { volantaWithStop } from '../../../../utils/volanta.js'
|
|
3
4
|
import styles from './LeeAdemas.module.scss'
|
|
4
5
|
|
|
5
6
|
function LeeAdemasCard({ article }) {
|
|
@@ -22,7 +23,7 @@ function LeeAdemasCard({ article }) {
|
|
|
22
23
|
)}
|
|
23
24
|
<div className={styles.body}>
|
|
24
25
|
<Link href={href} className={styles.header}>
|
|
25
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
26
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)} </span>}
|
|
26
27
|
{titulo && <span className={styles.titulo}>{titulo}</span>}
|
|
27
28
|
</Link>
|
|
28
29
|
</div>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useAdapters } from '../../../../adapters/AdaptersContext.jsx'
|
|
2
|
+
import { useAuthorDisplay } from '../../../../utils/authorDisplay.js'
|
|
2
3
|
import AspectImage from '../../../UI/AspectImage/AspectImage.jsx'
|
|
4
|
+
import { volantaWithStop } from '../../../../utils/volanta.js'
|
|
3
5
|
import styles from './LoQueSeLee.module.scss'
|
|
4
6
|
|
|
5
7
|
// View pura: recibe el artículo ya resuelto.
|
|
@@ -7,10 +9,13 @@ import styles from './LoQueSeLee.module.scss'
|
|
|
7
9
|
// /api/portal/articles/trending?categoria=X y filtrar por excludeId.
|
|
8
10
|
export default function LoQueSeLee({ article }) {
|
|
9
11
|
const { Link } = useAdapters()
|
|
12
|
+
// El hook va antes del early-return para no violar las reglas de hooks; null-safe
|
|
13
|
+
// porque `article` puede ser null (ver guard debajo).
|
|
14
|
+
const { displayName } = useAuthorDisplay(article?.autor, article?.publicarComoOrg)
|
|
10
15
|
|
|
11
16
|
if (!article) return null
|
|
12
17
|
|
|
13
|
-
const { titulo, volanta, imagen, slug,
|
|
18
|
+
const { titulo, volanta, imagen, slug, focalPoint } = article
|
|
14
19
|
const href = slug ? `/${slug}` : '#'
|
|
15
20
|
|
|
16
21
|
return (
|
|
@@ -36,11 +41,11 @@ export default function LoQueSeLee({ article }) {
|
|
|
36
41
|
|
|
37
42
|
<div className={styles.body}>
|
|
38
43
|
<Link href={href} className={styles.textLink}>
|
|
39
|
-
{volanta && <span className={styles.volanta}>{volanta}
|
|
44
|
+
{volanta && <span className={styles.volanta}>{volantaWithStop(volanta)} </span>}
|
|
40
45
|
{titulo && <span className={styles.titulo}>{titulo}</span>}
|
|
41
46
|
</Link>
|
|
42
|
-
{
|
|
43
|
-
<span className={styles.autor}>Por {
|
|
47
|
+
{displayName && (
|
|
48
|
+
<span className={styles.autor}>Por {displayName}</span>
|
|
44
49
|
)}
|
|
45
50
|
</div>
|
|
46
51
|
</article>
|