From aaf79b96e5a467c522f698c0a04dc839223daede Mon Sep 17 00:00:00 2001 From: frederickobrien Date: Tue, 26 Nov 2024 14:46:41 +0000 Subject: [PATCH 01/84] Bump `@guardian/commercial` to 23.7.2 Co-Authored-By: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Co-Authored-By: Marjan Kalanaki <15894063+marjisound@users.noreply.github.com> Co-Authored-By: Dominik Lander Co-Authored-By: Rowanne Kabalan <74187452+rowannekabalan@users.noreply.github.com> --- dotcom-rendering/package.json | 2 +- pnpm-lock.yaml | 356 +--------------------------------- 2 files changed, 8 insertions(+), 350 deletions(-) diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 570be4a9d1c..469dd42a867 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -41,7 +41,7 @@ "@guardian/bridget": "8.0.0", "@guardian/browserslist-config": "6.1.0", "@guardian/cdk": "50.13.0", - "@guardian/commercial": "23.2.0", + "@guardian/commercial": "23.7.4", "@guardian/core-web-vitals": "7.0.0", "@guardian/eslint-config": "7.0.1", "@guardian/eslint-config-typescript": "9.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32ca40d92d0..60388b658b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,8 +338,8 @@ importers: specifier: 50.13.0 version: 50.13.0(@swc/core@1.9.2)(@types/node@20.14.10)(aws-cdk-lib@2.100.0)(aws-cdk@2.100.0)(constructs@10.3.0)(typescript@5.5.3) '@guardian/commercial': - specifier: 23.2.0 - version: 23.2.0(@guardian/ab-core@8.0.0)(@guardian/core-web-vitals@7.0.0)(@guardian/identity-auth-frontend@4.0.0)(@guardian/identity-auth@2.1.0)(@guardian/libs@19.1.0)(@guardian/source@8.0.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) + specifier: 23.7.4 + version: 23.7.4(@guardian/ab-core@8.0.0)(@guardian/core-web-vitals@7.0.0)(@guardian/identity-auth-frontend@4.0.0)(@guardian/identity-auth@2.1.0)(@guardian/libs@19.1.0)(@guardian/source@8.0.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) '@guardian/core-web-vitals': specifier: 7.0.0 version: 7.0.0(@guardian/libs@19.1.0)(tslib@2.6.2)(typescript@5.5.3)(web-vitals@4.2.3) @@ -3275,187 +3275,6 @@ packages: resolution: {integrity: sha512-pHnlKaHz8UCrlGkAF6aoBptFUTzeZeL7OpHqKGx7uZC67ZfVvk1L11SJkxOw2CLxUQm9YxigSQXqtpUiImXDng==} dev: false - /@changesets/apply-release-plan@7.0.5: - resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} - dependencies: - '@changesets/config': 3.0.3 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.5.4 - dev: false - - /@changesets/assemble-release-plan@6.0.4: - resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.5.4 - dev: false - - /@changesets/changelog-git@0.2.0: - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} - dependencies: - '@changesets/types': 6.0.0 - dev: false - - /@changesets/cli@2.27.8: - resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} - hasBin: true - dependencies: - '@changesets/apply-release-plan': 7.0.5 - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.3 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/get-release-plan': 4.0.4 - '@changesets/git': 3.0.1 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.2 - '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.6 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - mri: 1.2.0 - outdent: 0.5.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.0 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.5.4 - spawndamnit: 2.0.0 - term-size: 2.2.1 - dev: false - - /@changesets/config@3.0.3: - resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - dev: false - - /@changesets/errors@0.2.0: - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - dependencies: - extendable-error: 0.1.7 - dev: false - - /@changesets/get-dependents-graph@2.1.2: - resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.5.4 - dev: false - - /@changesets/get-release-plan@4.0.4: - resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} - dependencies: - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/config': 3.0.3 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: false - - /@changesets/get-version-range-type@0.4.0: - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: false - - /@changesets/git@3.0.1: - resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 2.0.0 - dev: false - - /@changesets/logger@0.1.1: - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - dependencies: - picocolors: 1.1.1 - dev: false - - /@changesets/parse@0.4.0: - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} - dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 - dev: false - - /@changesets/pre@2.0.1: - resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - dev: false - - /@changesets/read@0.6.1: - resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} - dependencies: - '@changesets/git': 3.0.1 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - dev: false - - /@changesets/should-skip-package@0.1.1: - resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: false - - /@changesets/types@4.1.0: - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: false - - /@changesets/types@6.0.0: - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: false - - /@changesets/write@0.3.2: - resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} - dependencies: - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - dev: false - /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} @@ -4027,8 +3846,8 @@ packages: - typescript dev: false - /@guardian/commercial@23.2.0(@guardian/ab-core@8.0.0)(@guardian/core-web-vitals@7.0.0)(@guardian/identity-auth-frontend@4.0.0)(@guardian/identity-auth@2.1.0)(@guardian/libs@19.1.0)(@guardian/source@8.0.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): - resolution: {integrity: sha512-aQss6IZQN10Io3OfZ+4uSiEAJwhW9waEpos4bbUI5S3yx0dV75kuz33ba0/wt2szT34RdMVTq4NgtQWiUeNqwg==} + /@guardian/commercial@23.7.4(@guardian/ab-core@8.0.0)(@guardian/core-web-vitals@7.0.0)(@guardian/identity-auth-frontend@4.0.0)(@guardian/identity-auth@2.1.0)(@guardian/libs@19.1.0)(@guardian/source@8.0.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): + resolution: {integrity: sha512-VMRRWR0pUMcZkDYfJ8kf8LD5OK/x29WPjbphizCAB0h7zJTeRKhUiJfQQ3l0YNZA+MrRXzY+PZIaf2IZvQJbGg==} peerDependencies: '@guardian/ab-core': ^8.0.0 '@guardian/core-web-vitals': ^7.0.0 @@ -4038,13 +3857,12 @@ packages: '@guardian/source': ^8.0.0 typescript: ~5.5.3 dependencies: - '@changesets/cli': 2.27.8 '@guardian/ab-core': 8.0.0(tslib@2.6.2)(typescript@5.5.3) '@guardian/core-web-vitals': 7.0.0(@guardian/libs@19.1.0)(tslib@2.6.2)(typescript@5.5.3)(web-vitals@4.2.3) '@guardian/identity-auth': 2.1.0(@guardian/libs@19.1.0)(tslib@2.6.2)(typescript@5.5.3) '@guardian/identity-auth-frontend': 4.0.0(@guardian/identity-auth@2.1.0)(@guardian/libs@19.1.0)(tslib@2.6.2)(typescript@5.5.3) '@guardian/libs': 19.1.0(tslib@2.6.2)(typescript@5.5.3) - '@guardian/prebid.js': 8.52.0-7(react-dom@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) + '@guardian/prebid.js': 8.52.0-8(react-dom@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) '@guardian/source': 8.0.0(@emotion/react@11.11.3)(@types/react@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) '@octokit/core': 6.1.2 fastdom: 1.0.12 @@ -4344,8 +4162,8 @@ packages: '@guardian/tsconfig': 1.0.0 dev: false - /@guardian/prebid.js@8.52.0-7(react-dom@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3): - resolution: {integrity: sha512-RWg8vyyfZccrqRXu5w+nnghsskYCHkLWQqXdFh1iSadYRU+exuNcynxStKdzUZdPNnh8aUyEuMIoaZXHmEZEYg==} + /@guardian/prebid.js@8.52.0-8(react-dom@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3): + resolution: {integrity: sha512-JQ9unudgn5DAwLoqEiBdjuEaEFOhR9JZhjHD46pm4306C1XfYF0ts3YWIT9poq6vN1tbMQJw0NQeyXcL5QGIcA==} engines: {node: '>=12.0.0'} dependencies: '@babel/core': 7.26.0 @@ -4893,26 +4711,6 @@ packages: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} dev: false - /@manypkg/find-root@1.1.0: - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - dependencies: - '@babel/runtime': 7.26.0 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - dev: false - - /@manypkg/get-packages@1.1.3: - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - dependencies: - '@babel/runtime': 7.26.0 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - dev: false - /@mdx-js/react@3.0.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} peerDependencies: @@ -7246,10 +7044,6 @@ packages: '@types/node': 20.14.10 dev: false - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: false - /@types/node@16.18.68: resolution: {integrity: sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==} dev: false @@ -8134,11 +7928,6 @@ packages: ansi-wrap: 0.1.0 dev: false - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: false - /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -8637,13 +8426,6 @@ packages: open: 8.4.2 dev: false - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - dependencies: - is-windows: 1.0.2 - dev: false - /big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} dev: false @@ -8971,10 +8753,6 @@ packages: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} dev: false - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: false - /check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -10044,11 +9822,6 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: false - /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -10310,14 +10083,6 @@ packages: tapable: 2.2.1 dev: false - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - dev: false - /entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} dev: false @@ -11340,19 +11105,6 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: false - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: false - - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - dev: false - /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false @@ -11713,15 +11465,6 @@ packages: universalify: 2.0.1 dev: false - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: false - /fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -12477,10 +12220,6 @@ packages: - supports-color dev: false - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: false - /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -12904,13 +12643,6 @@ packages: has-tostringtag: 1.0.2 dev: false - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - dependencies: - better-path-resolve: 1.0.0 - dev: false - /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} @@ -12942,11 +12674,6 @@ packages: get-intrinsic: 1.2.4 dev: false - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: false - /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -14060,10 +13787,6 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: false - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: false - /lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: false @@ -14606,11 +14329,6 @@ packages: resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} dev: false - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: false - /mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} @@ -14996,15 +14714,6 @@ packages: arch: 2.2.0 dev: false - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: false - - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: false - /p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -15017,13 +14726,6 @@ packages: p-timeout: 6.1.2 dev: false - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - dependencies: - p-map: 2.1.0 - dev: false - /p-filter@3.0.0: resolution: {integrity: sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -15085,11 +14787,6 @@ packages: p-limit: 4.0.0 dev: false - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: false - /p-map@5.5.0: resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} engines: {node: '>=12'} @@ -15121,10 +14818,6 @@ packages: engines: {node: '>=6'} dev: false - /package-manager-detector@0.2.0: - resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} - dev: false - /param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: @@ -15543,12 +15236,6 @@ packages: engines: {node: '>= 0.8.0'} dev: false - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: false - /prettier@3.0.3: resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} engines: {node: '>=14'} @@ -15837,16 +15524,6 @@ packages: type-fest: 0.6.0 dev: false - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - dev: false - /readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} dependencies: @@ -16637,13 +16314,6 @@ packages: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: false - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} - dependencies: - cross-spawn: 5.1.0 - signal-exit: 3.0.7 - dev: false - /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -17107,11 +16777,6 @@ packages: engines: {node: '>=6'} dev: false - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: false - /terser-webpack-plugin@5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} @@ -17259,13 +16924,6 @@ packages: engines: {node: '>=14.0.0'} dev: false - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 - dev: false - /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: false From 3a56ce349a7630484326da6e9af74ad7b0f551ec Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:56:26 +0000 Subject: [PATCH 02/84] Group Editions Crosswords By Date (#12917) Updated the shape of the editions crosswords data to be a list of crosswords. Added a `groupByDate` function to divide this up into dates. Added a `CrosswordSelect` component to allow selecting a date and a specific crossword on that date. Added stories and tests for this component. Added a `Crosswords` component that will call the `groupByDate` function, render the `CrosswordSelect` component, and render the selected crossword itself. This for now just renders the first clue of the selected crossword to demonstrate functionality. Added stories and tests for this component. --- .../fixtures/manual/editionsCrossword.ts | 67 +++ .../CrosswordSelect.editions.stories.tsx | 71 +++ .../components/CrosswordSelect.editions.tsx | 81 ++++ .../Crosswords.editions.stories.tsx | 67 +++ .../src/components/Crosswords.editions.tsx | 94 ++++ .../src/model/editions-crossword-schema.json | 448 ++++++------------ .../src/types/editionsCrossword.ts | 52 +- 7 files changed, 568 insertions(+), 312 deletions(-) create mode 100644 dotcom-rendering/fixtures/manual/editionsCrossword.ts create mode 100644 dotcom-rendering/src/components/CrosswordSelect.editions.stories.tsx create mode 100644 dotcom-rendering/src/components/CrosswordSelect.editions.tsx create mode 100644 dotcom-rendering/src/components/Crosswords.editions.stories.tsx create mode 100644 dotcom-rendering/src/components/Crosswords.editions.tsx diff --git a/dotcom-rendering/fixtures/manual/editionsCrossword.ts b/dotcom-rendering/fixtures/manual/editionsCrossword.ts new file mode 100644 index 00000000000..10c9a8145b0 --- /dev/null +++ b/dotcom-rendering/fixtures/manual/editionsCrossword.ts @@ -0,0 +1,67 @@ +import type { FEEditionsCrossword } from '../../src/types/editionsCrossword'; + +export const quickCrossword: FEEditionsCrossword = { + date: '2024-11-01T23:00:00Z', + dateSolutionAvailable: '2024-11-01T23:00:00Z', + dimensions: { + cols: 13, + rows: 13, + }, + entries: [ + { + clue: 'A quick crossword clue', + direction: 'across', + group: ['1-across'], + humanNumber: '1', + id: '1-across', + length: 6, + number: 1, + position: { + x: 0, + y: 0, + }, + separatorLocations: {}, + solution: 'AAAAAA', + format: '6', + }, + ], + hasNumbers: true, + name: 'Quick crossword No 1', + number: 1, + randomCluesOrdering: false, + solutionAvailable: true, + type: 'quick', +}; + +export const crypticCrossword: FEEditionsCrossword = { + date: '2024-11-01T23:00:00Z', + dateSolutionAvailable: '2024-11-01T23:00:00Z', + dimensions: { + cols: 15, + rows: 15, + }, + entries: [ + { + clue: 'A cryptic crossword clue', + direction: 'down', + group: ['7-down'], + humanNumber: '7', + id: '7-down', + length: 8, + number: 7, + position: { + x: 12, + y: 0, + }, + separatorLocations: {}, + solution: 'BBBBBBBB', + format: '8', + }, + ], + hasNumbers: true, + name: 'Cryptic crossword No 2', + number: 2, + randomCluesOrdering: false, + solutionAvailable: true, + type: 'cryptic', +}; diff --git a/dotcom-rendering/src/components/CrosswordSelect.editions.stories.tsx b/dotcom-rendering/src/components/CrosswordSelect.editions.stories.tsx new file mode 100644 index 00000000000..d5e4c2a6b7a --- /dev/null +++ b/dotcom-rendering/src/components/CrosswordSelect.editions.stories.tsx @@ -0,0 +1,71 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, fn, userEvent, waitFor, within } from '@storybook/test'; +import { CrosswordSelect } from './CrosswordSelect.editions'; + +const meta = { + title: 'Components/Crossword Select (Editions)', + component: CrosswordSelect, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const NoCrosswords = { + args: { + crosswordsByDate: {}, + date: 'Monday', + crosswordIndex: 0, + onDateChange: fn(), + onCrosswordIndexChange: fn(), + }, +} satisfies Story; + +export const SomeCrosswords = { + args: { + ...NoCrosswords.args, + crosswordsByDate: { + Monday: [ + { name: 'A Monday Crossword' }, + { name: 'Another Monday Crossword' }, + ], + Tuesday: [ + { name: 'A Tuesday Crossword' }, + { name: 'Another Tuesday Crossword' }, + ], + }, + }, + play: async ({ args, canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Select 'Tuesday'", async () => { + await userEvent.selectOptions( + canvas.getByLabelText('Date'), + 'Tuesday', + ); + await waitFor(() => + expect(args.onDateChange).toHaveBeenLastCalledWith('Tuesday'), + ); + }); + + await step("Select the second 'Tuesday' crossword", async () => { + await userEvent.selectOptions( + canvas.getByLabelText('Crossword'), + '1', + ); + await waitFor(() => + expect(args.onCrosswordIndexChange).toHaveBeenLastCalledWith(1), + ); + }); + + await step("Select 'Monday'", async () => { + await userEvent.selectOptions( + canvas.getByLabelText('Date'), + 'Monday', + ); + await waitFor(() => + expect(args.onDateChange).toHaveBeenLastCalledWith('Monday'), + ); + }); + }, +} satisfies Story; diff --git a/dotcom-rendering/src/components/CrosswordSelect.editions.tsx b/dotcom-rendering/src/components/CrosswordSelect.editions.tsx new file mode 100644 index 00000000000..a44b9236972 --- /dev/null +++ b/dotcom-rendering/src/components/CrosswordSelect.editions.tsx @@ -0,0 +1,81 @@ +type Props = { + /** + * Crosswords organised by date. + */ + crosswordsByDate: Record; + /** + * The selected date. + */ + date: string; + /** + * Called when a new date is chosen. + */ + onDateChange: (date: string) => void; + /** + * The location of the crossword in the list of crosswords for a given date. + */ + crosswordIndex: number; + /** + * Called when a new crossword index in the list of crosswords is chosen. + */ + onCrosswordIndexChange: (crossword: number) => void; +}; + +/** + * Provides two select elements to choose a specific crossword. Each date has a + * list of crosswords available. The first select element is for choosing the + * date, the second is for choosing a particular crossword on that date. + */ +export function CrosswordSelect({ + crosswordsByDate, + date, + onDateChange, + crosswordIndex, + onCrosswordIndexChange, +}: Props) { + const dates = Object.keys(crosswordsByDate); + + if (dates.length === 0) { + return null; + } + + return ( + <> + + + {crosswordsByDate[date] === undefined || + crosswordsByDate[date].length === 0 ? null : ( + <> + + + + )} + + ); +} diff --git a/dotcom-rendering/src/components/Crosswords.editions.stories.tsx b/dotcom-rendering/src/components/Crosswords.editions.stories.tsx new file mode 100644 index 00000000000..833e0641923 --- /dev/null +++ b/dotcom-rendering/src/components/Crosswords.editions.stories.tsx @@ -0,0 +1,67 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, waitFor, within } from '@storybook/test'; +import { + crypticCrossword, + quickCrossword, +} from '../../fixtures/manual/editionsCrossword'; +import { Crosswords } from './Crosswords.editions'; + +const meta = { + title: 'Components/Crosswords (Editions)', + component: Crosswords, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const UKTimezone = { + args: { + crosswords: [ + quickCrossword, + crypticCrossword, + { + ...quickCrossword, + date: '2024-11-02T23:00:00Z', + dateSolutionAvailable: '2024-11-02T23:00:00Z', + entries: quickCrossword.entries.map((entry) => ({ + ...entry, + clue: 'Another quick crossword clue', + })), + }, + { + ...crypticCrossword, + date: '2024-11-02T23:00:00Z', + dateSolutionAvailable: '2024-11-02T23:00:00Z', + entries: crypticCrossword.entries.map((entry) => ({ + ...entry, + clue: 'Another cryptic crossword clue', + })), + }, + ], + timeZone: 'Europe/London', + }, + play: async ({ args, canvasElement, step }) => { + const canvas = within(canvasElement); + + await step( + 'Tuesday quick crossword is rendered initially', + async () => { + await waitFor(() => { + const clue = canvas.getByRole('listitem'); + return expect(clue.textContent).toEqual( + args.crosswords[0]?.entries[0]?.clue, + ); + }); + }, + ); + }, +} satisfies Story; + +export const AnotherTimezone = { + ...UKTimezone, + args: { + ...UKTimezone.args, + timeZone: 'Australia/Sydney', + }, +}; diff --git a/dotcom-rendering/src/components/Crosswords.editions.tsx b/dotcom-rendering/src/components/Crosswords.editions.tsx new file mode 100644 index 00000000000..dadaade8628 --- /dev/null +++ b/dotcom-rendering/src/components/Crosswords.editions.tsx @@ -0,0 +1,94 @@ +import { useEffect, useState } from 'react'; +import { + type CrosswordsByDate, + type FEEditionsCrossword, + groupByDate, +} from '../types/editionsCrossword'; +import { CrosswordSelect } from './CrosswordSelect.editions'; + +type Props = { + crosswords: FEEditionsCrossword[]; + /** + * Used to derive the dates by which to group the crosswords. If `undefined` + * it will use a side-effect to determine the local timezone. + */ + timeZone: string | undefined; +}; + +export const Crosswords = ({ crosswords, timeZone }: Props) => { + const [crosswordsByDate, setCrosswordsByDate] = useState< + CrosswordsByDate | undefined + >(undefined); + + useEffect(() => { + // This is side-effectful, it can depend on the user's timezone. + const formatter = Intl.DateTimeFormat('en-GB', { + year: 'numeric', + month: 'long', + day: '2-digit', + weekday: 'long', + timeZone, + }); + + setCrosswordsByDate(groupByDate(formatter)(crosswords)); + }, [crosswords, timeZone]); + + // Initially undefined until the effect runs. + if (crosswordsByDate === undefined) { + return null; + } + + const initialDate = Object.keys(crosswordsByDate)[0]; + + // If there are no dates then there are no crosswords. + if (initialDate === undefined) { + return null; + } + + return ( + + ); +}; + +type CrosswordsWithInitialDateProps = { + crosswordsByDate: CrosswordsByDate; + initialDate: string; +}; + +const CrosswordsWithInitialDate = ({ + crosswordsByDate, + initialDate, +}: CrosswordsWithInitialDateProps) => { + const [date, setDate] = + useState(initialDate); + const [crosswordIndex, setCrosswordIndex] = useState(0); + + const crossword = crosswordsByDate[date]?.at(crosswordIndex); + + return ( + <> + { + setDate(newDate); + // When selecting a new date, display the first crossword + // in the list for that date. + setCrosswordIndex(0); + }} + crosswordIndex={crosswordIndex} + onCrosswordIndexChange={setCrosswordIndex} + /> + {crossword === undefined ? null : ( +
    + {crossword.entries.map((entry) => ( +
  • {entry.clue}
  • + ))} +
+ )} + + ); +}; diff --git a/dotcom-rendering/src/model/editions-crossword-schema.json b/dotcom-rendering/src/model/editions-crossword-schema.json index 82e1a0b11b2..330df0bc36b 100644 --- a/dotcom-rendering/src/model/editions-crossword-schema.json +++ b/dotcom-rendering/src/model/editions-crossword-schema.json @@ -1,346 +1,178 @@ { "type": "object", "properties": { - "quick": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "number": { - "type": "number" - }, - "date": { - "type": "string" - }, - "dimensions": { - "type": "object", - "properties": { - "cols": { - "type": "number" - }, - "rows": { - "type": "number" - } + "crosswords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "required": [ - "cols", - "rows" - ] - }, - "entries": { - "type": "array", - "items": { + "type": { + "type": "string" + }, + "number": { + "type": "number" + }, + "date": { + "type": "string" + }, + "dimensions": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "number": { + "cols": { "type": "number" }, - "humanNumber": { - "type": "string" - }, - "direction": { - "enum": [ - "across", - "down" - ], - "type": "string" - }, - "position": { - "type": "object", - "properties": { - "x": { - "type": "number" - }, - "y": { - "type": "number" - } - }, - "required": [ - "x", - "y" - ] - }, - "separatorLocations": { - "type": "object", - "properties": { - ",": { - "type": "array", - "items": { - "type": "number" - } - }, - "-": { - "type": "array", - "items": { - "type": "number" - } - } - } - }, - "length": { + "rows": { "type": "number" - }, - "clue": { - "type": "string" - }, - "group": { - "type": "array", - "items": { - "type": "string" - } - }, - "solution": { - "type": "string" - }, - "format": { - "type": "string" } }, "required": [ - "clue", - "direction", - "format", - "group", - "humanNumber", - "id", - "length", - "number", - "position", - "separatorLocations", - "solution" + "cols", + "rows" ] - } - }, - "solutionAvailable": { - "type": "boolean" - }, - "hasNumbers": { - "type": "boolean" - }, - "randomCluesOrdering": { - "type": "boolean" - }, - "instructions": { - "type": "string" - }, - "creator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "webUrl": { - "type": "string" - } - }, - "required": [ - "name", - "webUrl" - ] - }, - "pdf": { - "type": "string" - }, - "annotatedSolution": { - "type": "string" - }, - "dateSolutionAvailable": { - "type": "string" - } - }, - "required": [ - "date", - "dateSolutionAvailable", - "dimensions", - "entries", - "hasNumbers", - "name", - "number", - "pdf", - "randomCluesOrdering", - "solutionAvailable", - "type" - ] - }, - "cryptic": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "number": { - "type": "number" - }, - "date": { - "type": "string" - }, - "dimensions": { - "type": "object", - "properties": { - "cols": { - "type": "number" - }, - "rows": { - "type": "number" - } }, - "required": [ - "cols", - "rows" - ] - }, - "entries": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "number": { - "type": "number" - }, - "humanNumber": { - "type": "string" - }, - "direction": { - "enum": [ - "across", - "down" - ], - "type": "string" - }, - "position": { - "type": "object", - "properties": { - "x": { - "type": "number" - }, - "y": { - "type": "number" - } + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "required": [ - "x", - "y" - ] - }, - "separatorLocations": { - "type": "object", - "properties": { - ",": { - "type": "array", - "items": { + "number": { + "type": "number" + }, + "humanNumber": { + "type": "string" + }, + "direction": { + "enum": [ + "across", + "down" + ], + "type": "string" + }, + "position": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { "type": "number" } }, - "-": { - "type": "array", - "items": { - "type": "number" + "required": [ + "x", + "y" + ] + }, + "separatorLocations": { + "type": "object", + "properties": { + ",": { + "type": "array", + "items": { + "type": "number" + } + }, + "-": { + "type": "array", + "items": { + "type": "number" + } } } - } - }, - "length": { - "type": "number" - }, - "clue": { - "type": "string" - }, - "group": { - "type": "array", - "items": { + }, + "length": { + "type": "number" + }, + "clue": { + "type": "string" + }, + "group": { + "type": "array", + "items": { + "type": "string" + } + }, + "solution": { + "type": "string" + }, + "format": { "type": "string" } }, - "solution": { + "required": [ + "clue", + "direction", + "group", + "humanNumber", + "id", + "length", + "number", + "position", + "separatorLocations", + "solution" + ] + } + }, + "solutionAvailable": { + "type": "boolean" + }, + "hasNumbers": { + "type": "boolean" + }, + "randomCluesOrdering": { + "type": "boolean" + }, + "instructions": { + "type": "string" + }, + "creator": { + "type": "object", + "properties": { + "name": { "type": "string" }, - "format": { + "webUrl": { "type": "string" } }, "required": [ - "clue", - "direction", - "format", - "group", - "humanNumber", - "id", - "length", - "number", - "position", - "separatorLocations", - "solution" + "name", + "webUrl" ] - } - }, - "solutionAvailable": { - "type": "boolean" - }, - "hasNumbers": { - "type": "boolean" - }, - "randomCluesOrdering": { - "type": "boolean" - }, - "instructions": { - "type": "string" - }, - "creator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "webUrl": { - "type": "string" - } }, - "required": [ - "name", - "webUrl" - ] - }, - "pdf": { - "type": "string" - }, - "annotatedSolution": { - "type": "string" + "pdf": { + "type": "string" + }, + "annotatedSolution": { + "type": "string" + }, + "dateSolutionAvailable": { + "type": "string" + } }, - "dateSolutionAvailable": { - "type": "string" - } - }, - "required": [ - "date", - "dateSolutionAvailable", - "dimensions", - "entries", - "hasNumbers", - "name", - "number", - "pdf", - "randomCluesOrdering", - "solutionAvailable", - "type" - ] + "required": [ + "date", + "dateSolutionAvailable", + "dimensions", + "entries", + "hasNumbers", + "name", + "number", + "randomCluesOrdering", + "solutionAvailable", + "type" + ] + } } }, "required": [ - "cryptic", - "quick" + "crosswords" ], "$schema": "http://json-schema.org/draft-07/schema#" } \ No newline at end of file diff --git a/dotcom-rendering/src/types/editionsCrossword.ts b/dotcom-rendering/src/types/editionsCrossword.ts index 7950e21f2e9..af2b57e31f8 100644 --- a/dotcom-rendering/src/types/editionsCrossword.ts +++ b/dotcom-rendering/src/types/editionsCrossword.ts @@ -1,6 +1,5 @@ export type FEEditionsCrosswords = { - quick: FEEditionsCrossword; - cryptic: FEEditionsCrossword; + crosswords: FEEditionsCrossword[]; }; type FECrosswordEntry = { @@ -17,7 +16,7 @@ type FECrosswordEntry = { clue: string; group: string[]; solution: string; - format: string; + format?: string; }; type FECrosswordDimensions = { @@ -37,7 +36,52 @@ export type FEEditionsCrossword = { randomCluesOrdering: boolean; instructions?: string; creator?: { name: string; webUrl: string }; - pdf: string; + pdf?: string; annotatedSolution?: string; dateSolutionAvailable: string; }; + +export type CrosswordsByDate = Record; + +/** + * Groups crosswords by their date, and drops any crosswords that have an + * invalid date. + * + * @param formatter The `DateTimeFormat` used to generate the dates used for + * the keys of the object. This is a parameter because it often depends on the + * runtime being used (e.g. to set the timezone). + * @example + * const formatter = Intl.DateTimeFormat('en-GB', { + * year: 'numeric', + * month: 'long', + * day: '2-digit', + * weekday: 'long', + * }); + * @param crosswords A list of crosswords to be grouped + * @returns An object where the keys are a representation of the dates the + * crosswords were published + * @example + * { + * "Wednesday 20 November 2024": [crosswordOne, crosswordTwo], + * "Thursday 21 November 2024": [crosswordThree], + * } + */ +export const groupByDate = + (formatter: Intl.DateTimeFormat) => + (crosswords: FEEditionsCrossword[]): CrosswordsByDate => + crosswords.reduce((crosswordsByDate, crossword) => { + const date = new Date(crossword.date); + + if (date.toString() === 'Invalid Date') { + return crosswordsByDate; + } + + const key = formatter.format(date); + const existingEntry = crosswordsByDate[key]; + const newEntry = + existingEntry === undefined + ? [crossword] + : [...existingEntry, crossword]; + + return { ...crosswordsByDate, [key]: newEntry }; + }, {}); From f64ae0f7c4eda0e63e45e3752207289a8022df85 Mon Sep 17 00:00:00 2001 From: Simon Byford Date: Wed, 27 Nov 2024 09:00:38 +0000 Subject: [PATCH 03/84] Use h3 for titles when mini profiles are sectioned --- dotcom-rendering/src/components/MiniProfile.tsx | 16 +++++++++++++--- .../src/components/MiniProfiles.stories.tsx | 14 ++++++++++++++ dotcom-rendering/src/components/MiniProfiles.tsx | 3 +++ dotcom-rendering/src/lib/ArticleRenderer.tsx | 8 ++++++++ dotcom-rendering/src/lib/renderElement.tsx | 5 +++++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/components/MiniProfile.tsx b/dotcom-rendering/src/components/MiniProfile.tsx index c0e6d842f07..fd3c64dee79 100644 --- a/dotcom-rendering/src/components/MiniProfile.tsx +++ b/dotcom-rendering/src/components/MiniProfile.tsx @@ -5,6 +5,7 @@ import type { ArticleFormat } from '../lib/articleFormat'; import { slugify } from '../model/enhance-H2s'; import { palette } from '../palette'; import type { MiniProfile as MiniProfileModel } from '../types/content'; +import { Heading } from './Heading'; import { headingLineStyles } from './KeyTakeaway'; import { subheadingStyles } from './Subheading'; @@ -75,27 +76,36 @@ const headingMarginStyle = css` margin-bottom: ${space[2]}px; `; +const sectionedTitleStyle = css` + font-size: 1.5rem; +`; + interface MiniProfileProps { miniProfile: MiniProfileModel; format: ArticleFormat; children: React.ReactNode; + sectioned: boolean; } export const MiniProfile = ({ miniProfile, format, children, + sectioned, }: MiniProfileProps) => { return ( <>

  • -

    - {miniProfile.title} -

    + + {miniProfile.title} + + {children} {miniProfile.endNote ? ( diff --git a/dotcom-rendering/src/components/MiniProfiles.stories.tsx b/dotcom-rendering/src/components/MiniProfiles.stories.tsx index afabcba7008..8a2c65e479b 100644 --- a/dotcom-rendering/src/components/MiniProfiles.stories.tsx +++ b/dotcom-rendering/src/components/MiniProfiles.stories.tsx @@ -75,6 +75,7 @@ export const ThemeVariations = { pageId: 'testID', switches: {}, RenderArticleElement, + sectioned: false, }, decorators: [centreColumnDecorator], parameters: { @@ -204,3 +205,16 @@ export const WithSeparatorLine = { }, decorators: [centreColumnDecorator], } satisfies Story; + +export const Sectioned = { + args: { + ...ThemeVariations.args, + sectioned: true, + format: { + design: ArticleDesign.Standard, + display: ArticleDisplay.Standard, + theme: Pillar.Culture, + }, + }, + decorators: [centreColumnDecorator], +} satisfies Story; diff --git a/dotcom-rendering/src/components/MiniProfiles.tsx b/dotcom-rendering/src/components/MiniProfiles.tsx index 186bc263220..5913ed513b2 100644 --- a/dotcom-rendering/src/components/MiniProfiles.tsx +++ b/dotcom-rendering/src/components/MiniProfiles.tsx @@ -25,6 +25,7 @@ interface MiniProfilesProps { * Whether this is the last element in the article. If true, no separator will be rendered. */ isLastElement: boolean; + sectioned: boolean; } const separatorStyles = css` @@ -48,6 +49,7 @@ export const MiniProfiles = ({ starRating, RenderArticleElement, isLastElement, + sectioned, }: MiniProfilesProps) => { return (
      @@ -57,6 +59,7 @@ export const MiniProfiles = ({ miniProfile={miniProfile} format={format} key={`${miniProfile.title}-${index}`} + sectioned={sectioned} > {miniProfile.body.map((element) => ( // eslint-disable-next-line react/jsx-key -- The element array should remain consistent as it's derived from the order of elements in CAPI diff --git a/dotcom-rendering/src/lib/ArticleRenderer.tsx b/dotcom-rendering/src/lib/ArticleRenderer.tsx index af8aa5166e4..85af008e9f5 100644 --- a/dotcom-rendering/src/lib/ArticleRenderer.tsx +++ b/dotcom-rendering/src/lib/ArticleRenderer.tsx @@ -56,6 +56,13 @@ export const ArticleRenderer = ({ abTests, editionId, }: Props) => { + const isSectionedMiniProfilesArticle = + elements.filter( + (element) => + element._type === + 'model.dotcomrendering.pageElements.MiniProfilesBlockElement', + ).length > 1; + const renderedElements = elements.map((element, index, { length }) => { return ( ); }); diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index be18547e755..a220303bfc8 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -91,6 +91,7 @@ type Props = { isTimeline?: boolean; totalElements?: number; isListElement?: boolean; + isSectionedMiniProfilesArticle?: boolean; }; // updateRole modifies the role of an element in a way appropriate for most @@ -150,6 +151,7 @@ export const renderElement = ({ isTimeline = false, totalElements = 0, isListElement = false, + isSectionedMiniProfilesArticle = false, }: Props) => { const isBlog = format.design === ArticleDesign.LiveBlog || @@ -490,6 +492,7 @@ export const renderElement = ({ editionId={editionId} RenderArticleElement={RenderArticleElement} isLastElement={index === totalElements - 1} + sectioned={!!isSectionedMiniProfilesArticle} /> ); case 'model.dotcomrendering.pageElements.MultiImageBlockElement': @@ -891,6 +894,7 @@ export const RenderArticleElement = ({ isTimeline, totalElements, isListElement, + isSectionedMiniProfilesArticle, }: Props) => { const withUpdatedRole = updateRole(element, format); @@ -915,6 +919,7 @@ export const RenderArticleElement = ({ isTimeline, totalElements, isListElement, + isSectionedMiniProfilesArticle, }); const needsFigure = !bareElements.has(element._type); From eb58f1dfea19d060017dd570d12e7a04be7a42e2 Mon Sep 17 00:00:00 2001 From: Simon Byford Date: Wed, 27 Nov 2024 09:41:17 +0000 Subject: [PATCH 04/84] Don't show separator for sectioned mini profiles --- dotcom-rendering/src/components/MiniProfiles.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/MiniProfiles.tsx b/dotcom-rendering/src/components/MiniProfiles.tsx index 5913ed513b2..b0778ac8f50 100644 --- a/dotcom-rendering/src/components/MiniProfiles.tsx +++ b/dotcom-rendering/src/components/MiniProfiles.tsx @@ -51,6 +51,8 @@ export const MiniProfiles = ({ isLastElement, sectioned, }: MiniProfilesProps) => { + const displaySeparator = !isLastElement && !sectioned; + return (
        {miniProfiles.map((miniProfile, index) => ( @@ -85,7 +87,7 @@ export const MiniProfiles = ({ ))} ))} - {!isLastElement &&
        } + {displaySeparator &&
        }
      ); }; From 31a5c116c3551d722387737fb1fdda0ff546425a Mon Sep 17 00:00:00 2001 From: Simon Byford Date: Wed, 27 Nov 2024 09:45:13 +0000 Subject: [PATCH 05/84] Don't render empty bios --- dotcom-rendering/src/components/MiniProfile.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/MiniProfile.tsx b/dotcom-rendering/src/components/MiniProfile.tsx index fd3c64dee79..ca1597a61e1 100644 --- a/dotcom-rendering/src/components/MiniProfile.tsx +++ b/dotcom-rendering/src/components/MiniProfile.tsx @@ -116,8 +116,16 @@ export const MiniProfile = ({ ); }; +const containsText = (html: string) => { + const htmlWithoutTags = sanitise(html, { + allowedTags: [], + allowedAttributes: {}, + }); + return htmlWithoutTags.length > 0; +}; + const Bio = ({ html }: { html?: string }) => { - if (!html) return null; + if (!html || !containsText(html)) return null; const sanitizedHtml = sanitise(html, {}); return ( <> From a0c09e1c3e9f06d17f332ee1967ba595d559d5a4 Mon Sep 17 00:00:00 2001 From: Charlotte Emms <43961396+cemms1@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:33:03 +0000 Subject: [PATCH 06/84] remove sublinks background for cards in containers with special palettes applied (#12941) --- .../src/components/ContainerOverrides.tsx | 55 +------------------ 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/dotcom-rendering/src/components/ContainerOverrides.tsx b/dotcom-rendering/src/components/ContainerOverrides.tsx index e1d566bb356..32b0517178b 100644 --- a/dotcom-rendering/src/components/ContainerOverrides.tsx +++ b/dotcom-rendering/src/components/ContainerOverrides.tsx @@ -464,63 +464,12 @@ const cardBorderTopDark: ContainerFunction = (containerPalette) => { }; const cardSublinksBackgroundLight: ContainerFunction = (containerPalette) => { - switch (containerPalette) { - case 'LongRunningPalette': - return sourcePalette.neutral[93]; - case 'LongRunningAltPalette': - return sourcePalette.neutral[86]; - case 'SombrePalette': - return sourcePalette.neutral[38]; - case 'SombreAltPalette': - return sourcePalette.neutral[20]; - case 'InvestigationPalette': - return sourcePalette.neutral[46]; - case 'BreakingPalette': - return sourcePalette.news[100]; - case 'EventPalette': - return sourcePalette.neutral[86]; - case 'EventAltPalette': - return sourcePalette.neutral[93]; - case 'SpecialReportAltPalette': - return sourcePalette.neutral[86]; - case 'Branded': - return sourcePalette.neutral[86]; - case 'MediaPalette': - return sourcePalette.neutral[46]; - case 'PodcastPalette': - return sourcePalette.neutral[86]; - } + return cardBackgroundLight(containerPalette); }; const cardSublinksBackgroundDark: ContainerFunction = (containerPalette) => { - switch (containerPalette) { - case 'LongRunningPalette': - return sourcePalette.brand[100]; - case 'LongRunningAltPalette': - return sourcePalette.neutral[10]; - case 'SombrePalette': - return sourcePalette.neutral[20]; - case 'SombreAltPalette': - return sourcePalette.neutral[10]; - case 'BreakingPalette': - return sourcePalette.news[100]; - case 'InvestigationPalette': - return sourcePalette.neutral[38]; - case 'EventPalette': - return sourcePalette.neutral[10]; - case 'EventAltPalette': - return sourcePalette.neutral[10]; - case 'SpecialReportAltPalette': - return sourcePalette.neutral[20]; - case 'Branded': - return sourcePalette.neutral[10]; - case 'MediaPalette': - return sourcePalette.neutral[46]; - case 'PodcastPalette': - return sourcePalette.neutral[86]; - } + return cardBackgroundDark(containerPalette); }; - const articleBorderLight: ContainerFunction = (containerPalette) => { switch (containerPalette) { case 'LongRunningPalette': From e1b58b30293d200041e86d3a29863cb32e0eba11 Mon Sep 17 00:00:00 2001 From: Jake Date: Tue, 3 Dec 2024 15:43:06 +0000 Subject: [PATCH 07/84] fix the labs header text colour (#12940) --- dotcom-rendering/src/components/LabsHeader.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotcom-rendering/src/components/LabsHeader.tsx b/dotcom-rendering/src/components/LabsHeader.tsx index 78f68367b7d..77076f444b8 100644 --- a/dotcom-rendering/src/components/LabsHeader.tsx +++ b/dotcom-rendering/src/components/LabsHeader.tsx @@ -24,6 +24,8 @@ const FlexWrapper = ({ children }: { children: React.ReactNode }) => ( height: ${LABS_HEADER_HEIGHT}px; display: flex; justify-content: space-between; + + color: ${palette.neutral[7]}; `} > {children} From 90debfb631c93a2dece846c820e1a4f6cd5b0bfe Mon Sep 17 00:00:00 2001 From: Georges Lebreton <102960844+Georges-GNM@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:14:35 +0000 Subject: [PATCH 08/84] Use aspect ratio for new containers (#12897) * Add aspect ratio to model * Specify aspect ratio as optional * replace AspectRatio type previously from CardPicture with the one defined in types/front * Pass aspect ratio to flex general * Specify aspect ratio in flex gen stories * Provide a fallback aspect ratio to avoid it being undefined * Pass aspect ratio to flex special * Pass aspect ratio to scrollable feature * Pass aspect ratio to scrollable medium * Pass aspect ratio to scrollable small * Pass aspect ratio to static feature 2 * Pass aspect ratio to static medium 4 * update comment explaining aspect ratio in card.tsx * Use nullish coalescing rather than a ternary to define aspect ratio --- dotcom-rendering/src/components/Card/Card.tsx | 2 +- .../src/components/DecideContainer.tsx | 10 +++++ .../components/FlexibleGeneral.stories.tsx | 1 + .../src/components/FlexibleGeneral.tsx | 18 +++++++-- .../components/FlexibleSpecial.stories.tsx | 1 + .../src/components/FlexibleSpecial.tsx | 14 ++++++- .../ScrollableFeature.importable.tsx | 10 ++++- .../components/ScrollableFeature.stories.tsx | 1 + .../ScrollableMedium.importable.tsx | 5 ++- .../components/ScrollableMedium.stories.tsx | 1 + .../components/ScrollableSmall.importable.tsx | 5 ++- .../components/ScrollableSmall.stories.tsx | 1 + .../components/StaticFeatureTwo.stories.tsx | 1 + .../src/components/StaticFeatureTwo.tsx | 10 ++++- .../components/StaticMediumFour.stories.tsx | 1 + .../src/components/StaticMediumFour.tsx | 10 ++++- dotcom-rendering/src/layouts/FrontLayout.tsx | 40 ++++++++++++++++++- 17 files changed, 116 insertions(+), 15 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 2b5533028ef..66f8391a64a 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -117,7 +117,7 @@ export type Props = { pauseOffscreenVideo?: boolean; showMainVideo?: boolean; isTagPage?: boolean; - /** Alows the consumer to set an aspect ratio on the image of 5:3 or 5:4 */ + /** Allows the consumer to set an aspect ratio on the image of 5:3, 5:4, 4:5 or 1:1 */ aspectRatio?: AspectRatio; index?: number; /** The Splash card in a flexible container gets a different visual treatment to other cards*/ diff --git a/dotcom-rendering/src/components/DecideContainer.tsx b/dotcom-rendering/src/components/DecideContainer.tsx index d78cc30a7e6..1a60b3a4f8a 100644 --- a/dotcom-rendering/src/components/DecideContainer.tsx +++ b/dotcom-rendering/src/components/DecideContainer.tsx @@ -1,5 +1,6 @@ import type { ImgHTMLAttributes } from 'react'; import type { + AspectRatio, DCRContainerPalette, DCRContainerType, DCRFrontCard, @@ -41,6 +42,7 @@ type Props = { containerPalette?: DCRContainerPalette; showAge?: boolean; absoluteServerTimes: boolean; + aspectRatio: AspectRatio; }; export const DecideContainer = ({ @@ -51,6 +53,7 @@ export const DecideContainer = ({ showAge, absoluteServerTimes, imageLoading, + aspectRatio, }: Props) => { // If you add a new container type which contains an MPU, you must also add it to switch (containerType) { @@ -242,6 +245,7 @@ export const DecideContainer = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); case 'flexible/general': @@ -252,6 +256,7 @@ export const DecideContainer = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); case 'scrollable/small': @@ -264,6 +269,7 @@ export const DecideContainer = ({ containerPalette={containerPalette} showAge={showAge} absoluteServerTimes={absoluteServerTimes} + aspectRatio={aspectRatio} /> ); @@ -277,6 +283,7 @@ export const DecideContainer = ({ containerPalette={containerPalette} showAge={showAge} absoluteServerTimes={absoluteServerTimes} + aspectRatio={aspectRatio} /> ); @@ -288,6 +295,7 @@ export const DecideContainer = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); case 'scrollable/feature': @@ -298,6 +306,7 @@ export const DecideContainer = ({ imageLoading={imageLoading} containerPalette={containerPalette} absoluteServerTimes={absoluteServerTimes} + aspectRatio={aspectRatio} /> ); @@ -308,6 +317,7 @@ export const DecideContainer = ({ containerPalette={containerPalette} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); default: diff --git a/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx b/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx index 98ba581a44e..6923c0d3488 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx @@ -145,6 +145,7 @@ const meta = { showAge: true, absoluteServerTimes: true, imageLoading: 'eager', + aspectRatio: '5:4', }, render: ({ frontSectionTitle, ...args }) => ( { const card = cards[0]; if (!card) return null; @@ -202,7 +206,7 @@ export const SplashCardLayout = ({ : supportingContentAlignment } imageLoading={imageLoading} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={card.showLivePlayable} liveUpdatesAlignment={liveUpdatesAlignment} @@ -262,12 +266,14 @@ export const BoostedCardLayout = ({ showAge, absoluteServerTimes, imageLoading, + aspectRatio, }: { cards: DCRFrontCard[]; imageLoading: Loading; containerPalette?: DCRContainerPalette; showAge?: boolean; absoluteServerTimes: boolean; + aspectRatio: AspectRatio; }) => { const card = cards[0]; if (!card) return null; @@ -302,7 +308,7 @@ export const BoostedCardLayout = ({ : supportingContentAlignment } imageLoading={imageLoading} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={card.showLivePlayable} liveUpdatesAlignment="horizontal" @@ -323,6 +329,7 @@ export const StandardCardLayout = ({ showImage = true, imageLoading, isFirstRow, + aspectRatio, }: { cards: DCRFrontCard[]; imageLoading: Loading; @@ -331,6 +338,7 @@ export const StandardCardLayout = ({ showAge?: boolean; absoluteServerTimes: boolean; showImage?: boolean; + aspectRatio: AspectRatio; }) => { if (cards.length === 0) return null; @@ -370,7 +378,7 @@ export const StandardCardLayout = ({ supportingContentAlignment="vertical" supportingContentPosition="outer" imageSize={'medium'} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={false} showTopBarDesktop={false} @@ -400,6 +408,7 @@ export const FlexibleGeneral = ({ showAge, absoluteServerTimes, imageLoading, + aspectRatio, }: Props) => { const splash = [...groupedTrails.splash].slice(0, 1); const cards = [...groupedTrails.standard].slice(0, 8); @@ -414,6 +423,7 @@ export const FlexibleGeneral = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> )} @@ -427,6 +437,7 @@ export const FlexibleGeneral = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); @@ -441,6 +452,7 @@ export const FlexibleGeneral = ({ absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} isFirstRow={i === 0} + aspectRatio={aspectRatio} /> ); } diff --git a/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx b/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx index b81c75cfcdf..257655d6f91 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx @@ -32,6 +32,7 @@ const meta = { showAge: true, absoluteServerTimes: true, imageLoading: 'eager', + aspectRatio: '5:4', }, render: (args) => ( { const card = cards[0]; if (!card) return null; @@ -149,7 +153,7 @@ export const OneCardLayout = ({ supportingContent={card.supportingContent} supportingContentAlignment={supportingContentAlignment} imageLoading={imageLoading} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={card.showLivePlayable} liveUpdatesAlignment={liveUpdatesAlignment} @@ -170,6 +174,7 @@ const TwoCardOrFourCardLayout = ({ absoluteServerTimes, showImage = true, imageLoading, + aspectRatio, }: { cards: DCRFrontCard[]; imageLoading: Loading; @@ -177,6 +182,7 @@ const TwoCardOrFourCardLayout = ({ showAge?: boolean; absoluteServerTimes: boolean; showImage?: boolean; + aspectRatio: AspectRatio; }) => { if (cards.length === 0) return null; const hasTwoOrFewerCards = cards.length <= 2; @@ -210,7 +216,7 @@ const TwoCardOrFourCardLayout = ({ /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} imageSize={'medium'} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={false} showTopBarDesktop={false} @@ -229,6 +235,7 @@ export const FlexibleSpecial = ({ showAge, absoluteServerTimes, imageLoading, + aspectRatio, }: Props) => { const snaps = [...groupedTrails.snap].slice(0, 1); const splash = [...groupedTrails.standard].slice(0, 1); @@ -242,6 +249,7 @@ export const FlexibleSpecial = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} + aspectRatio={aspectRatio} /> ); diff --git a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx index 253862e4f09..17ead5b6432 100644 --- a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx @@ -1,5 +1,9 @@ import { ArticleDesign } from '../lib/articleFormat'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; +import type { + AspectRatio, + DCRContainerPalette, + DCRFrontCard, +} from '../types/front'; import { FeatureCard } from './FeatureCard'; import { ScrollableCarousel } from './ScrollableCarousel'; @@ -8,6 +12,7 @@ type Props = { containerPalette?: DCRContainerPalette; absoluteServerTimes: boolean; imageLoading: 'lazy' | 'eager'; + aspectRatio: AspectRatio; }; /** @@ -22,6 +27,7 @@ export const ScrollableFeature = ({ containerPalette, absoluteServerTimes, imageLoading, + aspectRatio, }: Props) => { return ( ( { return ( ( { return ( ( ( { const cards = trails.slice(0, 2); @@ -61,7 +67,7 @@ export const StaticFeatureTwo = ({ trailText={card.trailText} absoluteServerTimes={absoluteServerTimes} imageLoading={imageLoading} - aspectRatio="4:5" + aspectRatio={aspectRatio} imageSize="feature-large" headlineSizes={{ desktop: 'small' }} supportingContent={card.supportingContent} diff --git a/dotcom-rendering/src/components/StaticMediumFour.stories.tsx b/dotcom-rendering/src/components/StaticMediumFour.stories.tsx index 24e217d0170..0f5c249a357 100644 --- a/dotcom-rendering/src/components/StaticMediumFour.stories.tsx +++ b/dotcom-rendering/src/components/StaticMediumFour.stories.tsx @@ -23,6 +23,7 @@ const meta = { showAge: true, absoluteServerTimes: true, imageLoading: 'eager', + aspectRatio: '5:4', }, render: (args) => ( { const cards = trails.slice(0, 4); @@ -51,7 +57,7 @@ export const StaticMediumFour = ({ /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} imageSize={'medium'} - aspectRatio="5:4" + aspectRatio={aspectRatio} kickerText={card.kickerText} showLivePlayable={false} showTopBarDesktop={false} diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index c2a51609467..ccdb35945c7 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -45,7 +45,11 @@ import { import { hideAge } from '../lib/hideAge'; import type { NavType } from '../model/extract-nav'; import { palette as schemePalette } from '../palette'; -import { type DCRCollectionType, type DCRFrontType } from '../types/front'; +import type { + DCRCollectionType, + DCRContainerType, + DCRFrontType, +} from '../types/front'; import { pageSkinContainer } from './lib/pageSkin'; import { BannerWrapper, Stuck } from './lib/stickiness'; @@ -170,6 +174,24 @@ export const FrontLayout = ({ front, NAV }: Props) => { const { absoluteServerTimes = false } = front.config.switches; + const fallbackAspectRatio = (collectionType: DCRContainerType) => { + switch (collectionType) { + case 'scrollable/feature': + case 'static/feature/2': + return '4:5'; + case 'flexible/general': + case 'flexible/special': + case 'scrollable/small': + case 'scrollable/medium': + case 'static/medium/4': + return '5:4'; + case 'scrollable/highlights': + return '1:1'; + default: + return '5:3'; + } + }; + const Highlights = () => { const showHighlights = // Must be opted into the Europe beta test or in preview @@ -191,6 +213,10 @@ export const FrontLayout = ({ front, NAV }: Props) => { showAge={false} absoluteServerTimes={absoluteServerTimes} imageLoading="eager" + aspectRatio={ + highlightsCollection.aspectRatio ?? + fallbackAspectRatio(highlightsCollection.collectionType) + } /> ) ); @@ -531,6 +557,12 @@ export const FrontLayout = ({ front, NAV }: Props) => { absoluteServerTimes={ absoluteServerTimes } + aspectRatio={ + collection.aspectRatio ?? + fallbackAspectRatio( + collection.collectionType, + ) + } /> {decideMerchHighAndMobileAdSlots( @@ -696,6 +728,12 @@ export const FrontLayout = ({ front, NAV }: Props) => { } imageLoading={imageLoading} absoluteServerTimes={absoluteServerTimes} + aspectRatio={ + collection.aspectRatio ?? + fallbackAspectRatio( + collection.collectionType, + ) + } /> {decideMerchHighAndMobileAdSlots( From a43da03900c0fad3cf2cc4ac9549c8bec779100c Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Wed, 4 Dec 2024 13:58:16 +0000 Subject: [PATCH 09/84] Remove liveblog sticky ask --- dotcom-rendering/src/layouts/LiveLayout.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/dotcom-rendering/src/layouts/LiveLayout.tsx b/dotcom-rendering/src/layouts/LiveLayout.tsx index 58f9e6cb261..af3edf9cf46 100644 --- a/dotcom-rendering/src/layouts/LiveLayout.tsx +++ b/dotcom-rendering/src/layouts/LiveLayout.tsx @@ -45,7 +45,6 @@ import { Section } from '../components/Section'; import { Standfirst } from '../components/Standfirst'; import { StarRating } from '../components/StarRating/StarRating'; import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; -import { StickyLiveblogAskWrapper } from '../components/StickyLiveblogAskWrapper.importable'; import { SubMeta } from '../components/SubMeta'; import { SubNav } from '../components/SubNav.importable'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; @@ -717,22 +716,6 @@ export const LiveLayout = (props: WebProps | AppsProps) => { - {isWeb && ( - - - - - - )} - {/* Match stats */} {!!footballMatchUrl && ( Date: Wed, 4 Dec 2024 14:19:56 +0000 Subject: [PATCH 10/84] Improve tracking of expands in AB test --- .../ExpandableMarketingCardSwipeable.tsx | 25 +++++++++++++++++-- ...andableMarketingCardWrapper.importable.tsx | 19 ++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx b/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx index c7c662cd258..eced3875e84 100644 --- a/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx +++ b/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx @@ -1,8 +1,9 @@ import { css } from '@emotion/react'; import { type Dispatch, type SetStateAction, useEffect, useState } from 'react'; -import { getOphan } from '../client/ophan/ophan'; +import { getOphan, submitComponentEvent } from '../client/ophan/ophan'; import { getZIndex } from '../lib/getZIndex'; import { ExpandableMarketingCard } from './ExpandableMarketingCard'; +import type { UsBannerTestVariantName } from './ExpandableMarketingCardWrapper.importable'; // The minimum length of the left swipe on the x-axis to close the banner const LEFT_SWIPE_THRESHOLD_PX = 20; @@ -25,6 +26,7 @@ interface Props { isExpanded: boolean; setIsExpanded: Dispatch>; setIsClosed: Dispatch>; + abTestVariant: UsBannerTestVariantName; } const hideBannerStyles = css` @@ -86,6 +88,7 @@ export const ExpandableMarketingCardSwipeable = ({ isExpanded, setIsExpanded, setIsClosed, + abTestVariant, }: Props) => { const [lastDownXCoord, setLastDownXCoord] = useState(null); const [lastDownYCoord, setLastDownYCoord] = useState(null); @@ -153,6 +156,25 @@ export const ExpandableMarketingCardSwipeable = ({ } }, [shouldDisplayCard]); + useEffect(() => { + if (isExpanded) { + void submitComponentEvent( + { + component: { + componentType: 'CONTAINER', + id: 'us-expandable-marketing-card', + }, + action: 'EXPAND', + abTest: { + name: 'UsaExpandableMarketingCard', + variant: abTestVariant, + }, + }, + 'Web', + ); + } + }, [abTestVariant, isExpanded]); + if (!shouldDisplayCard) { return null; } @@ -164,7 +186,6 @@ export const ExpandableMarketingCardSwipeable = ({ css={[stickyContainerStyles]} >
      { +const getVariant = ( + abTestAPI: ABTestAPI | undefined, +): UsBannerTestVariantName | null => { if (!abTestAPI) { return null; } @@ -60,7 +67,7 @@ const getVariant = (abTestAPI: ABTestAPI | undefined): VariantName | null => { return null; }; -const getVariantCopy = (variant: VariantName): Variant => { +const getVariantCopy = (variant: UsBannerTestVariantName): Variant => { if (variant === 'variant-free') { return variantFree; } @@ -138,11 +145,6 @@ export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => { onClick={() => { !isExpanded && setIsExpanded(true); }} - data-link-name={ - !isExpanded - ? 'us-expandable-marketing-card expand' - : undefined - } > { isExpanded={isExpanded} setIsExpanded={setIsExpanded} setIsClosed={setIsClosed} + abTestVariant={abTestVariant} /> From ab953f0b105f03b4eac8344a16c6b2effc946a5d Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Tue, 3 Dec 2024 12:28:53 +0000 Subject: [PATCH 11/84] Remove branding from cards in collection.grouped when there's duplicate branding --- dotcom-rendering/src/layouts/FrontLayout.tsx | 45 +++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index ccdb35945c7..3ae49d120cc 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -327,6 +327,49 @@ export const FrontLayout = ({ front, NAV }: Props) => { })) : trails; + // We also need to remove the branding for the cards in grouped + // trails for dynamic containers + const groupedWithoutBranding = isPaidContentSameBranding( + collection.collectionBranding, + ) + ? { + snap: collection.grouped.snap.map( + (labTrail) => ({ + ...labTrail, + branding: undefined, + }), + ), + huge: collection.grouped.huge.map( + (labTrail) => ({ + ...labTrail, + branding: undefined, + }), + ), + veryBig: collection.grouped.veryBig.map( + (labTrail) => ({ + ...labTrail, + branding: undefined, + }), + ), + big: collection.grouped.big.map((labTrail) => ({ + ...labTrail, + branding: undefined, + })), + standard: collection.grouped.standard.map( + (labTrail) => ({ + ...labTrail, + branding: undefined, + }), + ), + splash: collection.grouped.splash.map( + (labTrail) => ({ + ...labTrail, + branding: undefined, + }), + ), + } + : collection.grouped; + if (collection.collectionType === 'scrollable/highlights') { // Highlights are rendered in the Masthead component return null; @@ -716,7 +759,7 @@ export const FrontLayout = ({ front, NAV }: Props) => { > Date: Wed, 4 Dec 2024 14:15:54 +0000 Subject: [PATCH 12/84] Make function more concise --- dotcom-rendering/src/layouts/FrontLayout.tsx | 65 ++++++++------------ 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index 3ae49d120cc..d29dd7c3333 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -49,6 +49,7 @@ import type { DCRCollectionType, DCRContainerType, DCRFrontType, + DCRGroupedTrails, } from '../types/front'; import { pageSkinContainer } from './lib/pageSkin'; import { BannerWrapper, Stuck } from './lib/stickiness'; @@ -329,46 +330,34 @@ export const FrontLayout = ({ front, NAV }: Props) => { // We also need to remove the branding for the cards in grouped // trails for dynamic containers - const groupedWithoutBranding = isPaidContentSameBranding( - collection.collectionBranding, - ) - ? { - snap: collection.grouped.snap.map( - (labTrail) => ({ - ...labTrail, - branding: undefined, - }), - ), - huge: collection.grouped.huge.map( - (labTrail) => ({ - ...labTrail, - branding: undefined, - }), - ), - veryBig: collection.grouped.veryBig.map( - (labTrail) => ({ + const groupedWithoutBranding: DCRGroupedTrails = (() => { + if ( + isPaidContentSameBranding( + collection.collectionBranding, + ) + ) { + const groupedTrailsWithoutBranding: DCRGroupedTrails = + { + snap: [], + huge: [], + veryBig: [], + big: [], + standard: [], + splash: [], + }; + for (const key of Object.keys( + collection.grouped, + ) as (keyof DCRGroupedTrails)[]) { + groupedTrailsWithoutBranding[key] = + collection.grouped[key].map((labTrail) => ({ ...labTrail, branding: undefined, - }), - ), - big: collection.grouped.big.map((labTrail) => ({ - ...labTrail, - branding: undefined, - })), - standard: collection.grouped.standard.map( - (labTrail) => ({ - ...labTrail, - branding: undefined, - }), - ), - splash: collection.grouped.splash.map( - (labTrail) => ({ - ...labTrail, - branding: undefined, - }), - ), - } - : collection.grouped; + })); + } + return groupedTrailsWithoutBranding; + } + return collection.grouped; + })(); if (collection.collectionType === 'scrollable/highlights') { // Highlights are rendered in the Masthead component From 75779f8491d7bf837c4892fa84e13422f12f7ef7 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 21 Nov 2024 09:51:53 +0000 Subject: [PATCH 13/84] Add AB test --- dotcom-rendering/src/experiments/ab-tests.ts | 2 ++ .../tests/onwards-content-article.ts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 dotcom-rendering/src/experiments/tests/onwards-content-article.ts diff --git a/dotcom-rendering/src/experiments/ab-tests.ts b/dotcom-rendering/src/experiments/ab-tests.ts index 89624262880..f00fb644ad0 100644 --- a/dotcom-rendering/src/experiments/ab-tests.ts +++ b/dotcom-rendering/src/experiments/ab-tests.ts @@ -4,6 +4,7 @@ import { adBlockAsk } from './tests/ad-block-ask'; import { consentlessAds } from './tests/consentless-ads'; import { integrateIma } from './tests/integrate-ima'; import { mpuWhenNoEpic } from './tests/mpu-when-no-epic'; +import { onwardsContentArticle } from './tests/onwards-content-article'; import { optimiseSpacefinderInline } from './tests/optimise-spacefinder-inline'; import { signInGateMainControl } from './tests/sign-in-gate-main-control'; import { signInGateMainVariant } from './tests/sign-in-gate-main-variant'; @@ -21,4 +22,5 @@ export const tests: ABTest[] = [ adBlockAsk, optimiseSpacefinderInline, UsaExpandableMarketingCard, + onwardsContentArticle, ]; diff --git a/dotcom-rendering/src/experiments/tests/onwards-content-article.ts b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts new file mode 100644 index 00000000000..2035d0f73b6 --- /dev/null +++ b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts @@ -0,0 +1,30 @@ +import type { ABTest } from '@guardian/ab-core'; + +export const onwardsContentArticle: ABTest = { + id: 'onwardsContentArticle', + start: '2024-11-25', + expiry: '2024-01-29', + author: 'dotcom.platform@guardian.co.uk', + description: + 'Test the impact of showing the galleries onwards content component on article pages.', + audience: 0 / 100, + audienceOffset: 0 / 100, + audienceCriteria: 'Article pages', + successMeasure: + 'Users are more likely to click a link in the onward content component.', + canRun: () => true, + variants: [ + { + id: 'control', + test: (): void => { + /* no-op */ + }, + }, + { + id: 'variant', + test: (): void => { + /* no-op */ + }, + }, + ], +}; From be09abc7a5abb99ac7ce33fe0c010910240d503c Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Wed, 4 Dec 2024 10:14:23 +0000 Subject: [PATCH 14/84] Onwards content gallery-style test --- .../src/components/BigSixOnwardsContent.tsx | 194 ++++++++++++++++++ .../src/components/Carousel.importable.tsx | 2 + .../components/OnwardsUpper.importable.tsx | 25 ++- .../tests/onwards-content-article.ts | 6 +- dotcom-rendering/src/types/front.ts | 10 +- 5 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 dotcom-rendering/src/components/BigSixOnwardsContent.tsx diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx new file mode 100644 index 00000000000..6e421abde0e --- /dev/null +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -0,0 +1,194 @@ +import { css } from '@emotion/react'; +import { from, headlineBold24, space } from '@guardian/source/foundations'; +import { decideFormat } from '../lib/articleFormat'; +import { useApi } from '../lib/useApi'; +import { palette } from '../palette'; +import type { DCRFrontCard } from '../types/front'; +import type { FETrailType } from '../types/trails'; +import { Card } from './Card/Card'; +import { LI } from './Card/components/LI'; +import { UL } from './Card/components/UL'; +import { LeftColumn } from './LeftColumn'; +import { Placeholder } from './Placeholder'; + +type Props = { url: string; discussionApiUrl: string }; + +type OnwardsResponse = { + trails: FETrailType[]; + heading: string; + displayname: string; + description: string; +}; + +const containerStyles = css` + display: flex; + flex-direction: column; + padding-bottom: ${space[6]}px; + + ${from.leftCol} { + flex-direction: row; + padding-right: 80px; + } +`; + +const headerStyles = css` + ${headlineBold24}; + color: ${palette('--carousel-text')}; + padding-bottom: ${space[2]}px; + padding-top: ${space[1]}px; + margin-left: 0; +`; +const mobileHeaderStyles = css` + ${headerStyles}; + ${from.tablet} { + padding-left: 10px; + } + ${from.leftCol} { + display: none; + } +`; + +const convertFETrailToDcrTrail = ( + trails: FETrailType[], + discussionApiUrl: string, +): DCRFrontCard[] => + trails.map((trail) => ({ + dataLinkName: 'onwards-content-card', + discussionId: trail.discussion?.discussionId, + discussionApiUrl, + format: decideFormat(trail.format), + headline: trail.headline, + image: { + src: trail.masterImage ?? '', + altText: trail.linkText ?? '', + }, + isExternalLink: false, + onwardsSource: 'related-content', + showLivePlayable: false, + showQuotedHeadline: false, + url: trail.url, + webPublicationDate: trail.webPublicationDate, + })); + +export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { + const { data, error } = useApi(url); + + if (error) { + // Send the error to Sentry and then prevent the element from rendering + window.guardian.modules.sentry.reportError(error, 'onwards-lower'); + return null; + } + + if (!data?.trails) { + return ( + + ); + } + + const trails: DCRFrontCard[] = convertFETrailToDcrTrail( + data.trails, + discussionApiUrl, + ); + + const firstSlice75 = trails.slice(0, 1); + const firstSlice25 = trails.slice(1, 2); + const secondSlice25 = trails.slice(2, 6); + + return ( +
      + +

      + {data.heading} +

      +
      +

      + {data.heading} +

      +
      +
        + {firstSlice75.map((trail) => ( +
      • + +
      • + ))} + {firstSlice25.map((trail) => ( +
      • + +
      • + ))} +
      +
        + {secondSlice25.map((trail, index) => ( +
      • 0} + > + +
      • + ))} +
      +
      +
      + ); +}; diff --git a/dotcom-rendering/src/components/Carousel.importable.tsx b/dotcom-rendering/src/components/Carousel.importable.tsx index 3f60618c868..7d3bc6f8dff 100644 --- a/dotcom-rendering/src/components/Carousel.importable.tsx +++ b/dotcom-rendering/src/components/Carousel.importable.tsx @@ -453,6 +453,7 @@ const Title = ({ {title} ); + type CarouselCardProps = { isFirst: boolean; index: number; @@ -500,6 +501,7 @@ const CarouselCard = ({ }: CarouselCardProps) => { const isVideoContainer = containerType === 'fixed/video'; const cardImagePosition = isOnwardContent ? 'bottom' : 'top'; + return (
    1. { + const abTestAPI = useAB()?.api; + const isInOnwardsAbTestVariant = abTestAPI?.isUserInVariant( + 'OnwardsContentArticle', + 'variant', + ); + const isAndroid = useIsAndroid(); if (isAndroid) return null; @@ -260,10 +268,12 @@ export const OnwardsUpper = ({ // --- Tag excludes --- // const tagsToExclude = []; + // Exclude ad features from non-ad feature content if (!isPaidContent) { tagsToExclude.push('tone/advertisement-features'); } + // We don't want to show professional network content on videos or interactives if ( contentType.toLowerCase() === 'video' || @@ -296,7 +306,18 @@ export const OnwardsUpper = ({ return (
      - {!!url && ( + {!!url && isInOnwardsAbTestVariant && ( +
      + +
      + )} + {!!url && !isInOnwardsAbTestVariant && (
      )} - {!!(!isPaidContent && curatedDataUrl) && ( + {!isPaidContent && curatedDataUrl !== undefined && (
      Date: Wed, 4 Dec 2024 10:33:06 +0000 Subject: [PATCH 15/84] add tracking --- .../src/components/BigSixOnwardsContent.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx index 6e421abde0e..3aad237b4e9 100644 --- a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -11,8 +11,6 @@ import { UL } from './Card/components/UL'; import { LeftColumn } from './LeftColumn'; import { Placeholder } from './Placeholder'; -type Props = { url: string; discussionApiUrl: string }; - type OnwardsResponse = { trails: FETrailType[]; heading: string; @@ -70,6 +68,12 @@ const convertFETrailToDcrTrail = ( webPublicationDate: trail.webPublicationDate, })); +type Props = { url: string; discussionApiUrl: string }; + +/** + * Big Six refers to the style of the onwards content container. It displays six article + * cards in a gallery-style container, as opposed to a carousel. + */ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { const { data, error } = useApi(url); @@ -99,7 +103,10 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { const secondSlice25 = trails.slice(2, 6); return ( -
      +

      {data.heading} @@ -122,7 +129,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { format={trail.format} headlineText={trail.headline} image={trail.image} - dataLinkName={trail.dataLinkName} + dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}`} discussionId={trail.discussionId} discussionApiUrl={trail.discussionApiUrl} isExternalLink={trail.isExternalLink} @@ -149,7 +156,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { format={trail.format} headlineText={trail.headline} image={trail.image} - dataLinkName={trail.dataLinkName} + dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}`} discussionId={trail.discussionId} discussionApiUrl={trail.discussionApiUrl} isExternalLink={trail.isExternalLink} @@ -177,7 +184,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { format={trail.format} headlineText={trail.headline} image={trail.image} - dataLinkName={trail.dataLinkName} + dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}`} discussionId={trail.discussionId} discussionApiUrl={trail.discussionApiUrl} isExternalLink={trail.isExternalLink} From 23ddc99e14e9bee6164520f4e090ab0e7d1a1eb6 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 5 Dec 2024 08:02:46 +0000 Subject: [PATCH 16/84] Fix heading --- dotcom-rendering/src/components/BigSixOnwardsContent.tsx | 6 ++++-- dotcom-rendering/src/components/OnwardsUpper.importable.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx index 3aad237b4e9..8e88cbad039 100644 --- a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -102,6 +102,8 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { const firstSlice25 = trails.slice(1, 2); const secondSlice25 = trails.slice(2, 6); + const heading = data.heading || data.displayname; + return (
      { >

      - {data.heading} + {heading}

      - {data.heading} + {heading}

        diff --git a/dotcom-rendering/src/components/OnwardsUpper.importable.tsx b/dotcom-rendering/src/components/OnwardsUpper.importable.tsx index 3bee7e0c8fb..888f0156ff7 100644 --- a/dotcom-rendering/src/components/OnwardsUpper.importable.tsx +++ b/dotcom-rendering/src/components/OnwardsUpper.importable.tsx @@ -332,7 +332,7 @@ export const OnwardsUpper = ({ />

      )} - {!isPaidContent && curatedDataUrl !== undefined && ( + {!!curatedDataUrl && !isPaidContent && (
      Date: Thu, 5 Dec 2024 09:27:55 +0000 Subject: [PATCH 17/84] Readd Liveblog sticky ask --- dotcom-rendering/src/layouts/LiveLayout.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dotcom-rendering/src/layouts/LiveLayout.tsx b/dotcom-rendering/src/layouts/LiveLayout.tsx index af3edf9cf46..58f9e6cb261 100644 --- a/dotcom-rendering/src/layouts/LiveLayout.tsx +++ b/dotcom-rendering/src/layouts/LiveLayout.tsx @@ -45,6 +45,7 @@ import { Section } from '../components/Section'; import { Standfirst } from '../components/Standfirst'; import { StarRating } from '../components/StarRating/StarRating'; import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; +import { StickyLiveblogAskWrapper } from '../components/StickyLiveblogAskWrapper.importable'; import { SubMeta } from '../components/SubMeta'; import { SubNav } from '../components/SubNav.importable'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; @@ -716,6 +717,22 @@ export const LiveLayout = (props: WebProps | AppsProps) => {
      + {isWeb && ( + + + + + + )} + {/* Match stats */} {!!footballMatchUrl && ( Date: Thu, 5 Dec 2024 12:31:28 +0000 Subject: [PATCH 18/84] Add tracking for swipe. Rename tracking of close button to distinguish form swipe --- .../components/ExpandableMarketingCard.tsx | 2 +- .../ExpandableMarketingCardSwipeable.tsx | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/ExpandableMarketingCard.tsx b/dotcom-rendering/src/components/ExpandableMarketingCard.tsx index abb170c077c..dcaaacce00c 100644 --- a/dotcom-rendering/src/components/ExpandableMarketingCard.tsx +++ b/dotcom-rendering/src/components/ExpandableMarketingCard.tsx @@ -253,7 +253,7 @@ export const ExpandableMarketingCard = ({

      {heading}

      + ); +}; diff --git a/dotcom-rendering/src/components/ScrollableCarousel.tsx b/dotcom-rendering/src/components/ScrollableCarousel.tsx index 28b1a55b183..714cabba628 100644 --- a/dotcom-rendering/src/components/ScrollableCarousel.tsx +++ b/dotcom-rendering/src/components/ScrollableCarousel.tsx @@ -6,14 +6,10 @@ import { space, textSansBold17Object, } from '@guardian/source/foundations'; -import type { ThemeButton } from '@guardian/source/react-components'; -import { - Button, - SvgChevronLeftSingle, - SvgChevronRightSingle, -} from '@guardian/source/react-components'; import { useEffect, useRef, useState } from 'react'; +import { nestedOphanComponents } from '../lib/ophan-helpers'; import { palette } from '../palette'; +import { CarouselNavigationButtons } from './CarouselNavigationButtons'; type Props = { children: React.ReactNode; @@ -39,18 +35,6 @@ const gridColumnWidth = 60; const gridGap = 20; const gridGapMobile = 10; -const themeButton: Partial = { - borderTertiary: palette('--carousel-chevron-border'), - textTertiary: palette('--carousel-chevron'), - backgroundTertiaryHover: palette('--carousel-chevron-hover'), -}; - -const themeButtonDisabled: Partial = { - borderTertiary: palette('--carousel-chevron-border-disabled'), - textTertiary: palette('--carousel-chevron-disabled'), - backgroundTertiaryHover: 'transparent', -}; - /** * On mobile the carousel extends into the outer margins to use the full width * of the screen. From tablet onwards the carousel sits within the page grid. @@ -161,15 +145,6 @@ const carouselStyles = css` } `; -const buttonStyles = css` - display: none; - ${from.tablet} { - display: flex; - gap: ${space[1]}px; - margin-left: auto; - } -`; - const itemStyles = css` display: flex; scroll-snap-align: start; @@ -346,44 +321,22 @@ export const ScrollableCarousel = ({ > {children}
    - {showNavigation && ( -
    -
    + {showNavigation && ( + scrollTo('left')} + onClickNextButton={() => scrollTo('right')} + dataLinkNamePreviousButton={nestedOphanComponents( + 'carousel', + 'previous-button', + )} + dataLinkNameNextButton={nestedOphanComponents( + 'carousel', + 'next-button', + )} + /> )} ); From 206ff282861886cb08611635faa065a522dc6308 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:05:53 +0000 Subject: [PATCH 55/84] Bump webpack and fix dev server --- apps-rendering/package.json | 6 +- dotcom-rendering/package.json | 4 +- .../webpack/webpack.config.dev-server.js | 1 + pnpm-lock.yaml | 460 +++++++++--------- 4 files changed, 237 insertions(+), 234 deletions(-) diff --git a/apps-rendering/package.json b/apps-rendering/package.json index 50d61212527..6f42b8c8fa4 100644 --- a/apps-rendering/package.json +++ b/apps-rendering/package.json @@ -86,7 +86,7 @@ "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-react": "7.33.2", "express": "4.21.0", - "html-webpack-plugin": "5.6.0", + "html-webpack-plugin": "5.6.3", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jsdom": "16.7.0", @@ -103,9 +103,9 @@ "tslib": "2.6.2", "tsx": "4.6.2", "typescript": "5.5.3", - "webpack": "5.94.0", + "webpack": "5.97.1", "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.1.0", "webpack-manifest-plugin": "5.0.0", "whatwg-fetch": "3.6.19", "winston": "3.11.0", diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 100e4cb551a..9905ae938bc 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -191,12 +191,12 @@ "unified": "11.0.5", "valibot": "0.28.1", "web-vitals": "4.2.3", - "webpack": "5.94.0", + "webpack": "5.97.1", "webpack-assets-manifest": "5.2.1", "webpack-bundle-analyzer": "4.10.2", "webpack-cli": "5.1.4", "webpack-dev-middleware": "7.4.2", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.1.0", "webpack-hot-middleware": "2.26.1", "webpack-hot-server-middleware": "0.6.1", "webpack-manifest-plugin": "5.0.0", diff --git a/dotcom-rendering/webpack/webpack.config.dev-server.js b/dotcom-rendering/webpack/webpack.config.dev-server.js index c14f72a2f7b..d21805fcda5 100644 --- a/dotcom-rendering/webpack/webpack.config.dev-server.js +++ b/dotcom-rendering/webpack/webpack.config.dev-server.js @@ -34,6 +34,7 @@ module.exports = { devMiddleware: { publicPath: '/assets/', serverSideRender: true, + writeToDisk: true, headers: (req, res) => { // Allow any localhost request from accessing the assets if ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea34b01c925..2457e5e83f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,7 +102,7 @@ importers: version: 8.4.2(@types/react@18.3.1)(storybook@8.4.2) '@storybook/addon-webpack5-compiler-babel': specifier: 3.0.3 - version: 3.0.3(webpack@5.94.0) + version: 3.0.3(webpack@5.97.1) '@storybook/components': specifier: 8.4.2 version: 8.4.2(storybook@8.4.2) @@ -165,7 +165,7 @@ importers: version: 2.100.0(constructs@10.3.0) babel-loader: specifier: 9.2.1 - version: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + version: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) buffer: specifier: 6.0.3 version: 6.0.3 @@ -197,8 +197,8 @@ importers: specifier: 4.21.0 version: 4.21.0 html-webpack-plugin: - specifier: 5.6.0 - version: 5.6.0(webpack@5.94.0) + specifier: 5.6.3 + version: 5.6.3(webpack@5.97.1) jest: specifier: 29.7.0 version: 29.7.0(@types/node@18.18.14) @@ -237,7 +237,7 @@ importers: version: 29.1.2(@babel/core@7.26.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.5.3) ts-loader: specifier: 9.5.1 - version: 9.5.1(typescript@5.5.3)(webpack@5.94.0) + version: 9.5.1(typescript@5.5.3)(webpack@5.97.1) tslib: specifier: 2.6.2 version: 2.6.2 @@ -248,17 +248,17 @@ importers: specifier: 5.5.3 version: 5.5.3 webpack: - specifier: 5.94.0 - version: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + specifier: 5.97.1 + version: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 - version: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + version: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-server: - specifier: 5.0.4 - version: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + specifier: 5.1.0 + version: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-manifest-plugin: specifier: 5.0.0 - version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0) + version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1) whatwg-fetch: specifier: 3.6.19 version: 3.6.19 @@ -396,7 +396,7 @@ importers: version: 8.4.2(storybook@8.4.2) '@storybook/addon-webpack5-compiler-swc': specifier: 1.0.5 - version: 1.0.5(webpack@5.94.0) + version: 1.0.5(webpack@5.97.1) '@storybook/components': specifier: 8.4.2 version: 8.4.2(storybook@8.4.2) @@ -558,7 +558,7 @@ importers: version: 2.100.0(constructs@10.3.0) babel-loader: specifier: 9.2.1 - version: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + version: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) babel-plugin-polyfill-corejs3: specifier: 0.10.6 version: 0.10.6(@babel/core@7.26.0) @@ -594,7 +594,7 @@ importers: version: 11.0.0 css-loader: specifier: 7.1.2 - version: 7.1.2(webpack@5.94.0) + version: 7.1.2(webpack@5.97.1) curlyquotes: specifier: 1.5.5 version: 1.5.5 @@ -744,7 +744,7 @@ importers: version: 14.0.0(stylelint@16.5.0) swc-loader: specifier: 0.2.6 - version: 0.2.6(@swc/core@1.9.2)(webpack@5.94.0) + version: 0.2.6(@swc/core@1.9.2)(webpack@5.97.1) swr: specifier: 1.3.0 version: 1.3.0(react@18.3.1) @@ -759,7 +759,7 @@ importers: version: 2.0.0 ts-loader: specifier: 9.5.1 - version: 9.5.1(typescript@5.5.3)(webpack@5.94.0) + version: 9.5.1(typescript@5.5.3)(webpack@5.97.1) ts-unused-exports: specifier: 10.1.0 version: 10.1.0(typescript@5.5.3) @@ -788,32 +788,32 @@ importers: specifier: 4.2.3 version: 4.2.3 webpack: - specifier: 5.94.0 - version: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + specifier: 5.97.1 + version: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-assets-manifest: specifier: 5.2.1 - version: 5.2.1(webpack@5.94.0) + version: 5.2.1(webpack@5.97.1) webpack-bundle-analyzer: specifier: 4.10.2 version: 4.10.2 webpack-cli: specifier: 5.1.4 - version: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0) + version: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-middleware: specifier: 7.4.2 - version: 7.4.2(webpack@5.94.0) + version: 7.4.2(webpack@5.97.1) webpack-dev-server: - specifier: 5.0.4 - version: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + specifier: 5.1.0 + version: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-hot-middleware: specifier: 2.26.1 version: 2.26.1 webpack-hot-server-middleware: specifier: 0.6.1 - version: 0.6.1(webpack@5.94.0) + version: 0.6.1(webpack@5.97.1) webpack-manifest-plugin: specifier: 5.0.0 - version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0) + version: 5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1) webpack-merge: specifier: 6.0.1 version: 6.0.1 @@ -5865,23 +5865,23 @@ packages: storybook: 8.4.2(prettier@3.0.3) dev: false - /@storybook/addon-webpack5-compiler-babel@3.0.3(webpack@5.94.0): + /@storybook/addon-webpack5-compiler-babel@3.0.3(webpack@5.97.1): resolution: {integrity: sha512-rVQTTw+oxJltbVKaejIWSHwVKOBJs3au21f/pYXhV0aiNgNhxEa3vr79t/j0j8ox8uJtzM8XYOb7FlkvGfHlwQ==} engines: {node: '>=18'} dependencies: '@babel/core': 7.26.0 - babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.94.0) + babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) transitivePeerDependencies: - supports-color - webpack dev: false - /@storybook/addon-webpack5-compiler-swc@1.0.5(webpack@5.94.0): + /@storybook/addon-webpack5-compiler-swc@1.0.5(webpack@5.97.1): resolution: {integrity: sha512-1NlM3noit2vA22OyWb8Ma2lhcEKCS1Snv2kr+EkaVABUqNDfVc9AD/GgYQhF7F/2CoF5N2JU7uzXDzFHd5TzZg==} engines: {node: '>=18'} dependencies: '@swc/core': 1.9.2 - swc-loader: 0.2.6(@swc/core@1.9.2)(webpack@5.94.0) + swc-loader: 0.2.6(@swc/core@1.9.2)(webpack@5.97.1) transitivePeerDependencies: - '@swc/helpers' - webpack @@ -5923,24 +5923,24 @@ packages: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.2.3 constants-browserify: 1.0.0 - css-loader: 6.10.0(webpack@5.94.0) + css-loader: 6.10.0(webpack@5.97.1) es-module-lexer: 1.5.3 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.94.0) - html-webpack-plugin: 5.6.0(webpack@5.94.0) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.97.1) + html-webpack-plugin: 5.6.3(webpack@5.97.1) magic-string: 0.30.5 path-browserify: 1.0.1 process: 0.11.10 semver: 7.5.4 storybook: 8.4.2(prettier@3.0.3) - style-loader: 3.3.3(webpack@5.94.0) - terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0) + style-loader: 3.3.3(webpack@5.97.1) + terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1) ts-dedent: 2.2.0 typescript: 5.5.3 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-middleware: 6.1.3(webpack@5.94.0) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 6.1.3(webpack@5.97.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.1 transitivePeerDependencies: @@ -5967,24 +5967,24 @@ packages: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.2.3 constants-browserify: 1.0.0 - css-loader: 6.10.0(webpack@5.94.0) + css-loader: 6.10.0(webpack@5.97.1) es-module-lexer: 1.5.3 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.94.0) - html-webpack-plugin: 5.6.0(webpack@5.94.0) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.5.3)(webpack@5.97.1) + html-webpack-plugin: 5.6.3(webpack@5.97.1) magic-string: 0.30.5 path-browserify: 1.0.1 process: 0.11.10 semver: 7.5.4 storybook: 8.4.2(prettier@3.0.3) - style-loader: 3.3.3(webpack@5.94.0) - terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.94.0) + style-loader: 3.3.3(webpack@5.97.1) + terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.97.1) ts-dedent: 2.2.0 typescript: 5.5.3 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-middleware: 6.1.3(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 6.1.3(webpack@5.97.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.1 transitivePeerDependencies: @@ -6109,7 +6109,7 @@ packages: dependencies: '@storybook/core-webpack': 8.4.2(storybook@8.4.2) '@storybook/react': 8.4.2(@storybook/test@8.4.2)(react-dom@18.3.1)(react@18.3.1)(storybook@8.4.2)(typescript@5.5.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1) '@types/node': 22.8.0 '@types/semver': 7.5.6 find-up: 5.0.0 @@ -6122,7 +6122,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) tsconfig-paths: 4.2.0 typescript: 5.5.3 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -6146,7 +6146,7 @@ packages: dependencies: '@storybook/core-webpack': 8.4.2(storybook@8.4.2) '@storybook/react': 8.4.2(@storybook/test@8.4.2)(react-dom@18.3.1)(react@18.3.1)(storybook@8.4.2)(typescript@5.5.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1) '@types/node': 22.8.0 '@types/semver': 7.5.6 find-up: 5.0.0 @@ -6159,7 +6159,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) tsconfig-paths: 4.2.0 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -6177,7 +6177,7 @@ packages: storybook: 8.4.2(prettier@3.0.3) dev: false - /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.94.0): + /@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==} peerDependencies: typescript: '>= 4.x' @@ -6191,7 +6191,7 @@ packages: react-docgen-typescript: 2.2.2(typescript@5.5.3) tslib: 2.6.2 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color dev: false @@ -6851,10 +6851,28 @@ packages: '@types/trusted-types': 2.0.7 dev: false + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + dev: false + + /@types/eslint@9.6.1: + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + dev: false + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: false + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: false + /@types/express-serve-static-core@4.17.41: resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} dependencies: @@ -7232,7 +7250,7 @@ packages: dependencies: '@types/node': 20.14.10 tapable: 2.2.1 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -7248,7 +7266,7 @@ packages: resolution: {integrity: sha512-8Z3/edqxE3RRlOJwKSgOFxLZRt/i1qFlv/Bi308ZUKo9jh8oGngd9r8GR0ZNKW5AEJq8QNQE3b17CwghTjQ0Uw==} dependencies: '@types/node': 20.14.10 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -7618,135 +7636,135 @@ packages: tinyrainbow: 1.2.0 dev: false - /@webassemblyjs/ast@1.12.1: - resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + /@webassemblyjs/ast@1.14.1: + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} dependencies: - '@webassemblyjs/helper-numbers': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 dev: false - /@webassemblyjs/floating-point-hex-parser@1.11.6: - resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + /@webassemblyjs/floating-point-hex-parser@1.13.2: + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} dev: false - /@webassemblyjs/helper-api-error@1.11.6: - resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + /@webassemblyjs/helper-api-error@1.13.2: + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} dev: false - /@webassemblyjs/helper-buffer@1.12.1: - resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + /@webassemblyjs/helper-buffer@1.14.1: + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} dev: false - /@webassemblyjs/helper-numbers@1.11.6: - resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + /@webassemblyjs/helper-numbers@1.13.2: + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 '@xtuc/long': 4.2.2 dev: false - /@webassemblyjs/helper-wasm-bytecode@1.11.6: - resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + /@webassemblyjs/helper-wasm-bytecode@1.13.2: + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} dev: false - /@webassemblyjs/helper-wasm-section@1.12.1: - resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + /@webassemblyjs/helper-wasm-section@1.14.1: + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 dev: false - /@webassemblyjs/ieee754@1.11.6: - resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + /@webassemblyjs/ieee754@1.13.2: + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} dependencies: '@xtuc/ieee754': 1.2.0 dev: false - /@webassemblyjs/leb128@1.11.6: - resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + /@webassemblyjs/leb128@1.13.2: + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} dependencies: '@xtuc/long': 4.2.2 dev: false - /@webassemblyjs/utf8@1.11.6: - resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + /@webassemblyjs/utf8@1.13.2: + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} dev: false - /@webassemblyjs/wasm-edit@1.12.1: - resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + /@webassemblyjs/wasm-edit@1.14.1: + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-opt': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - '@webassemblyjs/wast-printer': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 dev: false - /@webassemblyjs/wasm-gen@1.12.1: - resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + /@webassemblyjs/wasm-gen@1.14.1: + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 dev: false - /@webassemblyjs/wasm-opt@1.12.1: - resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + /@webassemblyjs/wasm-opt@1.14.1: + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 dev: false - /@webassemblyjs/wasm-parser@1.12.1: - resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + /@webassemblyjs/wasm-parser@1.14.1: + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-api-error': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 dev: false - /@webassemblyjs/wast-printer@1.12.1: - resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + /@webassemblyjs/wast-printer@1.14.1: + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} dependencies: - '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 dev: false - /@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.94.0): + /@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.97.1): resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false - /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.94.0): + /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.97.1): resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false - /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0): + /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} engines: {node: '>=14.15.0'} peerDependencies: @@ -7757,9 +7775,9 @@ packages: webpack-dev-server: optional: true dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) dev: false /@xhmikosr/archive-type@7.0.0: @@ -7895,14 +7913,6 @@ packages: acorn-walk: 8.3.1 dev: false - /acorn-import-attributes@1.9.5(acorn@8.11.2): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.11.2 - dev: false - /acorn-jsx@5.3.2(acorn@8.11.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -7933,6 +7943,12 @@ packages: hasBin: true dev: false + /acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -8358,7 +8374,7 @@ packages: - supports-color dev: false - /babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.94.0): + /babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.97.1): resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -8368,7 +8384,7 @@ packages: '@babel/core': 7.26.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /babel-plugin-istanbul@6.1.1: @@ -9511,7 +9527,7 @@ packages: engines: {node: '>=12 || >=16'} dev: false - /css-loader@6.10.0(webpack@5.94.0): + /css-loader@6.10.0(webpack@5.97.1): resolution: {integrity: sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -9531,10 +9547,10 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /css-loader@7.1.2(webpack@5.94.0): + /css-loader@7.1.2(webpack@5.97.1): resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} engines: {node: '>= 18.12.0'} peerDependencies: @@ -9554,7 +9570,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.39) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /css-select@4.3.0: @@ -9849,13 +9865,6 @@ packages: default-browser-id: 5.0.0 dev: false - /default-gateway@6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} - dependencies: - execa: 5.1.1 - dev: false - /defaults@3.0.0: resolution: {integrity: sha512-RsqXDEAALjfRTro+IFNKpcPCt0/Cy2FqHSIlnomiJp9YGadpQnrtbRpSgN2+np21qHcIKiva4fiOQGjS9/qR/A==} engines: {node: '>=18'} @@ -11463,7 +11472,7 @@ packages: signal-exit: 4.1.0 dev: false - /fork-ts-checker-webpack-plugin@8.0.0(typescript@5.5.3)(webpack@5.94.0): + /fork-ts-checker-webpack-plugin@8.0.0(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} engines: {node: '>=12.13.0', yarn: '>=1.0.0'} peerDependencies: @@ -11483,7 +11492,7 @@ packages: semver: 7.5.4 tapable: 2.2.1 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /form-data-encoder@2.1.4: @@ -12154,8 +12163,8 @@ packages: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} dev: false - /html-webpack-plugin@5.6.0(webpack@5.94.0): - resolution: {integrity: sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==} + /html-webpack-plugin@5.6.3(webpack@5.97.1): + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} engines: {node: '>=10.13.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -12171,7 +12180,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /htmlparser2@6.1.0: @@ -16662,13 +16671,13 @@ packages: peek-readable: 5.3.1 dev: false - /style-loader@3.3.3(webpack@5.94.0): + /style-loader@3.3.3(webpack@5.97.1): resolution: {integrity: sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /stylelint-config-recommended@14.0.0(stylelint@16.5.0): @@ -16797,7 +16806,7 @@ packages: picocolors: 1.1.1 dev: false - /swc-loader@0.2.6(@swc/core@1.9.2)(webpack@5.94.0): + /swc-loader@0.2.6(@swc/core@1.9.2)(webpack@5.97.1): resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==} peerDependencies: '@swc/core': ^1.2.147 @@ -16805,7 +16814,7 @@ packages: dependencies: '@swc/core': 1.9.2 '@swc/counter': 0.1.3 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /swr@1.3.0(react@18.3.1): @@ -16844,7 +16853,7 @@ packages: streamx: 2.21.0 dev: false - /terser-webpack-plugin@5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0): + /terser-webpack-plugin@5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16867,10 +16876,10 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.26.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /terser-webpack-plugin@5.3.10(esbuild@0.18.20)(webpack@5.94.0): + /terser-webpack-plugin@5.3.10(esbuild@0.18.20)(webpack@5.97.1): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16892,7 +16901,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.26.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /terser@5.26.0: @@ -17141,7 +17150,7 @@ packages: yargs-parser: 21.1.1 dev: false - /ts-loader@9.5.1(typescript@5.5.3)(webpack@5.94.0): + /ts-loader@9.5.1(typescript@5.5.3)(webpack@5.97.1): resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -17154,7 +17163,7 @@ packages: semver: 7.5.4 source-map: 0.7.4 typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /ts-node@10.9.2(@swc/core@1.9.2)(@types/node@16.18.68)(typescript@5.1.6): @@ -17807,7 +17816,7 @@ packages: engines: {node: '>=12'} dev: false - /webpack-assets-manifest@5.2.1(webpack@5.94.0): + /webpack-assets-manifest@5.2.1(webpack@5.97.1): resolution: {integrity: sha512-MsEcXVio1GY6R+b4dVfTHIDMB0RB90KajQG8neRbH92vE2S1ClGw9mNa9NPlratYBvZOhExmN0qqMNFTaCTuIg==} engines: {node: '>=10.13.0'} peerDependencies: @@ -17820,7 +17829,7 @@ packages: lodash.has: 4.5.2 schema-utils: 3.3.0 tapable: 2.2.1 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /webpack-bundle-analyzer@4.10.2: @@ -17845,7 +17854,7 @@ packages: - utf-8-validate dev: false - /webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0): + /webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} engines: {node: '>=14.15.0'} hasBin: true @@ -17863,9 +17872,9 @@ packages: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -17874,13 +17883,13 @@ packages: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-merge: 5.10.0 dev: false - /webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0): + /webpack-cli@5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1): resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} engines: {node: '>=14.15.0'} hasBin: true @@ -17898,9 +17907,9 @@ packages: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.94.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.0.4)(webpack@5.94.0) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.97.1) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -17909,12 +17918,12 @@ packages: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-dev-server: 5.0.4(webpack-cli@5.1.4)(webpack@5.94.0) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) webpack-merge: 5.10.0 dev: false - /webpack-dev-middleware@6.1.3(webpack@5.94.0): + /webpack-dev-middleware@6.1.3(webpack@5.97.1): resolution: {integrity: sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -17928,28 +17937,10 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /webpack-dev-middleware@7.2.1(webpack@5.94.0): - resolution: {integrity: sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - webpack: ^5.0.0 - peerDependenciesMeta: - webpack: - optional: true - dependencies: - colorette: 2.0.20 - memfs: 4.7.7 - mime-types: 2.1.35 - on-finished: 2.4.1 - range-parser: 1.2.1 - schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - dev: false - - /webpack-dev-middleware@7.4.2(webpack@5.94.0): + /webpack-dev-middleware@7.4.2(webpack@5.97.1): resolution: {integrity: sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==} engines: {node: '>= 18.12.0'} peerDependencies: @@ -17964,11 +17955,11 @@ packages: on-finished: 2.4.1 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false - /webpack-dev-server@5.0.4(webpack-cli@5.1.4)(webpack@5.94.0): - resolution: {integrity: sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==} + /webpack-dev-server@5.1.0(webpack-cli@5.1.4)(webpack@5.97.1): + resolution: {integrity: sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==} engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: @@ -17993,7 +17984,6 @@ packages: colorette: 2.0.20 compression: 1.7.4 connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 express: 4.21.0 graceful-fs: 4.2.11 html-entities: 2.4.0 @@ -18002,16 +17992,15 @@ packages: launch-editor: 2.6.1 open: 10.1.0 p-retry: 6.2.0 - rimraf: 5.0.5 schema-utils: 4.2.0 selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) - webpack-dev-middleware: 7.2.1(webpack@5.94.0) - ws: 8.17.1 + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack-dev-middleware: 7.4.2(webpack@5.97.1) + ws: 8.18.0 transitivePeerDependencies: - bufferutil - debug @@ -18034,7 +18023,7 @@ packages: strip-ansi: 6.0.1 dev: false - /webpack-hot-server-middleware@0.6.1(webpack@5.94.0): + /webpack-hot-server-middleware@0.6.1(webpack@5.97.1): resolution: {integrity: sha512-YOKwdS0hnmADsNCsReGkMOBkoz2YVrQZvnVcViM2TDXlK9NnaOGXmnrLFjzwsHFa0/iuJy/QJFEoMxzk8R1Mgg==} peerDependencies: webpack: '*' @@ -18042,19 +18031,19 @@ packages: debug: 3.2.7 require-from-string: 2.0.2 source-map-support: 0.5.21 - webpack: 5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color dev: false - /webpack-manifest-plugin@5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.94.0): + /webpack-manifest-plugin@5.0.0(patch_hash=zao44j4xgexeap52664hqknxfu)(webpack@5.97.1): resolution: {integrity: sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==} engines: {node: '>=12.22.0'} peerDependencies: webpack: ^5.47.0 dependencies: tapable: 2.2.1 - webpack: 5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 2.3.1 dev: false patched: true @@ -18108,8 +18097,8 @@ packages: resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==} dev: false - /webpack@5.94.0(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4): - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + /webpack@5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4): + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -18118,13 +18107,13 @@ packages: webpack-cli: optional: true dependencies: - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.2 - acorn-import-attributes: 1.9.5(acorn@8.11.2) - browserslist: 4.23.0 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 @@ -18138,9 +18127,9 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.94.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.9.2)(esbuild@0.18.20)(webpack@5.97.1) watchpack: 2.4.1 - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -18148,8 +18137,8 @@ packages: - uglify-js dev: false - /webpack@5.94.0(esbuild@0.18.20)(webpack-cli@5.1.4): - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + /webpack@5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4): + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -18158,13 +18147,13 @@ packages: webpack-cli: optional: true dependencies: - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.2 - acorn-import-attributes: 1.9.5(acorn@8.11.2) - browserslist: 4.23.0 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.3 @@ -18178,9 +18167,9 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.94.0) + terser-webpack-plugin: 5.3.10(esbuild@0.18.20)(webpack@5.97.1) watchpack: 2.4.1 - webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.94.0) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -18468,6 +18457,19 @@ packages: optional: true dev: false + /ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /xml-name-validator@3.0.0: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} dev: false From 36232eab577fcaa10fe52e0f4ba8af0a27be098b Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Wed, 11 Dec 2024 17:14:50 +0000 Subject: [PATCH 56/84] Remove decidePalette from CalloutEmbedBlock component --- .../CalloutEmbedBlockComponent.importable.tsx | 18 ++++------ .../CalloutEmbedBlockComponent.stories.tsx | 6 ---- dotcom-rendering/src/lib/decidePalette.ts | 13 ------- dotcom-rendering/src/lib/renderElement.tsx | 5 +-- dotcom-rendering/src/paletteDeclarations.ts | 35 +++++++++++++++++++ dotcom-rendering/src/types/palette.ts | 1 - 6 files changed, 42 insertions(+), 36 deletions(-) diff --git a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx index 8ca13f2f796..d6f5d4b14db 100644 --- a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx +++ b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.importable.tsx @@ -6,15 +6,13 @@ import { } from '@guardian/source/foundations'; import { Button } from '@guardian/source/react-components'; import { useEffect, useState } from 'react'; -import type { ArticleFormat } from '../lib/articleFormat'; -import { decidePalette } from '../lib/decidePalette'; +import { palette } from '../palette'; import MinusIcon from '../static/icons/minus.svg'; import PlusIcon from '../static/icons/plus.svg'; import type { CalloutBlockElement, CalloutBlockElementV2, } from '../types/content'; -import type { Palette } from '../types/palette'; import { Form } from './CalloutEmbed/Form'; const wrapperStyles = css` @@ -79,10 +77,10 @@ const summaryContentWrapper = css` flex-direction: row; `; -const speechBubbleStyles = (palette: Palette) => css` +const speechBubbleStyles = css` ${textSansBold17} color: ${srcPalette.neutral[100]}; - background-color: ${palette.background.speechBubble}; + background-color: ${palette('--speech-bubble-background')}; min-width: 88px; padding-bottom: 6px; padding-left: 10px; @@ -93,7 +91,7 @@ const speechBubbleStyles = (palette: Palette) => css` height: 22px; border-bottom-right-radius: 18px; position: absolute; - background-color: ${palette.background.speechBubble}; + background-color: ${palette('--speech-bubble-background')}; } `; @@ -147,10 +145,8 @@ type FormDataType = { [key in string]: unknown }; */ export const CalloutEmbedBlockComponent = ({ callout, - format, }: { callout: CalloutBlockElement | CalloutBlockElementV2; - format: ArticleFormat; }) => { let expandFormButtonRef: HTMLButtonElement | null = null; let firstFieldElementRef: HTMLElement | null = null; @@ -160,8 +156,6 @@ export const CalloutEmbedBlockComponent = ({ const [error, setError] = useState(''); const [submissionSuccess, setSubmissionSuccess] = useState(false); - const palette = decidePalette(format); - const { title, description, formFields } = callout; const onSubmit = async (formData: FormDataType) => { @@ -312,7 +306,7 @@ export const CalloutEmbedBlockComponent = ({
    -
    +

    Share your story

    @@ -338,7 +332,7 @@ export const CalloutEmbedBlockComponent = ({
    -
    +

    Share your story

    diff --git a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx index 3f6f711ab8e..16e144fe17c 100644 --- a/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/CalloutEmbedBlockComponent.stories.tsx @@ -2,7 +2,6 @@ import type { Decorator, Meta, StoryObj } from '@storybook/react'; import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators'; import { allModes } from '../../.storybook/modes'; import { calloutCampaign } from '../../fixtures/manual/calloutCampaign'; -import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { customMockFetch } from '../lib/mockRESTCalls'; import { CalloutEmbedBlockComponent as CalloutEmbedBlock } from './CalloutEmbedBlockComponent.importable'; @@ -39,11 +38,6 @@ const goodRequest: Decorator = (Story) => { export const CalloutEmbedBlockComponent = { args: { callout: calloutCampaign, - format: { - display: ArticleDisplay.Standard, - design: ArticleDesign.Standard, - theme: Pillar.News, - }, }, decorators: [goodRequest], } satisfies Story; diff --git a/dotcom-rendering/src/lib/decidePalette.ts b/dotcom-rendering/src/lib/decidePalette.ts index ea1e9ff98ea..a2ca4b6e860 100644 --- a/dotcom-rendering/src/lib/decidePalette.ts +++ b/dotcom-rendering/src/lib/decidePalette.ts @@ -138,18 +138,6 @@ const backgroundBulletStandfirst = (format: ArticleFormat): string => { return neutral[86]; // default previously defined in Standfirst.tsx }; -const backgroundSpeechBubble = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.Analysis) { - switch (format.theme) { - case Pillar.News: - return news[300]; - default: - return pillarPalette[format.theme].main; - } - } - return pillarPalette[format.theme].main; -}; - const backgroundFilterButtonHover = (format: ArticleFormat): string => { switch (format.theme) { case Pillar.News: @@ -439,7 +427,6 @@ export const decidePalette = (format: ArticleFormat): Palette => { analysisContrastHover: backgroundAnalysisContrastHoverColour(), bullet: backgroundBullet(format), bulletStandfirst: backgroundBulletStandfirst(format), - speechBubble: backgroundSpeechBubble(format), filterButton: backgroundFilterButton(), filterButtonHover: backgroundFilterButtonHover(format), filterButtonActive: backgroundFilterButtonActive(format), diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index a220303bfc8..cd0241d8f26 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -185,10 +185,7 @@ export const renderElement = ({ case 'model.dotcomrendering.pageElements.CalloutBlockElement': return ( - + ); case 'model.dotcomrendering.pageElements.CalloutBlockElementV2': diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 191d007e138..366039f3198 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -4892,6 +4892,37 @@ const staffPickBadgeTextLight: PaletteFunction = () => sourcePalette.neutral[100]; const staffPickBadgeTextDark: PaletteFunction = () => sourcePalette.neutral[7]; +const speechBubbleBackgroundLight: PaletteFunction = ({ theme, design }) => { + switch (theme) { + case Pillar.News: { + return design === ArticleDesign.Analysis + ? sourcePalette.news[300] + : sourcePalette.news[400]; + } + case Pillar.Opinion: { + return sourcePalette.opinion[400]; + } + case Pillar.Sport: { + return sourcePalette.sport[400]; + } + case Pillar.Culture: { + return sourcePalette.culture[400]; + } + case Pillar.Lifestyle: { + return sourcePalette.lifestyle[400]; + } + case ArticleSpecial.Labs: { + return sourcePalette.labs[400]; + } + case ArticleSpecial.SpecialReport: { + return sourcePalette.specialReport[400]; + } + case ArticleSpecial.SpecialReportAlt: { + return sourcePalette.specialReportAlt[200]; + } + } +}; + const staffBadgeLight: PaletteFunction = () => sourcePalette.brand[400]; const staffBadgeDark: PaletteFunction = () => sourcePalette.neutral[100]; @@ -6946,6 +6977,10 @@ const paletteColours = { light: slideshowPaginationDotActiveLight, dark: slideshowPaginationDotActiveDark, }, + '--speech-bubble-background': { + light: speechBubbleBackgroundLight, + dark: speechBubbleBackgroundLight, + }, '--staff-contributor-badge': { light: staffBadgeLight, dark: staffBadgeDark, diff --git a/dotcom-rendering/src/types/palette.ts b/dotcom-rendering/src/types/palette.ts index a462360ebd1..98a5fc70462 100644 --- a/dotcom-rendering/src/types/palette.ts +++ b/dotcom-rendering/src/types/palette.ts @@ -18,7 +18,6 @@ export type Palette = { analysisContrastHover: Colour; bullet: Colour; bulletStandfirst: Colour; - speechBubble: Colour; filterButton: Colour; filterButtonHover: Colour; filterButtonActive: Colour; From 19c945b09ff65ba613e112a6b2b5b6dc7607f0b9 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 12 Dec 2024 09:49:57 +0000 Subject: [PATCH 57/84] Include card index in data link name --- .../src/components/BigSixOnwardsContent.tsx | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx index 8e88cbad039..59b36a6b0ed 100644 --- a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -131,7 +131,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { format={trail.format} headlineText={trail.headline} image={trail.image} - dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}`} + dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}-position-0`} discussionId={trail.discussionId} discussionApiUrl={trail.discussionApiUrl} isExternalLink={trail.isExternalLink} @@ -158,7 +158,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { format={trail.format} headlineText={trail.headline} image={trail.image} - dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}`} + dataLinkName={`onwards-content-gallery-style ${trail.dataLinkName}-position-1`} discussionId={trail.discussionId} discussionApiUrl={trail.discussionApiUrl} isExternalLink={trail.isExternalLink} @@ -170,32 +170,40 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { ))}
      - {secondSlice25.map((trail, index) => ( -
    • 0} - > - -
    • - ))} + {secondSlice25.map((trail, index) => { + const dataLinkName = `onwards-content-gallery-style ${ + trail.dataLinkName + }-position-${index + 2}`; + + return ( +
    • 0} + > + +
    • + ); + })}
    From c8f5b0907a2452abda6da25ce940733ac87b5e7c Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 12 Dec 2024 09:50:57 +0000 Subject: [PATCH 58/84] Rework container level spacing for beta containers (#12951) * Add container spacing context and space * Lift small spacing into front section and out of rows * fix linting * Use boolean rather than falsey --- .../src/components/FlexibleGeneral.tsx | 25 +- .../src/components/FlexibleSpecial.tsx | 13 +- .../src/components/FrontSection.tsx | 342 ++++++++---------- .../src/components/ScrollableCarousel.tsx | 1 - .../src/components/StaticFeatureTwo.tsx | 2 +- .../src/components/StaticMediumFour.tsx | 2 +- dotcom-rendering/src/layouts/FrontLayout.tsx | 1 + .../src/model/enhanceCollections.ts | 10 + dotcom-rendering/src/types/front.ts | 1 + 9 files changed, 190 insertions(+), 207 deletions(-) diff --git a/dotcom-rendering/src/components/FlexibleGeneral.tsx b/dotcom-rendering/src/components/FlexibleGeneral.tsx index 8c24acdc2c5..11f874b545d 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.tsx @@ -158,6 +158,7 @@ export const SplashCardLayout = ({ absoluteServerTimes, imageLoading, aspectRatio, + isLastRow, }: { cards: DCRFrontCard[]; imageLoading: Loading; @@ -165,6 +166,7 @@ export const SplashCardLayout = ({ showAge?: boolean; absoluteServerTimes: boolean; aspectRatio: AspectRatio; + isLastRow: boolean; }) => { const card = cards[0]; if (!card) return null; @@ -183,7 +185,11 @@ export const SplashCardLayout = ({ ); return ( -
      +
      • { const card = cards[0]; if (!card) return null; @@ -287,7 +295,11 @@ export const BoostedCardLayout = ({ liveUpdatesPosition, } = decideCardProperties(card.boostLevel); return ( -
          +
          • { if (cards.length === 0) return null; return (
              )} @@ -444,6 +459,7 @@ export const FlexibleGeneral = ({ imageLoading={imageLoading} aspectRatio={aspectRatio} isFirstRow={!splash.length && i === 0} + isLastRow={i === groupedCards.length - 1} /> ); @@ -460,6 +476,7 @@ export const FlexibleGeneral = ({ isFirstRow={!splash.length && i === 0} isFirstStandardRow={i === 0} aspectRatio={aspectRatio} + isLastRow={i === groupedCards.length - 1} /> ); } diff --git a/dotcom-rendering/src/components/FlexibleSpecial.tsx b/dotcom-rendering/src/components/FlexibleSpecial.tsx index fc1fff05590..36de07c0b1d 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.tsx @@ -113,6 +113,7 @@ export const OneCardLayout = ({ absoluteServerTimes, imageLoading, aspectRatio, + isLastRow, }: { cards: DCRFrontCard[]; imageLoading: Loading; @@ -120,6 +121,7 @@ export const OneCardLayout = ({ showAge?: boolean; absoluteServerTimes: boolean; aspectRatio: AspectRatio; + isLastRow: boolean; }) => { const card = cards[0]; if (!card) return null; @@ -137,7 +139,7 @@ export const OneCardLayout = ({ card.supportingContent?.length ?? 0, ); return ( -
                +
                • +
                    {cards.map((card, cardIndex) => { return (
                  • - {/** Primary level containers have additional spacing above the horizontal rule */} - {containerLevel === 'Primary' && ( -
                    -
                    -
                    - )} - -
                    - {!!containerLevel && showTopBorder && ( -
                    - )} - + +
                    + {!!containerLevel && showTopBorder && (
                    + )} -
                    as the leftContent - title?.toLowerCase() === 'opinion', - ), - showVerticalRule && - !containerLevel && - sectionHeadlineFromLeftCol( - schemePalette('--section-border'), - ), - title?.toLowerCase() === 'headlines' && - sectionHeadlineHeight, - ]} - > - - } - collectionBranding={collectionBranding} - /> +
                    - {leftContent} -
                    +
                    as the leftContent + title?.toLowerCase() === 'opinion', + ), + showVerticalRule && + !containerLevel && + sectionHeadlineFromLeftCol( + schemePalette('--section-border'), + ), + title?.toLowerCase() === 'headlines' && + sectionHeadlineHeight, + ]} + > + + } + collectionBranding={collectionBranding} + /> - {isToggleable && ( -
                    - -
                    - )} + {leftContent} +
                    -
                    - {children} + {isToggleable && ( +
                    +
                    + )} -
                    - {isString(targetedTerritory) && - isAustralianTerritory(targetedTerritory) ? ( - - - - ) : showMore ? ( - - - - ) : null} - {pagination && ( - - )} -
                    +
                    + {children} +
                    - {treats && !hasPageSkin && ( -
                    - + {isString(targetedTerritory) && + isAustralianTerritory(targetedTerritory) ? ( + + -
                    + + ) : showMore ? ( + + + + ) : null} + {pagination && ( + )} -
                    -
                    - +
                    + + {treats && !hasPageSkin && ( +
                    + +
                    + )} +
                    +
                    ); }; diff --git a/dotcom-rendering/src/components/ScrollableCarousel.tsx b/dotcom-rendering/src/components/ScrollableCarousel.tsx index 714cabba628..99cd166ad11 100644 --- a/dotcom-rendering/src/components/ScrollableCarousel.tsx +++ b/dotcom-rendering/src/components/ScrollableCarousel.tsx @@ -47,7 +47,6 @@ const gridGapMobile = 10; */ const containerStyles = css` position: relative; - margin-bottom: ${space[6]}px; margin-left: -${gridGapMobile}px; margin-right: -${gridGapMobile}px; ${from.mobileLandscape} { diff --git a/dotcom-rendering/src/components/StaticFeatureTwo.tsx b/dotcom-rendering/src/components/StaticFeatureTwo.tsx index 4aeac445feb..d46959615d7 100644 --- a/dotcom-rendering/src/components/StaticFeatureTwo.tsx +++ b/dotcom-rendering/src/components/StaticFeatureTwo.tsx @@ -26,7 +26,7 @@ export const StaticFeatureTwo = ({ const cards = trails.slice(0, 2); return ( -
                      +
                        {cards.map((card) => { return (
                      • +
                          {cards.map((card, cardIndex) => { return (
                        • { containerLevel={ collection.config.containerLevel } + containerSpacing={collection.containerSpacing} > { + const nextCollectionIsPrimary = + nextSiblingCollection?.config.collectionLevel === 'Primary'; + return nextCollectionIsPrimary ? 'large' : 'small'; +}; + export const enhanceCollections = ({ collections, editionId, @@ -89,6 +96,8 @@ export const enhanceCollections = ({ }, ); + const containerSpacing = getContainerSpacing(collections[index + 1]); + return { id, displayName, @@ -99,6 +108,7 @@ export const enhanceCollections = ({ collectionType, href, containerPalette, + containerSpacing, collectionBranding, grouped: groupCards( collectionType, diff --git a/dotcom-rendering/src/types/front.ts b/dotcom-rendering/src/types/front.ts index 6b72b26c0d4..f2f0128aad4 100644 --- a/dotcom-rendering/src/types/front.ts +++ b/dotcom-rendering/src/types/front.ts @@ -417,6 +417,7 @@ export type DCRCollectionType = { description?: string; collectionType: DCRContainerType; containerPalette?: DCRContainerPalette; + containerSpacing?: 'large' | 'small'; grouped: DCRGroupedTrails; curated: DCRFrontCard[]; backfill: DCRFrontCard[]; From 1a28f88952bedb533bc5adffc01ce2bd83f82461 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 12 Dec 2024 10:17:52 +0000 Subject: [PATCH 59/84] Update dotcom-rendering/src/paletteDeclarations.ts Co-authored-by: Daniel Clifton <110032454+DanielCliftonGuardian@users.noreply.github.com> --- dotcom-rendering/src/paletteDeclarations.ts | 29 +++++++-------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 366039f3198..cf2814f0d34 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -4894,32 +4894,21 @@ const staffPickBadgeTextDark: PaletteFunction = () => sourcePalette.neutral[7]; const speechBubbleBackgroundLight: PaletteFunction = ({ theme, design }) => { switch (theme) { - case Pillar.News: { + case Pillar.News: return design === ArticleDesign.Analysis ? sourcePalette.news[300] : sourcePalette.news[400]; - } - case Pillar.Opinion: { - return sourcePalette.opinion[400]; - } - case Pillar.Sport: { - return sourcePalette.sport[400]; - } - case Pillar.Culture: { - return sourcePalette.culture[400]; - } - case Pillar.Lifestyle: { - return sourcePalette.lifestyle[400]; - } - case ArticleSpecial.Labs: { + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: + return pillarPalette(theme, 400); + case ArticleSpecial.Labs: return sourcePalette.labs[400]; - } - case ArticleSpecial.SpecialReport: { + case ArticleSpecial.SpecialReport: return sourcePalette.specialReport[400]; - } - case ArticleSpecial.SpecialReportAlt: { + case ArticleSpecial.SpecialReportAlt: return sourcePalette.specialReportAlt[200]; - } } }; From e5f02cc04b086b39bae4e87ee869c894aae80b91 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 12 Dec 2024 10:41:24 +0000 Subject: [PATCH 60/84] Remove decidePalette from TextBlock component --- .../src/components/TextBlockComponent.tsx | 3 +- dotcom-rendering/src/lib/decidePalette.ts | 17 ---------- dotcom-rendering/src/paletteDeclarations.ts | 33 +++++++++++++++++-- dotcom-rendering/src/types/palette.ts | 1 - 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/dotcom-rendering/src/components/TextBlockComponent.tsx b/dotcom-rendering/src/components/TextBlockComponent.tsx index 655757d0069..acea33aec5c 100644 --- a/dotcom-rendering/src/components/TextBlockComponent.tsx +++ b/dotcom-rendering/src/components/TextBlockComponent.tsx @@ -17,7 +17,6 @@ import { type ArticleFormat, ArticleSpecial, } from '../lib/articleFormat'; -import { decidePalette } from '../lib/decidePalette'; import { getAttrs, isElement, parseHtml } from '../lib/domUtils'; import { palette as themePalette } from '../palette'; import { logger } from '../server/lib/logging'; @@ -228,7 +227,7 @@ const styles = (format: ArticleFormat) => css` border-radius: 50%; height: ${remSpace[2]}; width: ${remSpace[2]}; - background-color: ${decidePalette(format).background.bullet}; + background-color: ${themePalette('--textblock-bullet-background')}; } ${until.tablet} { diff --git a/dotcom-rendering/src/lib/decidePalette.ts b/dotcom-rendering/src/lib/decidePalette.ts index a2ca4b6e860..a7c837d2f0a 100644 --- a/dotcom-rendering/src/lib/decidePalette.ts +++ b/dotcom-rendering/src/lib/decidePalette.ts @@ -86,22 +86,6 @@ const textStandfirstLink = (format: ArticleFormat): string => { } }; -const backgroundBullet = (format: ArticleFormat): string => { - if (format.theme === ArticleSpecial.Labs) return BLACK; - if (format.theme === ArticleSpecial.SpecialReport) { - return specialReport[300]; - } - if (format.design === ArticleDesign.Analysis) { - switch (format.theme) { - case Pillar.News: - return news[300]; - default: - return pillarPalette[format.theme].main; - } - } - return pillarPalette[format.theme].main; -}; - const backgroundBulletStandfirst = (format: ArticleFormat): string => { if ( format.design === ArticleDesign.DeadBlog || @@ -425,7 +409,6 @@ export const decidePalette = (format: ArticleFormat): Palette => { background: { analysisContrast: backgroundAnalysisContrastColour(), analysisContrastHover: backgroundAnalysisContrastHoverColour(), - bullet: backgroundBullet(format), bulletStandfirst: backgroundBulletStandfirst(format), filterButton: backgroundFilterButton(), filterButtonHover: backgroundFilterButtonHover(format), diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index cf2814f0d34..73c8b057568 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -50,8 +50,33 @@ const pillarPalette = ( } }; -const textblockTextLight: PaletteFunction = (format: ArticleFormat) => { - switch (format.design) { +const textblockBulletLight: PaletteFunction = ({ theme, design }) => { + switch (theme) { + case Pillar.News: { + return design === ArticleDesign.Analysis + ? sourcePalette.news[300] + : sourcePalette.news[400]; + } + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: { + return pillarPalette(theme, 400); + } + case ArticleSpecial.Labs: { + return sourcePalette.neutral[7]; + } + case ArticleSpecial.SpecialReport: { + return sourcePalette.specialReport[300]; + } + case ArticleSpecial.SpecialReportAlt: { + return sourcePalette.specialReportAlt[200]; + } + } +}; + +const textblockTextLight: PaletteFunction = ({ design }) => { + switch (design) { case ArticleDesign.Audio: return sourcePalette.neutral[97]; default: @@ -7102,6 +7127,10 @@ const paletteColours = { light: () => sourcePalette.neutral[0], dark: () => sourcePalette.neutral[86], }, + '--textblock-bullet-background': { + light: textblockBulletLight, + dark: textblockBulletLight, + }, '--textblock-text': { light: textblockTextLight, dark: textblockTextDark, diff --git a/dotcom-rendering/src/types/palette.ts b/dotcom-rendering/src/types/palette.ts index 98a5fc70462..ac904af5a03 100644 --- a/dotcom-rendering/src/types/palette.ts +++ b/dotcom-rendering/src/types/palette.ts @@ -16,7 +16,6 @@ export type Palette = { background: { analysisContrast: Colour; analysisContrastHover: Colour; - bullet: Colour; bulletStandfirst: Colour; filterButton: Colour; filterButtonHover: Colour; From 7ba9de1b818b109f1435053f3cdc0f1a98f79408 Mon Sep 17 00:00:00 2001 From: frederickobrien Date: Wed, 20 Nov 2024 14:03:58 +0000 Subject: [PATCH 61/84] Dark mode for table block, add component story --- .../TableBlockComponent.stories.tsx | 215 ++++++++++++++++++ .../src/components/TableBlockComponent.tsx | 13 +- 2 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 dotcom-rendering/src/components/TableBlockComponent.stories.tsx diff --git a/dotcom-rendering/src/components/TableBlockComponent.stories.tsx b/dotcom-rendering/src/components/TableBlockComponent.stories.tsx new file mode 100644 index 00000000000..c04b644a85c --- /dev/null +++ b/dotcom-rendering/src/components/TableBlockComponent.stories.tsx @@ -0,0 +1,215 @@ +import type { Meta } from '@storybook/react'; +import { TableBlockComponent } from './TableBlockComponent'; + +const tableHtml = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                          PosTeamPGDPts
                          1Liverpool111528
                          2Man City11923
                          3Nottm Forest11519
                          4Brighton11419
                          5Chelsea10818
                          6Arsenal10618
                          7Fulham11318
                          8Newcastle11218
                          9Aston Villa11018
                          10Tottenham Hotspur111016
                          11Brentford11016
                          12AFC Bournemouth11015
                          13Man Utd11015
                          14West Ham11-612
                          15Leicester11-710
                          16Everton11-710
                          17Ipswich11-108
                          18Crystal Palace11-77
                          19Wolverhampton11-116
                          20Southampton11-144
                          `; + +const meta = { + title: 'Components/TableBlockComponent', + component: TableBlockComponent, + render: (args) => , + args: { + element: { + isMandatory: true, + elementId: 'table', + _type: 'model.dotcomrendering.pageElements.TableBlockElement', + html: tableHtml, + }, + }, +} satisfies Meta; + +export default meta; + +export const Default = {}; diff --git a/dotcom-rendering/src/components/TableBlockComponent.tsx b/dotcom-rendering/src/components/TableBlockComponent.tsx index 244817016b6..f4e9f45499b 100644 --- a/dotcom-rendering/src/components/TableBlockComponent.tsx +++ b/dotcom-rendering/src/components/TableBlockComponent.tsx @@ -1,17 +1,18 @@ import { css } from '@emotion/react'; -import { palette, text, textSans12 } from '@guardian/source/foundations'; +import { textSans12 } from '@guardian/source/foundations'; import { unescapeData } from '../lib/escapeData'; +import { palette } from '../palette'; import type { TableBlockElement } from '../types/content'; const tableEmbed = css` .table--football { width: 100%; - background: ${palette.neutral[97]}; - border-top: 0.0625rem solid ${palette.focus[400]}; - color: ${palette.neutral[7]}; + background: ${palette('--table-block-background')}; + border-top: 0.0625rem solid ${palette('--table-block-border-top')}; + color: ${palette('--table-block-text')}; border-collapse: inherit; tr:nth-child(odd) > td { - background-color: ${palette.neutral[93]}; + background-color: ${palette('--table-block-stripe')}; } th { padding: 0.5rem; @@ -30,7 +31,7 @@ const tableEmbed = css` } tr > th:first-child, td:first-child { - color: ${text.supporting}; + color: ${palette('--table-block-text-first-column')}; } .table-column--main { width: 100%; From 72d49c10204646e5e8004aa27b07de2f084bc46c Mon Sep 17 00:00:00 2001 From: frederickobrien Date: Thu, 21 Nov 2024 10:54:20 +0000 Subject: [PATCH 62/84] Build element tree from HTML A la `TextBlockComponent` --- .../src/components/TableBlockComponent.tsx | 65 ++++++++++++++++--- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/dotcom-rendering/src/components/TableBlockComponent.tsx b/dotcom-rendering/src/components/TableBlockComponent.tsx index f4e9f45499b..66a94aac6c2 100644 --- a/dotcom-rendering/src/components/TableBlockComponent.tsx +++ b/dotcom-rendering/src/components/TableBlockComponent.tsx @@ -1,7 +1,8 @@ import { css } from '@emotion/react'; import { textSans12 } from '@guardian/source/foundations'; -import { unescapeData } from '../lib/escapeData'; +import { isElement, parseHtml } from '../lib/domUtils'; import { palette } from '../palette'; +import { logger } from '../server/lib/logging'; import type { TableBlockElement } from '../types/content'; const tableEmbed = css` @@ -33,9 +34,6 @@ const tableEmbed = css` td:first-child { color: ${palette('--table-block-text-first-column')}; } - .table-column--main { - width: 100%; - } } margin-bottom: 16px; `; @@ -44,12 +42,61 @@ type Props = { element: TableBlockElement; }; +const buildElementTree = (node: Node) => { + const children = Array.from(node.childNodes).map(buildElementTree); + switch (node.nodeName) { + case 'TABLE': { + return {children}
                          ; + } + case 'THEAD': { + return {children}; + } + case 'TBODY': { + return {children}; + } + case 'ABBR': { + return ( + + {children} + + ); + } + case 'TR': { + return {children}; + } + case 'TH': { + return {children}; + } + case 'TD': { + const isMainColumn = (node as HTMLElement).className.includes( + 'table-column--main', + ); + return ( + + {children} + + ); + } + case '#text': { + return node.textContent; + } + default: + logger.warn('TableBlockComponent: Unknown element received', { + isDev: process.env.NODE_ENV !== 'production', + element: { + name: node.nodeName, + html: isElement(node) ? node.outerHTML : undefined, + }, + }); + return null; + } +}; + export const TableBlockComponent = ({ element }: Props) => { + const fragment = parseHtml(element.html); return ( -
                          +
                          + {Array.from(fragment.childNodes).map(buildElementTree)} +
                          ); }; From a7a5ad6f3c713b9a169c37625c6f7c37482b5e3f Mon Sep 17 00:00:00 2001 From: frederickobrien Date: Thu, 21 Nov 2024 11:42:23 +0000 Subject: [PATCH 63/84] Split out styling --- .../src/components/TableBlockComponent.tsx | 81 +++++++++++-------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/dotcom-rendering/src/components/TableBlockComponent.tsx b/dotcom-rendering/src/components/TableBlockComponent.tsx index 66a94aac6c2..63fcf1c7460 100644 --- a/dotcom-rendering/src/components/TableBlockComponent.tsx +++ b/dotcom-rendering/src/components/TableBlockComponent.tsx @@ -5,37 +5,37 @@ import { palette } from '../palette'; import { logger } from '../server/lib/logging'; import type { TableBlockElement } from '../types/content'; +const tableStyles = css` + width: 100%; + background: ${palette('--table-block-background')}; + border-top: 0.0625rem solid ${palette('--table-block-border-top')}; + color: ${palette('--table-block-text')}; + border-collapse: inherit; +`; + +const headStyles = css` + tr { + font-weight: 800; + text-align: left; + } +`; + +const rowStyles = css` + ${textSans12}; + :nth-child(odd) > td { + background-color: ${palette('--table-block-stripe')}; + } +`; + +const cellPadding = css` + padding: 0.5rem; +`; + const tableEmbed = css` - .table--football { - width: 100%; - background: ${palette('--table-block-background')}; - border-top: 0.0625rem solid ${palette('--table-block-border-top')}; - color: ${palette('--table-block-text')}; - border-collapse: inherit; - tr:nth-child(odd) > td { - background-color: ${palette('--table-block-stripe')}; - } - th { - padding: 0.5rem; - } - td { - padding: 0.5rem; - } - tr { - ${textSans12}; - } - thead { - tr { - font-weight: 800; - text-align: left; - } - } - tr > th:first-child, - td:first-child { - color: ${palette('--table-block-text-first-column')}; - } + tr > th:first-child, + td:first-child { + color: ${palette('--table-block-text-first-column')}; } - margin-bottom: 16px; `; type Props = { @@ -46,10 +46,14 @@ const buildElementTree = (node: Node) => { const children = Array.from(node.childNodes).map(buildElementTree); switch (node.nodeName) { case 'TABLE': { - return {children}
                          ; + return ( + + {children} +
                          + ); } case 'THEAD': { - return {children}; + return {children}; } case 'TBODY': { return {children}; @@ -62,17 +66,20 @@ const buildElementTree = (node: Node) => { ); } case 'TR': { - return {children}; + return {children}; } case 'TH': { - return {children}; + return {children}; } case 'TD': { const isMainColumn = (node as HTMLElement).className.includes( 'table-column--main', ); return ( - + {children} ); @@ -95,7 +102,11 @@ const buildElementTree = (node: Node) => { export const TableBlockComponent = ({ element }: Props) => { const fragment = parseHtml(element.html); return ( -
                          +
                          {Array.from(fragment.childNodes).map(buildElementTree)}
                          ); From 587de7b33c87c3b1ff0f50c584c5835601696f8e Mon Sep 17 00:00:00 2001 From: frederickobrien Date: Thu, 12 Dec 2024 11:17:38 +0000 Subject: [PATCH 64/84] Update `paletteDeclarations` --- dotcom-rendering/src/paletteDeclarations.ts | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index cf2814f0d34..a1831ebf632 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -1823,6 +1823,23 @@ const accordionBackgroundDark: PaletteFunction = () => const accordionBackgroundLight: PaletteFunction = () => sourcePalette.neutral[97]; +const tableBlockTextLight: PaletteFunction = () => sourcePalette.neutral[7]; +const tableBlockTextDark: PaletteFunction = () => sourcePalette.neutral[86]; +const tableBlockBackgroundLight: PaletteFunction = () => + sourcePalette.neutral[97]; +const tableBlockBackgroundDark: PaletteFunction = () => + sourcePalette.neutral[38]; +const tableBlockStripeLight: PaletteFunction = () => sourcePalette.neutral[93]; +const tableBlockStripeDark: PaletteFunction = () => sourcePalette.neutral[20]; +const tableBlockTextFirstColumnLight: PaletteFunction = () => + sourcePalette.neutral[46]; +const tableBlockTextFirstColumnDark: PaletteFunction = () => + sourcePalette.neutral[60]; +const tableBlockBorderTopLight: PaletteFunction = () => + sourcePalette.brand[500]; +const tableBlockBorderTopDark: PaletteFunction = () => + sourcePalette.neutral[60]; + const tableOfContentsLight: PaletteFunction = () => sourcePalette.neutral[7]; const tableOfContentsDark: PaletteFunction = () => sourcePalette.neutral[86]; const tableOfContentsBorderLight: PaletteFunction = () => @@ -7086,6 +7103,26 @@ const paletteColours = { light: syndicationButtonTextLight, dark: syndicationButtonTextDark, }, + '--table-block-background': { + light: tableBlockBackgroundLight, + dark: tableBlockBackgroundDark, + }, + '--table-block-border-top': { + light: tableBlockBorderTopLight, + dark: tableBlockBorderTopDark, + }, + '--table-block-stripe': { + light: tableBlockStripeLight, + dark: tableBlockStripeDark, + }, + '--table-block-text': { + light: tableBlockTextLight, + dark: tableBlockTextDark, + }, + '--table-block-text-first-column': { + light: tableBlockTextFirstColumnLight, + dark: tableBlockTextFirstColumnDark, + }, '--table-of-contents': { light: tableOfContentsLight, dark: tableOfContentsDark, From f6b9bb2ef708809f08d6c6fa9970acfbfffa6343 Mon Sep 17 00:00:00 2001 From: Rupert Bates Date: Wed, 11 Dec 2024 14:39:57 +0000 Subject: [PATCH 65/84] Upgrade support-dotcom-components to 3.2.0 --- dotcom-rendering/package.json | 2 +- pnpm-lock.yaml | 67 ++++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 9905ae938bc..6ab042c52b6 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -53,7 +53,7 @@ "@guardian/shimport": "1.0.2", "@guardian/source": "8.0.0", "@guardian/source-development-kitchen": "12.0.0", - "@guardian/support-dotcom-components": "3.1.0", + "@guardian/support-dotcom-components": "3.2.0", "@guardian/tsconfig": "0.2.0", "@playwright/test": "1.45.3", "@sentry/browser": "7.75.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2457e5e83f0..1a72b4251eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -374,8 +374,8 @@ importers: specifier: 12.0.0 version: 12.0.0(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) '@guardian/support-dotcom-components': - specifier: 3.1.0 - version: 3.1.0(@guardian/libs@19.2.1)(zod@3.22.4) + specifier: 3.2.0 + version: 3.2.0(@guardian/libs@19.2.1)(zod@3.22.4) '@guardian/tsconfig': specifier: 0.2.0 version: 0.2.0 @@ -4378,8 +4378,8 @@ packages: - utf-8-validate dev: false - /@guardian/support-dotcom-components@3.1.0(@guardian/libs@19.2.1)(zod@3.22.4): - resolution: {integrity: sha512-JzXo3QGITIyehVFEeM2ZvUsCWbjEkAAxtl+zsTpWniEnmylFuyb9YH7DS+uT7GeXUnSBzkHFle/pkL2WPDa1kA==} + /@guardian/support-dotcom-components@3.2.0(@guardian/libs@19.2.1)(zod@3.22.4): + resolution: {integrity: sha512-jxsOmP+DTGdpy3oitRGFjnRfznvOAW+ojRltZQT3qT6Eoe5nkuNc5ip0ok0y4qSnov/3MKn1UxJpvqTQT6okTw==} peerDependencies: '@guardian/libs': ^17.0.0 zod: ^3.22.4 @@ -4389,7 +4389,7 @@ packages: compression: 1.7.4 cors: 2.8.5 date-fns: 2.30.0 - express: 4.21.0 + express: 4.21.2 jsonschema: 1.4.1 lodash.debounce: 4.0.8 log4js: 6.9.1 @@ -9360,6 +9360,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + dev: false + /copy-file@11.0.0: resolution: {integrity: sha512-mFsNh/DIANLqFt5VHZoGirdg7bK5+oTWlhnGu6tgRhzBlnEKWaPX2xrFaLltii/6rmhqFMJqffUgknuRdpYlHw==} engines: {node: '>=18'} @@ -10244,7 +10249,7 @@ packages: is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.13.2 + object-inspect: 1.13.3 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 @@ -11158,6 +11163,45 @@ packages: - supports-color dev: false + /express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /ext-list@2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} engines: {node: '>=0.10.0'} @@ -14605,11 +14649,6 @@ packages: engines: {node: '>= 6'} dev: false - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - dev: false - /object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} @@ -15005,6 +15044,10 @@ packages: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} dev: false + /path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -16238,7 +16281,7 @@ packages: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.2 + object-inspect: 1.13.3 dev: false /signal-exit@3.0.7: From d3935811fdc77e7b06273e2db375e88c7e446d6b Mon Sep 17 00:00:00 2001 From: Rupert Bates Date: Wed, 11 Dec 2024 14:41:05 +0000 Subject: [PATCH 66/84] Remove isRecurringContributor --- .../components/LiveBlogEpic.importable.tsx | 2 -- .../SlotBodyEnd/ReaderRevenueEpic.tsx | 4 --- dotcom-rendering/src/lib/contributions.ts | 36 +------------------ 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx index c0b5dcf67da..05e87ba470f 100644 --- a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx +++ b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx @@ -10,7 +10,6 @@ import { submitComponentEvent } from '../client/ophan/ophan'; import { useArticleCounts } from '../lib/articleCount'; import { getLastOneOffContributionTimestamp, - isRecurringContributor, shouldHideSupportMessaging, useHasOptedOutOfArticleCount, } from '../lib/contributions'; @@ -125,7 +124,6 @@ const usePayload = ({ isPaidContent, tags, showSupportMessaging: !hideSupportMessagingForUser, - isRecurringContributor: isRecurringContributor(isSignedIn), lastOneOffContributionDate: getLastOneOffContributionTimestamp() ?? undefined, mvtId, diff --git a/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx b/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx index d84aa777432..9c2c9be1e2e 100644 --- a/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx +++ b/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx @@ -16,7 +16,6 @@ import { hasCmpConsentForBrowserId, hasCmpConsentForWeeklyArticleCount, hasOptedOutOfArticleCount, - isRecurringContributor, shouldHideSupportMessaging, } from '../../lib/contributions'; import { lazyFetchEmailWithTimeout } from '../../lib/fetchEmail'; @@ -70,9 +69,6 @@ const buildPayload = async ( isPaidContent: data.isPaidContent, tags: data.tags, showSupportMessaging: !data.hideSupportMessagingForUser, - isRecurringContributor: isRecurringContributor( - data.isSignedIn ?? false, - ), lastOneOffContributionDate: getLastOneOffContributionTimestamp(), epicViewLog: getEpicViewLog(storage.local), weeklyArticleHistory: await data.asyncArticleCount, diff --git a/dotcom-rendering/src/lib/contributions.ts b/dotcom-rendering/src/lib/contributions.ts index de91bc67ee0..71b3a8d9eaf 100644 --- a/dotcom-rendering/src/lib/contributions.ts +++ b/dotcom-rendering/src/lib/contributions.ts @@ -61,36 +61,6 @@ export const hasSupporterCookie = ( } }; -// Determine if user is a recurring contributor by checking if they are signed in -// AND have at least one of the relevant cookies. -// We need to look at both User Attributes and Frontend Support cookies -// as the former might not reflect the latest contributor status, since it's set upon signing in. -// Frontend Support cookies are set when a contribution is made. -export const isRecurringContributor = (isSignedIn: boolean): boolean => { - // Attributes cookie - we want this to have a specific value - const isRecurringContributorFromAttrs = - getCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }) === 'true'; - - // Support cookies - we only care whether these exist - const hasMonthlyContributionCookie = - getCookie({ - name: SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, - shouldMemoize: true, - }) !== null; - const hasAnnualContributionCookie = - getCookie({ - name: SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, - shouldMemoize: true, - }) !== null; - - return ( - isSignedIn && - (isRecurringContributorFromAttrs || - hasMonthlyContributionCookie || - hasAnnualContributionCookie) - ); -}; - // looks at attribute and support cookies // ONE_OFF_CONTRIBUTION_DATE_COOKIE (attributes cookie, when loggin in) // SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (support cookie, when making one-off contribution) @@ -168,11 +138,7 @@ export const shouldHideSupportMessaging = ( if (hasCookie === 'Pending') { return 'Pending'; } else { - return ( - hasCookie || - isRecurringContributor(isSignedIn) || - isRecentOneOffContributor() - ); + return hasCookie || isRecentOneOffContributor(); } }; From d8edd0087bc14aff3a9d292afa602ccfc497c75f Mon Sep 17 00:00:00 2001 From: Rupert Bates Date: Wed, 11 Dec 2024 14:46:13 +0000 Subject: [PATCH 67/84] Remove lastOneOffContributionDate --- .../components/LiveBlogEpic.importable.tsx | 3 - .../SlotBodyEnd/ReaderRevenueEpic.tsx | 10 ++-- .../ReaderRevenueBanner.tsx | 2 - .../components/TopBarSupport.importable.tsx | 2 - .../src/lib/contributions.test.ts | 58 ++++--------------- dotcom-rendering/src/lib/contributions.ts | 45 ++------------ 6 files changed, 24 insertions(+), 96 deletions(-) diff --git a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx index 05e87ba470f..a834d1f25b6 100644 --- a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx +++ b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx @@ -9,7 +9,6 @@ import { createPortal } from 'react-dom'; import { submitComponentEvent } from '../client/ophan/ophan'; import { useArticleCounts } from '../lib/articleCount'; import { - getLastOneOffContributionTimestamp, shouldHideSupportMessaging, useHasOptedOutOfArticleCount, } from '../lib/contributions'; @@ -124,8 +123,6 @@ const usePayload = ({ isPaidContent, tags, showSupportMessaging: !hideSupportMessagingForUser, - lastOneOffContributionDate: - getLastOneOffContributionTimestamp() ?? undefined, mvtId, countryCode, epicViewLog: getEpicViewLog(storage.local), diff --git a/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx b/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx index 9c2c9be1e2e..7553ef049b3 100644 --- a/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx +++ b/dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx @@ -1,6 +1,10 @@ import { css } from '@emotion/react'; -import { cmp } from '@guardian/libs'; -import { getCookie, startPerformanceMeasure, storage } from '@guardian/libs'; +import { + cmp, + getCookie, + startPerformanceMeasure, + storage, +} from '@guardian/libs'; import type { ComponentEvent } from '@guardian/ophan-tracker-js'; import { getEpic, getEpicViewLog } from '@guardian/support-dotcom-components'; import type { @@ -12,7 +16,6 @@ import type { import { useEffect, useState } from 'react'; import { submitComponentEvent } from '../../client/ophan/ophan'; import { - getLastOneOffContributionTimestamp, hasCmpConsentForBrowserId, hasCmpConsentForWeeklyArticleCount, hasOptedOutOfArticleCount, @@ -69,7 +72,6 @@ const buildPayload = async ( isPaidContent: data.isPaidContent, tags: data.tags, showSupportMessaging: !data.hideSupportMessagingForUser, - lastOneOffContributionDate: getLastOneOffContributionTimestamp(), epicViewLog: getEpicViewLog(storage.local), weeklyArticleHistory: await data.asyncArticleCount, diff --git a/dotcom-rendering/src/components/StickyBottomBanner/ReaderRevenueBanner.tsx b/dotcom-rendering/src/components/StickyBottomBanner/ReaderRevenueBanner.tsx index ee6903663f2..b0828463e2a 100644 --- a/dotcom-rendering/src/components/StickyBottomBanner/ReaderRevenueBanner.tsx +++ b/dotcom-rendering/src/components/StickyBottomBanner/ReaderRevenueBanner.tsx @@ -16,7 +16,6 @@ import { useEffect, useState } from 'react'; import { submitComponentEvent } from '../../client/ophan/ophan'; import type { ArticleCounts } from '../../lib/articleCount'; import { - getLastOneOffContributionDate, getPurchaseInfo, hasCmpConsentForBrowserId, hasOptedOutOfArticleCount, @@ -165,7 +164,6 @@ const buildPayload = async ({ : undefined, purchaseInfo: getPurchaseInfo(), isSignedIn, - lastOneOffContributionDate: getLastOneOffContributionDate(), hasConsented: userConsent, abandonedBasket: parseAbandonedBasket( getCookie({ name: 'GU_CO_INCOMPLETE', shouldMemoize: true }), diff --git a/dotcom-rendering/src/components/TopBarSupport.importable.tsx b/dotcom-rendering/src/components/TopBarSupport.importable.tsx index 625a33d04ca..871131f69e0 100644 --- a/dotcom-rendering/src/components/TopBarSupport.importable.tsx +++ b/dotcom-rendering/src/components/TopBarSupport.importable.tsx @@ -14,7 +14,6 @@ import type { import { useEffect, useState } from 'react'; import { submitComponentEvent } from '../client/ophan/ophan'; import { - getLastOneOffContributionDate, getPurchaseInfo, shouldHideSupportMessaging, } from '../lib/contributions'; @@ -81,7 +80,6 @@ const ReaderRevenueLinksRemote = ({ mvtId: Number( getCookie({ name: 'GU_mvt_id', shouldMemoize: true }), ), - lastOneOffContributionDate: getLastOneOffContributionDate(), purchaseInfo: getPurchaseInfo(), isSignedIn, }, diff --git a/dotcom-rendering/src/lib/contributions.test.ts b/dotcom-rendering/src/lib/contributions.test.ts index ee59149c44a..dcd998d15a8 100644 --- a/dotcom-rendering/src/lib/contributions.test.ts +++ b/dotcom-rendering/src/lib/contributions.test.ts @@ -6,7 +6,6 @@ import { HIDE_SUPPORT_MESSAGING_COOKIE, isRecentOneOffContributor, NO_RR_BANNER_KEY, - ONE_OFF_CONTRIBUTION_DATE_COOKIE, recentlyClosedBanner, setLocalNoBannerCachePeriod, SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, @@ -22,23 +21,10 @@ const clearAllCookies = () => { } }; -describe('getLastOneOffContributionDate', () => { +describe('getLastOneOffContributionTimestamp', () => { beforeEach(clearAllCookies); - it('returns date from attributes cookie if only cookie found', () => { - const somePastDate = '2020-01-28'; - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: somePastDate, - }); - const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); - - // Our function will convert YYYY-MM-DD into a timestamp - const somePastDateToTimestamp = Date.parse(somePastDate); - expect(lastOneOffContributionDate).toBe(somePastDateToTimestamp); - }); - - it('returns a support cookie date if only cookie found', () => { + it('returns a support cookie date if found', () => { const somePastDate = 1582567969093; setCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, @@ -48,31 +34,10 @@ describe('getLastOneOffContributionDate', () => { expect(lastOneOffContributionDate).toBe(somePastDate); }); - it('returns the most recent date if both cookies present', () => { - const muchLongerAgo = '2020-01-28'; - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: muchLongerAgo, - }); - - const notSoLongAgo = 1582567969093; + it('returns undefined if the date cannot be parsed correctly', () => { setCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - value: String(notSoLongAgo), - }); - - const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); - expect(lastOneOffContributionDate).toBe(notSoLongAgo); - }); - - it('returns an empty string if no dates can be parsed correctly', () => { - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: 'CANT_TOUCH_THIS', - }); - setCookie({ - name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - value: 'OR_THIS', + value: 'NOT_A_DATE', }); const lastOneOffContributionDate = getLastOneOffContributionTimestamp(); @@ -97,8 +62,8 @@ describe('isRecentOneOffContributor', () => { it('returns true if there are 5 days between the last contribution date and now', () => { setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: Date.parse('2018-08-01').toString(), }); MockDate.set(Date.parse('2018-08-07T10:50:34')); @@ -106,18 +71,19 @@ describe('isRecentOneOffContributor', () => { }); it('returns true if there are 0 days between the last contribution date and now', () => { + const theDate = Date.parse('2018-08-01T13:00:30'); setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: theDate.toString(), }); - MockDate.set(Date.parse('2018-08-01T13:00:30')); + MockDate.set(theDate); expect(isRecentOneOffContributor()).toBe(true); }); it('returns false if the one-off contribution was more than 3 months ago', () => { setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: '2018-08-01', + name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, + value: Date.parse('2018-08-01').toString(), }); MockDate.set(Date.parse('2019-08-01T13:00:30')); expect(isRecentOneOffContributor()).toBe(false); diff --git a/dotcom-rendering/src/lib/contributions.ts b/dotcom-rendering/src/lib/contributions.ts index 71b3a8d9eaf..9729ce31fde 100644 --- a/dotcom-rendering/src/lib/contributions.ts +++ b/dotcom-rendering/src/lib/contributions.ts @@ -14,14 +14,13 @@ import type { DCRTagPageType } from '../types/tagPage'; // User Attributes API cookies (created on sign-in) export const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging'; export const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor'; -export const ONE_OFF_CONTRIBUTION_DATE_COOKIE = 'gu_one_off_contribution_date'; export const OPT_OUT_OF_ARTICLE_COUNT_COOKIE = 'gu_article_count_opt_out'; // Support Frontend cookies (created when a contribution is made) export const SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Monthly'; + 'gu.contributions.recurring.contrib-timestamp.Monthly'; // TODO: delete this, no longer needed export const SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Annual'; + 'gu.contributions.recurring.contrib-timestamp.Annual'; // TODO: delete this, no longer needed export const SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE = 'gu.contributions.contrib-timestamp'; @@ -61,58 +60,26 @@ export const hasSupporterCookie = ( } }; -// looks at attribute and support cookies -// ONE_OFF_CONTRIBUTION_DATE_COOKIE (attributes cookie, when loggin in) -// SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (support cookie, when making one-off contribution) -// Get the date of the latest one-off contribution by looking at the two relevant cookies -// and returning a Unix epoch string of the latest date found. +// looks at the SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (set by support-frontend when making one-off contribution) +// and returns a Unix epoch int of the date if it exists. export const getLastOneOffContributionTimestamp = (): number | undefined => { - // Attributes cookie - expects YYYY-MM-DD - const contributionDateFromAttributes = getCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - }); - // Support cookies - expects Unix epoch const contributionDateFromSupport = getCookie({ name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, }); - if (!contributionDateFromAttributes && !contributionDateFromSupport) { + if (!contributionDateFromSupport) { return undefined; } - // Parse dates into common format so they can be compared - const parsedDateFromAttributes = contributionDateFromAttributes - ? Date.parse(contributionDateFromAttributes) - : 0; + // Parse dates into common a number const parsedDateFromSupport = contributionDateFromSupport ? parseInt(contributionDateFromSupport, 10) : 0; - // Return most recent date - // Condition only passed if 'parsedDateFromAttributes' is NOT NaN - if (parsedDateFromAttributes > parsedDateFromSupport) { - return parsedDateFromAttributes; - } - return parsedDateFromSupport || undefined; // This guards against 'parsedDateFromSupport' being NaN }; -export const getLastOneOffContributionDate = (): string | undefined => { - const timestamp = getLastOneOffContributionTimestamp(); - - if (isUndefined(timestamp)) { - return undefined; - } - - const date = new Date(timestamp); - const year = date.getFullYear(); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const day = date.getDate().toString().padStart(2, '0'); - - return `${year}-${month}-${day}`; -}; - const dateDiffDays = (from: number, to: number): number => { const oneDayMs = 1000 * 60 * 60 * 24; const diffMs = to - from; From 5d2f6a9cfb8447d694c216a9a9949784a820b315 Mon Sep 17 00:00:00 2001 From: Rupert Bates Date: Thu, 12 Dec 2024 11:08:09 +0000 Subject: [PATCH 68/84] Remove redundant cookies --- .../client/userFeatures/user-features.test.ts | 55 ------------------- .../src/client/userFeatures/user-features.ts | 24 -------- dotcom-rendering/src/lib/contributions.ts | 6 +- .../src/lib/readerRevenueDevUtils.ts | 4 -- 4 files changed, 1 insertion(+), 88 deletions(-) diff --git a/dotcom-rendering/src/client/userFeatures/user-features.test.ts b/dotcom-rendering/src/client/userFeatures/user-features.test.ts index e96a1fc0eec..a720abd3697 100644 --- a/dotcom-rendering/src/client/userFeatures/user-features.test.ts +++ b/dotcom-rendering/src/client/userFeatures/user-features.test.ts @@ -35,18 +35,11 @@ const getAuthStatus = getAuthStatus_ as jest.MockedFunction< const PERSISTENCE_KEYS = { USER_FEATURES_EXPIRY_COOKIE: 'gu_user_features_expiry', - PAYING_MEMBER_COOKIE: 'gu_paying_member', - RECURRING_CONTRIBUTOR_COOKIE: 'gu_recurring_contributor', AD_FREE_USER_COOKIE: 'GU_AF1', ACTION_REQUIRED_FOR_COOKIE: 'gu_action_required_for', DIGITAL_SUBSCRIBER_COOKIE: 'gu_digital_subscriber', SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE: 'gu.contributions.contrib-timestamp', - ONE_OFF_CONTRIBUTION_DATE_COOKIE: 'gu_one_off_contribution_date', HIDE_SUPPORT_MESSAGING_COOKIE: 'gu_hide_support_messaging', - SUPPORT_MONTHLY_CONTRIBUTION_COOKIE: - 'gu.contributions.recurring.contrib-timestamp.Monthly', - SUPPORT_ANNUAL_CONTRIBUTION_COOKIE: - 'gu.contributions.recurring.contrib-timestamp.Annual', }; const setAllFeaturesData = (opts: { isExpired: boolean }) => { @@ -58,11 +51,6 @@ const setAllFeaturesData = (opts: { isExpired: boolean }) => { const adFreeExpiryDate = opts.isExpired ? new Date(currentTime - msInOneDay * 2) : new Date(currentTime + msInOneDay * 2); - setCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE, value: 'true' }); - setCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - value: 'true', - }); setCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE, value: 'true', @@ -86,8 +74,6 @@ const setAllFeaturesData = (opts: { isExpired: boolean }) => { }; const deleteAllFeaturesData = () => { - removeCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }); - removeCookie({ name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE }); removeCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }); @@ -125,14 +111,6 @@ describe('Refreshing the features data', () => { it('Does not delete the data just because it has expired', async () => { setAllFeaturesData({ isExpired: true }); await refresh(); - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('true'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('true'); expect( getCookie({ name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE, @@ -148,15 +126,6 @@ describe('Refreshing the features data', () => { await refresh(); expect(fetchJsonSpy).not.toHaveBeenCalled(); }); - - it('Performs an update if membership-frontend wipes just the paying-member cookie', async () => { - // Set everything except paying-member cookie - setAllFeaturesData({ isExpired: true }); - removeCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }); - - await refresh(); - expect(fetchJsonSpy).toHaveBeenCalledTimes(1); - }); }); }); describe('If user signed out', () => { @@ -178,14 +147,6 @@ describe('If user signed out', () => { expect( getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }), ).toBeNull(); - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBeNull(); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBeNull(); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBeNull(); @@ -270,14 +231,6 @@ describe('Storing new feature data', () => { }), ); return refresh().then(() => { - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('false'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('false'); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBe('false'); @@ -300,14 +253,6 @@ describe('Storing new feature data', () => { }), ); return refresh().then(() => { - expect( - getCookie({ name: PERSISTENCE_KEYS.PAYING_MEMBER_COOKIE }), - ).toBe('true'); - expect( - getCookie({ - name: PERSISTENCE_KEYS.RECURRING_CONTRIBUTOR_COOKIE, - }), - ).toBe('true'); expect( getCookie({ name: PERSISTENCE_KEYS.DIGITAL_SUBSCRIBER_COOKIE }), ).toBe('true'); diff --git a/dotcom-rendering/src/client/userFeatures/user-features.ts b/dotcom-rendering/src/client/userFeatures/user-features.ts index 6229e1ef83f..333c9a2424f 100644 --- a/dotcom-rendering/src/client/userFeatures/user-features.ts +++ b/dotcom-rendering/src/client/userFeatures/user-features.ts @@ -27,15 +27,11 @@ import { import type { UserFeaturesResponse } from './user-features-lib'; const USER_FEATURES_EXPIRY_COOKIE = 'gu_user_features_expiry'; -const PAYING_MEMBER_COOKIE = 'gu_paying_member'; const ACTION_REQUIRED_FOR_COOKIE = 'gu_action_required_for'; const DIGITAL_SUBSCRIBER_COOKIE = 'gu_digital_subscriber'; const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging'; const AD_FREE_USER_COOKIE = 'GU_AF1'; -const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor'; -const ONE_OFF_CONTRIBUTION_DATE_COOKIE = 'gu_one_off_contribution_date'; - const forcedAdFreeMode = !!/[#&]noadsaf(&.*)?$/.exec(window.location.hash); const userHasData = () => { @@ -43,9 +39,6 @@ const userHasData = () => { getAdFreeCookie() ?? getCookie({ name: ACTION_REQUIRED_FOR_COOKIE }) ?? getCookie({ name: USER_FEATURES_EXPIRY_COOKIE }) ?? - getCookie({ name: PAYING_MEMBER_COOKIE }) ?? - getCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }) ?? - getCookie({ name: ONE_OFF_CONTRIBUTION_DATE_COOKIE }) ?? getCookie({ name: DIGITAL_SUBSCRIBER_COOKIE }) ?? getCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE }); return !!cookie; @@ -69,14 +62,6 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { name: USER_FEATURES_EXPIRY_COOKIE, value: timeInDaysFromNow(1), }); - setCookie({ - name: PAYING_MEMBER_COOKIE, - value: String(JsonResponse.contentAccess.paidMember), - }); - setCookie({ - name: RECURRING_CONTRIBUTOR_COOKIE, - value: String(JsonResponse.contentAccess.recurringContributor), - }); setCookie({ name: DIGITAL_SUBSCRIBER_COOKIE, value: String(JsonResponse.contentAccess.digitalPack), @@ -85,12 +70,6 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { name: HIDE_SUPPORT_MESSAGING_COOKIE, value: String(!JsonResponse.showSupportMessaging), }); - if (JsonResponse.oneOffContributionDate) { - setCookie({ - name: ONE_OFF_CONTRIBUTION_DATE_COOKIE, - value: JsonResponse.oneOffContributionDate, - }); - } removeCookie({ name: ACTION_REQUIRED_FOR_COOKIE }); if (JsonResponse.alertAvailableFor) { @@ -110,12 +89,9 @@ const persistResponse = (JsonResponse: UserFeaturesResponse) => { const deleteOldData = (): void => { removeCookie({ name: AD_FREE_USER_COOKIE }); removeCookie({ name: USER_FEATURES_EXPIRY_COOKIE }); - removeCookie({ name: PAYING_MEMBER_COOKIE }); - removeCookie({ name: RECURRING_CONTRIBUTOR_COOKIE }); removeCookie({ name: ACTION_REQUIRED_FOR_COOKIE }); removeCookie({ name: DIGITAL_SUBSCRIBER_COOKIE }); removeCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE }); - removeCookie({ name: ONE_OFF_CONTRIBUTION_DATE_COOKIE }); }; const requestNewData = () => { diff --git a/dotcom-rendering/src/lib/contributions.ts b/dotcom-rendering/src/lib/contributions.ts index 9729ce31fde..59c03659964 100644 --- a/dotcom-rendering/src/lib/contributions.ts +++ b/dotcom-rendering/src/lib/contributions.ts @@ -16,11 +16,7 @@ export const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging'; export const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor'; export const OPT_OUT_OF_ARTICLE_COUNT_COOKIE = 'gu_article_count_opt_out'; -// Support Frontend cookies (created when a contribution is made) -export const SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Monthly'; // TODO: delete this, no longer needed -export const SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE = - 'gu.contributions.recurring.contrib-timestamp.Annual'; // TODO: delete this, no longer needed +// Support Frontend cookie (created when a contribution is made) export const SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE = 'gu.contributions.contrib-timestamp'; diff --git a/dotcom-rendering/src/lib/readerRevenueDevUtils.ts b/dotcom-rendering/src/lib/readerRevenueDevUtils.ts index 082407e1177..53f2975ab72 100644 --- a/dotcom-rendering/src/lib/readerRevenueDevUtils.ts +++ b/dotcom-rendering/src/lib/readerRevenueDevUtils.ts @@ -3,16 +3,12 @@ import { HIDE_SUPPORT_MESSAGING_COOKIE, RECURRING_CONTRIBUTOR_COOKIE, SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, } from './contributions'; import { getLocaleCode } from './getCountryCode'; const readerRevenueCookies = [ HIDE_SUPPORT_MESSAGING_COOKIE, RECURRING_CONTRIBUTOR_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_MONTHLY_COOKIE, - SUPPORT_RECURRING_CONTRIBUTOR_ANNUAL_COOKIE, SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE, ]; From 014a4c7a57e570897ab97579f583e78ca2524f6a Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 12 Dec 2024 15:28:50 +0000 Subject: [PATCH 69/84] Pass gallery count to card (#12996) * Add gallery count to model and pass through to card * log out gallery count for linter * Disable lint check --- dotcom-rendering/src/components/Card/Card.tsx | 3 +++ dotcom-rendering/src/components/FrontCard.tsx | 1 + dotcom-rendering/src/model/enhanceCards.ts | 2 +- dotcom-rendering/src/model/front-schema.json | 9 +++++++++ dotcom-rendering/src/model/tag-page-schema.json | 3 +++ dotcom-rendering/src/types/front.ts | 2 ++ 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index a2a3a62ccfd..aa3f3c26153 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -141,6 +141,7 @@ export type Props = { trailTextColour?: string; /** The square podcast series image, if it exists for a card */ podcastImage?: PodcastSeriesImage; + galleryCount?: number; }; const starWrapper = (cardHasImage: boolean) => css` @@ -340,6 +341,8 @@ export const Card = ({ trailTextSize, trailTextColour, podcastImage, + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Added in preparation for UI changes to display gallery count + galleryCount, }: Props) => { const hasSublinks = supportingContent && supportingContent.length > 0; const sublinkPosition = decideSublinkPosition( diff --git a/dotcom-rendering/src/components/FrontCard.tsx b/dotcom-rendering/src/components/FrontCard.tsx index 3b54ecb82e8..1eb93841c0d 100644 --- a/dotcom-rendering/src/components/FrontCard.tsx +++ b/dotcom-rendering/src/components/FrontCard.tsx @@ -55,6 +55,7 @@ export const FrontCard = (props: Props) => { slideshowImages: trail.slideshowImages, showLivePlayable: trail.showLivePlayable, showMainVideo: trail.showMainVideo, + galleryCount: trail.galleryCount, }; return Card({ ...defaultProps, ...cardProps }); diff --git a/dotcom-rendering/src/model/enhanceCards.ts b/dotcom-rendering/src/model/enhanceCards.ts index eff5e94b83d..9bf80a23db8 100644 --- a/dotcom-rendering/src/model/enhanceCards.ts +++ b/dotcom-rendering/src/model/enhanceCards.ts @@ -269,7 +269,6 @@ export const enhanceCards = ( const podcastImage = getPodcastSeriesImage(faciaCard); const isContributorTagPage = !!pageId && pageId.startsWith('profile/'); - return { format, dataLinkName, @@ -329,5 +328,6 @@ export const enhanceCards = ( }, }), podcastImage, + galleryCount: faciaCard.card.galleryCount, }; }); diff --git a/dotcom-rendering/src/model/front-schema.json b/dotcom-rendering/src/model/front-schema.json index 1b08fa3f4b6..0dcefd774f3 100644 --- a/dotcom-rendering/src/model/front-schema.json +++ b/dotcom-rendering/src/model/front-schema.json @@ -1031,6 +1031,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ @@ -1779,6 +1782,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ @@ -2527,6 +2533,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ diff --git a/dotcom-rendering/src/model/tag-page-schema.json b/dotcom-rendering/src/model/tag-page-schema.json index 610b9414df7..0cee409dec3 100644 --- a/dotcom-rendering/src/model/tag-page-schema.json +++ b/dotcom-rendering/src/model/tag-page-schema.json @@ -552,6 +552,9 @@ }, "isLive": { "type": "boolean" + }, + "galleryCount": { + "type": "number" } }, "required": [ diff --git a/dotcom-rendering/src/types/front.ts b/dotcom-rendering/src/types/front.ts index f2f0128aad4..fdd565d528f 100644 --- a/dotcom-rendering/src/types/front.ts +++ b/dotcom-rendering/src/types/front.ts @@ -290,6 +290,7 @@ export type FEFrontCard = { shortUrl: string; group: string; isLive: boolean; + galleryCount?: number; }; discussion: { isCommentable: boolean; @@ -348,6 +349,7 @@ export type DCRFrontCard = { slideshowImages?: DCRSlideshowImage[]; showMainVideo?: boolean; podcastImageSrc?: string; + galleryCount?: number; }; export type DCRSlideshowImage = { From 41737b0cde8988892ea62fefdf70fb3d164d1e4c Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Thu, 12 Dec 2024 16:12:58 +0000 Subject: [PATCH 70/84] Start Onwards content test --- .../src/experiments/tests/onwards-content-article.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/experiments/tests/onwards-content-article.ts b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts index 91bc88a7248..6ec9284817c 100644 --- a/dotcom-rendering/src/experiments/tests/onwards-content-article.ts +++ b/dotcom-rendering/src/experiments/tests/onwards-content-article.ts @@ -7,8 +7,8 @@ export const onwardsContentArticle: ABTest = { author: 'dotcom.platform@guardian.co.uk', description: 'Test the impact of showing the galleries onwards content component on article pages.', - audience: 0 / 100, - audienceOffset: 0 / 100, + audience: 50 / 100, + audienceOffset: 50 / 100, audienceCriteria: 'Article pages', successMeasure: 'Users are more likely to click a link in the onward content component.', From 411e92829b8c96887e68f3d13b5d1c597aa617b5 Mon Sep 17 00:00:00 2001 From: Daniel Clifton <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:13:59 +0000 Subject: [PATCH 71/84] Remove accreditation text (#12969) * Remove accreditation text * conflicts --- .../src/components/Titlepiece.importable.tsx | 40 ------------------- dotcom-rendering/src/paletteDeclarations.ts | 7 ---- 2 files changed, 47 deletions(-) diff --git a/dotcom-rendering/src/components/Titlepiece.importable.tsx b/dotcom-rendering/src/components/Titlepiece.importable.tsx index 497d852cf65..29e11681ac1 100644 --- a/dotcom-rendering/src/components/Titlepiece.importable.tsx +++ b/dotcom-rendering/src/components/Titlepiece.importable.tsx @@ -3,8 +3,6 @@ import { from, headlineBold14, space, - textSansBold14, - textSansBold17, until, visuallyHidden, } from '@guardian/source/foundations'; @@ -62,32 +60,6 @@ const slimNavEditionSwitcherOverrides = css` } `; -const accreditationStyles = css` - ${gridContent} - grid-row: 1; - justify-self: start; - align-self: start; - display: flex; - flex-wrap: wrap; - padding-top: 10px; - color: ${themePalette('--masthead-accreditation-text')}; - - ${textSansBold14} - - ${until.mobileMedium} { - display: none; - } - ${until.mobileLandscape} { - max-width: 100px; - } -`; - -const accreditationStylesFromLeftCol = css` - ${from.leftCol} { - ${textSansBold17} - } -`; - const logoStyles = css` ${gridMainColumn} grid-row: 1; @@ -540,18 +512,6 @@ export const Titlepiece = ({
                          - {/* Accreditation text */} - {!showSlimNav && editionId === 'UK' && ( - - News provider of the year - - )} - {/** Expanded menu checkbox */} { } }; -const mastheadAccreditationText: PaletteFunction = () => - sourcePalette.brandAlt[400]; - const pinnedPostBorderLight: PaletteFunction = ({ theme }) => { switch (theme) { case Pillar.News: @@ -6640,10 +6637,6 @@ const paletteColours = { light: liveBlockContainerBackgroundLight, dark: liveBlockContainerBackgroundDark, }, - '--masthead-accreditation-text': { - light: mastheadAccreditationText, - dark: mastheadAccreditationText, - }, '--masthead-nav-background': { light: mastheadNavBackground, dark: mastheadNavBackground, From dcdc75a0a43f8a5183318a8fe7a0448727cfce8a Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Fri, 13 Dec 2024 12:06:55 +0000 Subject: [PATCH 72/84] Fix images in onwards content test --- .../src/components/BigSixOnwardsContent.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx index 59b36a6b0ed..b26ad0cd33d 100644 --- a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import { from, headlineBold24, space } from '@guardian/source/foundations'; import { decideFormat } from '../lib/articleFormat'; +import { getSourceImageUrl } from '../lib/getSourceImageUrl_temp_fix'; import { useApi } from '../lib/useApi'; import { palette } from '../palette'; import type { DCRFrontCard } from '../types/front'; @@ -25,6 +26,8 @@ const containerStyles = css` ${from.leftCol} { flex-direction: row; + } + ${from.wide} { padding-right: 80px; } `; @@ -57,7 +60,11 @@ const convertFETrailToDcrTrail = ( format: decideFormat(trail.format), headline: trail.headline, image: { - src: trail.masterImage ?? '', + src: trail.masterImage + ? trail.masterImage + : trail.image + ? getSourceImageUrl(trail.image) + : '', altText: trail.linkText ?? '', }, isExternalLink: false, @@ -126,7 +133,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { imagePositionOnDesktop="right" imagePositionOnMobile="top" imageSize="large" - imageLoading="lazy" + imageLoading="eager" linkTo={trail.url} format={trail.format} headlineText={trail.headline} @@ -153,7 +160,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { imagePositionOnDesktop="top" imagePositionOnMobile="left" imageSize="small" - imageLoading="lazy" + imageLoading="eager" linkTo={trail.url} format={trail.format} headlineText={trail.headline} @@ -186,7 +193,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => { imagePositionOnDesktop="top" imagePositionOnMobile="left" imageSize="small" - imageLoading="lazy" + imageLoading="eager" linkTo={trail.url} format={trail.format} headlineText={trail.headline} From 785a89c4bb4c6f4409aee2f91c95f32a5c6d1ce7 Mon Sep 17 00:00:00 2001 From: Sue Burt Date: Fri, 13 Dec 2024 12:29:48 +0000 Subject: [PATCH 73/84] Update not-thrasher copy, colour for USEOY 'final countdown' sub-campaign (#12984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace GivingTuesday with SubCampaign throughout * Update copy * Add highlight, change bg, h2 colour, add dates of sub-campaign * Add story for final countdown * revert hover colour until the design team can give us a steer --------- Co-authored-by: Sue Burt <“sue.burt@guardian.co.uk”> --- .../src/components/UsEoy2024.stories.tsx | 12 +++ .../UsEoy2024Wrapper.importable.tsx | 95 ++++++++++--------- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/dotcom-rendering/src/components/UsEoy2024.stories.tsx b/dotcom-rendering/src/components/UsEoy2024.stories.tsx index b01695b837e..f953d1326f0 100644 --- a/dotcom-rendering/src/components/UsEoy2024.stories.tsx +++ b/dotcom-rendering/src/components/UsEoy2024.stories.tsx @@ -21,3 +21,15 @@ export const Default = { date: new Date('2024-11-26T00:00:00Z'), }, } satisfies Story; + +export const FinalCountdown = { + args: { + tickerData: { + total: 1000000, + goal: 2000000, + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + submitTrackingEvent: () => {}, + date: new Date('2024-12-26T00:00:00Z'), + }, +} satisfies Story; diff --git a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx index a2ea21fbfeb..a34cc676719 100644 --- a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx +++ b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx @@ -94,6 +94,10 @@ const styles = { ${textEgyptianBold17}; } `, + highlight: css` + background-color: inherit; + color: inherit; + `, ticker: css` margin-bottom: ${space[4]}px; `, @@ -113,10 +117,10 @@ const styles = { `, }; -const stylesGivingTuesday = { +const stylesSubCampaign = { container: css` /* stylelint-disable-next-line color-no-hex */ - background: #f3afd9; + background: #edb438; color: ${palette.neutral[100]}; `, grid: css` @@ -157,7 +161,7 @@ const stylesGivingTuesday = { } h2 { margin: ${space[2]}px 0 ${space[4]}px; - color: ${'#670055'}; + color: ${'#1A2835'}; ${headlineMedium24} ${from.tablet} { @@ -180,6 +184,11 @@ const stylesGivingTuesday = { } color: ${'#1A2835'}; `, + highlight: css` + background-color: ${'#670055'}; + color: ${'#F6F6F6'}; + padding-left: 2px; + `, ticker: css` margin-bottom: ${space[4]}px; `, @@ -211,7 +220,7 @@ const tickerSettings = { }, }; -const tickerSettingsGivingTuesday = { +const tickerSettingsSubCampaign = { currencySymbol: '$', copy: {}, tickerStylingSettings: { @@ -223,24 +232,24 @@ const tickerSettingsGivingTuesday = { }, }; -const heading = (isGivingTuesday: boolean) => { - return isGivingTuesday - ? 'This Giving Tuesday, give to the Guardian.' +const heading = (isSubCampaign: boolean) => { + return isSubCampaign + ? 'Last chance to support us this year' : 'Can you help us hit our goal?'; }; -const bodyCopy = (isGivingTuesday: boolean) => { - const givingTuesdayCopy = +const bodyCopy = (isSubCampaign: boolean) => { + const SubCampaignCopy = 'We’re funded by readers, not billionaires - which means we can publish factual journalism with no outside influence.'; const normalCopy = 'With no billionaire owner or shareholders pulling our strings, reader support keeps us fiercely independent.'; - return isGivingTuesday ? givingTuesdayCopy : normalCopy; + return isSubCampaign ? SubCampaignCopy : normalCopy; }; -const bodyCopyHighlightedText = (isGivingTuesday: boolean) => { - const givingTuesdayCopy = 'Help us raise $4m to keep going in 2025.'; +const bodyCopyHighlightedText = (isSubCampaign: boolean) => { + const SubCampaignCopy = 'Help us keep going in 2025.'; const normalCopy = 'Help us hit our most important annual fundraising goal so we can keep going.'; - return isGivingTuesday ? givingTuesdayCopy : normalCopy; + return isSubCampaign ? SubCampaignCopy : normalCopy; }; const getTickerData = async (): Promise => { @@ -316,48 +325,38 @@ export const UsEoy2024: ReactComponent = ({ currencySymbol, } = useChoiceCards(choiceCardAmounts, 'US', cta, cta); - const isGivingTuesday = - date >= new Date('2024-11-27T00:00:01') && - date < new Date('2024-12-03T23:59:59'); + const isSubCampaign = + date >= new Date('2024-12-20T00:00:01') && + date < new Date('2024-12-31T23:59:59'); return (
                          -
                          -
                          +
                          +
                          -

                          {heading(isGivingTuesday)}

                          +

                          {heading(isSubCampaign)}

                          @@ -366,24 +365,32 @@ export const UsEoy2024: ReactComponent = ({ copy={{}} tickerData={tickerData} tickerStylingSettings={ - isGivingTuesday - ? tickerSettingsGivingTuesday.tickerStylingSettings + isSubCampaign + ? tickerSettingsSubCampaign.tickerStylingSettings : tickerSettings.tickerStylingSettings } size={'medium'} />
                          - {bodyCopy(isGivingTuesday)} + {bodyCopy(isSubCampaign)} {' '} - {bodyCopyHighlightedText(isGivingTuesday)} + + {bodyCopyHighlightedText(isSubCampaign)} +
                          @@ -399,13 +406,13 @@ export const UsEoy2024: ReactComponent = ({ getCtaUrl={getCtaUrl} cssCtaOverides={buttonStyles({ default: { - backgroundColour: isGivingTuesday + backgroundColour: isSubCampaign ? '#016D67' : '#C41C1C', textColour: '#FFFFFF', }, hover: { - backgroundColour: isGivingTuesday + backgroundColour: isSubCampaign ? '#891414' : '#C41C1C', textColour: '#FFFFFF', From a23679f3e78e4c74f868fe5f929e81d5f29f3e6f Mon Sep 17 00:00:00 2001 From: Ravi <7014230+arelra@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:52:23 +0000 Subject: [PATCH 74/84] Reduce article stack size from 36 to 30 --- dotcom-rendering/cdk/bin/cdk.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/cdk/bin/cdk.ts b/dotcom-rendering/cdk/bin/cdk.ts index 14bdf2eadb4..3fbb57c2971 100644 --- a/dotcom-rendering/cdk/bin/cdk.ts +++ b/dotcom-rendering/cdk/bin/cdk.ts @@ -28,8 +28,8 @@ new RenderingCDKStack(cdkApp, 'ArticleRendering-PROD', { stage: 'PROD', domainName: 'article-rendering.guardianapis.com', scaling: { - minimumInstances: 36, - maximumInstances: 360, + minimumInstances: 30, + maximumInstances: 300, policies: { step: { cpu: cpuScalingSteps, From 94387a9695b1593101b5afba23e85aa7cc03d2a5 Mon Sep 17 00:00:00 2001 From: Sue Burt Date: Mon, 16 Dec 2024 12:14:36 +0000 Subject: [PATCH 75/84] update copy for goal achieved (#13014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sue Burt <“sue.burt@guardian.co.uk”> --- .../src/components/UsEoy2024Wrapper.importable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx index a34cc676719..d96f9653f87 100644 --- a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx +++ b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx @@ -235,7 +235,7 @@ const tickerSettingsSubCampaign = { const heading = (isSubCampaign: boolean) => { return isSubCampaign ? 'Last chance to support us this year' - : 'Can you help us hit our goal?'; + : 'Can you help us beat our goal?'; }; const bodyCopy = (isSubCampaign: boolean) => { const SubCampaignCopy = @@ -248,7 +248,7 @@ const bodyCopy = (isSubCampaign: boolean) => { const bodyCopyHighlightedText = (isSubCampaign: boolean) => { const SubCampaignCopy = 'Help us keep going in 2025.'; const normalCopy = - 'Help us hit our most important annual fundraising goal so we can keep going.'; + 'Help us raise as much as we can to power our journalism in 2025.'; return isSubCampaign ? SubCampaignCopy : normalCopy; }; From acc3eca205f66c397cfc8df9819fecd1814b01fb Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Tue, 17 Dec 2024 09:09:50 +0000 Subject: [PATCH 76/84] Correct types (#13016) --- dotcom-rendering/src/components/FrontCard.tsx | 1 + dotcom-rendering/src/types/front.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/FrontCard.tsx b/dotcom-rendering/src/components/FrontCard.tsx index 1eb93841c0d..6c5891e8a8a 100644 --- a/dotcom-rendering/src/components/FrontCard.tsx +++ b/dotcom-rendering/src/components/FrontCard.tsx @@ -56,6 +56,7 @@ export const FrontCard = (props: Props) => { showLivePlayable: trail.showLivePlayable, showMainVideo: trail.showMainVideo, galleryCount: trail.galleryCount, + podcastImage: trail.podcastImage, }; return Card({ ...defaultProps, ...cardProps }); diff --git a/dotcom-rendering/src/types/front.ts b/dotcom-rendering/src/types/front.ts index fdd565d528f..b09b700741f 100644 --- a/dotcom-rendering/src/types/front.ts +++ b/dotcom-rendering/src/types/front.ts @@ -11,7 +11,7 @@ import type { BoostLevel, Image, StarRating } from './content'; import type { FooterType } from './footer'; import type { FEFormat, FENavType } from './frontend'; import type { MainMedia } from './mainMedia'; -import type { FETagType } from './tag'; +import type { FETagType, PodcastSeriesImage } from './tag'; import type { Territory } from './territory'; import type { FETrailType, TrailType } from './trails'; @@ -348,8 +348,8 @@ export type DCRFrontCard = { branding?: Branding; slideshowImages?: DCRSlideshowImage[]; showMainVideo?: boolean; - podcastImageSrc?: string; galleryCount?: number; + podcastImage?: PodcastSeriesImage; }; export type DCRSlideshowImage = { From e32d554be21d374dd27c8f135fd2bc65f750f42e Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Tue, 17 Dec 2024 10:54:54 +0000 Subject: [PATCH 77/84] Introduce padding above top row to match fronts container --- dotcom-rendering/src/components/BigSixOnwardsContent.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx index b26ad0cd33d..6f7217805e6 100644 --- a/dotcom-rendering/src/components/BigSixOnwardsContent.tsx +++ b/dotcom-rendering/src/components/BigSixOnwardsContent.tsx @@ -32,6 +32,10 @@ const containerStyles = css` } `; +const cardsContainer = css` + padding-top: ${space[2]}px; +`; + const headerStyles = css` ${headlineBold24}; color: ${palette('--carousel-text')}; @@ -124,7 +128,7 @@ export const BigSixOnwardsContent = ({ url, discussionApiUrl }: Props) => {

                          {heading}

                          -
                          +
                            {firstSlice75.map((trail) => (
                          • From 83c6ae41d64da894486977cdc971bfc3700706fe Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Tue, 17 Dec 2024 12:56:05 +0000 Subject: [PATCH 78/84] only use podcast type if its a beta container (#13024) --- dotcom-rendering/src/components/Card/Card.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index aa3f3c26153..2431f62b3dc 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -195,6 +195,7 @@ const getMedia = ({ mainMedia, isPlayableMediaCard, podcastImage, + isBetaContainer, }: { imageUrl?: string; imageAltText?: string; @@ -204,6 +205,7 @@ const getMedia = ({ mainMedia?: MainMedia; isPlayableMediaCard?: boolean; podcastImage?: PodcastSeriesImage; + isBetaContainer: boolean; }) => { if (mainMedia && mainMedia.type === 'Video' && isPlayableMediaCard) { return { @@ -214,7 +216,7 @@ const getMedia = ({ } if (slideshowImages) return { type: 'slideshow', slideshowImages } as const; if (avatarUrl) return { type: 'avatar', avatarUrl } as const; - if (podcastImage) { + if (podcastImage && isBetaContainer) { return { type: 'podcast', podcastImage, @@ -436,6 +438,7 @@ export const Card = ({ mainMedia, isPlayableMediaCard, podcastImage, + isBetaContainer, }); // For opinion type cards with avatars (which aren't onwards content) From 331a94c8806e6779e4f5be50de956438d261daed Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Tue, 17 Dec 2024 14:20:48 +0000 Subject: [PATCH 79/84] Keep dependencies up to date Co-authored-by: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Co-authored-by: Marjan Kalanaki --- pnpm-lock.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a72b4251eb..a238575d840 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9514,8 +9514,8 @@ packages: resolution: {integrity: sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q==} dev: false - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + /cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} dependencies: path-key: 3.1.1 @@ -10973,7 +10973,7 @@ packages: '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 debug: 4.3.5 doctrine: 3.0.0 escape-string-regexp: 4.0.0 @@ -11082,7 +11082,7 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -11097,7 +11097,7 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 8.0.1 human-signals: 5.0.0 is-stream: 3.0.0 @@ -11512,7 +11512,7 @@ packages: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 dev: false @@ -14987,7 +14987,7 @@ packages: resolution: {integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==} dependencies: ansi-escapes: 4.3.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 dev: false /path-browserify@1.0.1: @@ -17920,7 +17920,7 @@ packages: '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 envinfo: 7.11.0 fastest-levenshtein: 1.0.16 import-local: 3.1.0 @@ -17955,7 +17955,7 @@ packages: '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1) colorette: 2.0.20 commander: 10.0.1 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 envinfo: 7.11.0 fastest-levenshtein: 1.0.16 import-local: 3.1.0 From 8eb43e49a8b38cb4925b2c228c863bef439536eb Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Wed, 18 Dec 2024 10:38:33 +0000 Subject: [PATCH 80/84] Rename video prop to better represent use case (#13023) --- .../src/components/Card/Card.stories.tsx | 10 +++++----- dotcom-rendering/src/components/Card/Card.tsx | 14 +++++++------- .../src/components/Carousel.importable.tsx | 2 +- .../src/components/FeatureCard.stories.tsx | 2 +- dotcom-rendering/src/components/FeatureCard.tsx | 12 ++++++------ .../src/components/FlexibleGeneral.tsx | 3 +++ .../src/components/FlexibleSpecial.tsx | 2 ++ dotcom-rendering/src/components/FrontCard.tsx | 2 +- .../components/ScrollableFeature.importable.tsx | 2 +- .../components/ScrollableSmall.importable.tsx | 1 + .../src/components/StaticFeatureTwo.tsx | 2 +- .../src/components/StaticMediumFour.tsx | 1 + .../src/components/SupportingContent.stories.tsx | 2 +- dotcom-rendering/src/lib/cardWrappers.tsx | 16 ++++++++-------- 14 files changed, 39 insertions(+), 32 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.stories.tsx b/dotcom-rendering/src/components/Card/Card.stories.tsx index 13ff33a0312..f190f9ba7cb 100644 --- a/dotcom-rendering/src/components/Card/Card.stories.tsx +++ b/dotcom-rendering/src/components/Card/Card.stories.tsx @@ -40,7 +40,7 @@ const basicCardProps: CardProps = { imagePositionOnDesktop: 'top', showAge: true, isExternalLink: false, - isPlayableMediaCard: true, + canPlayInline: true, imageLoading: 'eager', discussionApiUrl: 'https://discussion.theguardian.com/discussion-api/', showMainVideo: true, @@ -1001,7 +1001,7 @@ export const WhenVideoWithPlayButton = () => { }} imagePositionOnDesktop="top" mainMedia={mainVideo} - isPlayableMediaCard={false} + canPlayInline={false} />
                          @@ -1032,7 +1032,7 @@ export const WhenVideoWithPlayButton = () => { }} imagePositionOnDesktop="left" mainMedia={mainVideo} - isPlayableMediaCard={false} + canPlayInline={false} />
                        • @@ -1045,7 +1045,7 @@ export const WhenVideoWithPlayButton = () => { }} imagePositionOnDesktop="right" mainMedia={mainVideo} - isPlayableMediaCard={false} + canPlayInline={false} />
                        • @@ -1059,7 +1059,7 @@ export const WhenVideoWithPlayButton = () => { }} imagePositionOnDesktop="right" mainMedia={mainVideo} - isPlayableMediaCard={false} + canPlayInline={false} />
                        diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 2431f62b3dc..27a39eea5d3 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -98,7 +98,7 @@ export type Props = { * At 300px or below, the player will begin to lose functionality e.g. volume controls being omitted. * Youtube requires a minimum width 200px. */ - isPlayableMediaCard?: boolean; + canPlayInline?: boolean; kickerText?: string; showPulsingDot?: boolean; starRating?: Rating; @@ -193,7 +193,7 @@ const getMedia = ({ isCrossword, slideshowImages, mainMedia, - isPlayableMediaCard, + canPlayInline, podcastImage, isBetaContainer, }: { @@ -203,11 +203,11 @@ const getMedia = ({ isCrossword?: boolean; slideshowImages?: DCRSlideshowImage[]; mainMedia?: MainMedia; - isPlayableMediaCard?: boolean; + canPlayInline?: boolean; podcastImage?: PodcastSeriesImage; isBetaContainer: boolean; }) => { - if (mainMedia && mainMedia.type === 'Video' && isPlayableMediaCard) { + if (mainMedia && mainMedia.type === 'Video' && canPlayInline) { return { type: 'video', mainMedia, @@ -306,7 +306,7 @@ export const Card = ({ avatarUrl, showClock, mainMedia, - isPlayableMediaCard, + canPlayInline, kickerText, showPulsingDot, starRating, @@ -427,7 +427,7 @@ export const Card = ({ // If the card isn't playable, we need to show a play icon. // Otherwise, this is handled by the YoutubeAtom const showPlayIcon = - mainMedia?.type === 'Video' && !isPlayableMediaCard && showMainVideo; + mainMedia?.type === 'Video' && !canPlayInline && showMainVideo; const media = getMedia({ imageUrl: image?.src, @@ -436,7 +436,7 @@ export const Card = ({ isCrossword, slideshowImages, mainMedia, - isPlayableMediaCard, + canPlayInline, podcastImage, isBetaContainer, }); diff --git a/dotcom-rendering/src/components/Carousel.importable.tsx b/dotcom-rendering/src/components/Carousel.importable.tsx index 7d3bc6f8dff..cd88df7e693 100644 --- a/dotcom-rendering/src/components/Carousel.importable.tsx +++ b/dotcom-rendering/src/components/Carousel.importable.tsx @@ -533,7 +533,7 @@ const CarouselCard = ({ isExternalLink={false} mainMedia={mainMedia} minWidthInPixels={220} - isPlayableMediaCard={isVideoContainer} + canPlayInline={isVideoContainer} onwardsSource={onwardsSource} containerType={containerType} imageLoading={imageLoading} diff --git a/dotcom-rendering/src/components/FeatureCard.stories.tsx b/dotcom-rendering/src/components/FeatureCard.stories.tsx index 8cf330168a0..2e6df5ddb9d 100644 --- a/dotcom-rendering/src/components/FeatureCard.stories.tsx +++ b/dotcom-rendering/src/components/FeatureCard.stories.tsx @@ -20,7 +20,7 @@ const cardProps: CardProps = { }, imagePositionOnDesktop: 'top', isExternalLink: false, - isPlayableMediaCard: false, + canPlayInline: true, imageLoading: 'eager', discussionApiUrl: 'https://discussion.theguardian.com/discussion-api/', absoluteServerTimes: true, diff --git a/dotcom-rendering/src/components/FeatureCard.tsx b/dotcom-rendering/src/components/FeatureCard.tsx index 9b08c5de3d2..fec389228b6 100644 --- a/dotcom-rendering/src/components/FeatureCard.tsx +++ b/dotcom-rendering/src/components/FeatureCard.tsx @@ -56,7 +56,7 @@ export type Props = { * At 300px or below, the player will begin to lose functionality e.g. volume controls being omitted. * Youtube requires a minimum width 200px. */ - isPlayableMediaCard?: boolean; + canPlayInline?: boolean; kickerText?: string; showPulsingDot?: boolean; starRating?: Rating; @@ -166,14 +166,14 @@ const getMedia = ({ imageUrl, imageAltText, mainMedia, - isPlayableMediaCard, + canPlayInline, }: { imageUrl?: string; imageAltText?: string; mainMedia?: MainMedia; - isPlayableMediaCard?: boolean; + canPlayInline?: boolean; }) => { - if (mainMedia && mainMedia.type === 'Video' && isPlayableMediaCard) { + if (mainMedia && mainMedia.type === 'Video' && canPlayInline) { return { type: 'video', mainMedia, @@ -279,7 +279,7 @@ export const FeatureCard = ({ imageLoading, showClock, mainMedia, - isPlayableMediaCard, + canPlayInline, kickerText, showPulsingDot, dataLinkName, @@ -305,7 +305,7 @@ export const FeatureCard = ({ imageUrl: image?.src, imageAltText: image?.altText, mainMedia, - isPlayableMediaCard, + canPlayInline, }); return ( diff --git a/dotcom-rendering/src/components/FlexibleGeneral.tsx b/dotcom-rendering/src/components/FlexibleGeneral.tsx index 11f874b545d..d296ff707cb 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.tsx @@ -220,6 +220,7 @@ export const SplashCardLayout = ({ showTopBarDesktop={false} showTopBarMobile={true} trailTextSize={trailTextSize} + canPlayInline={true} />
                      @@ -329,6 +330,7 @@ export const BoostedCardLayout = ({ showTopBarDesktop={false} showTopBarMobile={true} liveUpdatesPosition={liveUpdatesPosition} + canPlayInline={true} />
                    @@ -413,6 +415,7 @@ export const StandardCardLayout = ({ } : undefined } + canPlayInline={false} />
                  • ); diff --git a/dotcom-rendering/src/components/FlexibleSpecial.tsx b/dotcom-rendering/src/components/FlexibleSpecial.tsx index 36de07c0b1d..ad29cdc47df 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.tsx @@ -163,6 +163,7 @@ export const OneCardLayout = ({ showTopBarDesktop={false} showTopBarMobile={true} trailTextSize={trailTextSize} + canPlayInline={true} />
                  @@ -218,6 +219,7 @@ const TwoCardOrFourCardLayout = ({ showLivePlayable={false} showTopBarDesktop={false} showTopBarMobile={true} + canPlayInline={false} />
                • ); diff --git a/dotcom-rendering/src/components/FrontCard.tsx b/dotcom-rendering/src/components/FrontCard.tsx index 6c5891e8a8a..5191f5ce889 100644 --- a/dotcom-rendering/src/components/FrontCard.tsx +++ b/dotcom-rendering/src/components/FrontCard.tsx @@ -42,7 +42,7 @@ export const FrontCard = (props: Props) => { showClock: false, image: trail.image, isCrossword: trail.isCrossword, - isPlayableMediaCard: true, + canPlayInline: true, starRating: trail.starRating, dataLinkName: trail.dataLinkName, snapData: trail.snapData, diff --git a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx index 17ead5b6432..653fc4924b4 100644 --- a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx @@ -53,7 +53,7 @@ export const ScrollableFeature = ({ /** TODO - implement show age */ showClock={false} image={card.image} - isPlayableMediaCard={true} + canPlayInline={true} starRating={card.starRating} dataLinkName={card.dataLinkName} discussionApiUrl={card.discussionApiUrl} diff --git a/dotcom-rendering/src/components/ScrollableSmall.importable.tsx b/dotcom-rendering/src/components/ScrollableSmall.importable.tsx index 2ad9059155a..c6e740720d1 100644 --- a/dotcom-rendering/src/components/ScrollableSmall.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableSmall.importable.tsx @@ -63,6 +63,7 @@ export const ScrollableSmall = ({ showLivePlayable={trail.showLivePlayable} showTopBarDesktop={false} showTopBarMobile={false} + canPlayInline={false} /> ); diff --git a/dotcom-rendering/src/components/StaticFeatureTwo.tsx b/dotcom-rendering/src/components/StaticFeatureTwo.tsx index d46959615d7..c321282b09a 100644 --- a/dotcom-rendering/src/components/StaticFeatureTwo.tsx +++ b/dotcom-rendering/src/components/StaticFeatureTwo.tsx @@ -50,7 +50,7 @@ export const StaticFeatureTwo = ({ /** TODO - implement show age */ showClock={false} image={card.image} - isPlayableMediaCard={true} + canPlayInline={true} starRating={card.starRating} dataLinkName={card.dataLinkName} discussionApiUrl={card.discussionApiUrl} diff --git a/dotcom-rendering/src/components/StaticMediumFour.tsx b/dotcom-rendering/src/components/StaticMediumFour.tsx index d822fc2d39a..56e4ebce1fc 100644 --- a/dotcom-rendering/src/components/StaticMediumFour.tsx +++ b/dotcom-rendering/src/components/StaticMediumFour.tsx @@ -57,6 +57,7 @@ export const StaticMediumFour = ({ showLivePlayable={false} showTopBarDesktop={false} showTopBarMobile={true} + canPlayInline={false} /> ); diff --git a/dotcom-rendering/src/components/SupportingContent.stories.tsx b/dotcom-rendering/src/components/SupportingContent.stories.tsx index 06065b9fb80..122470a39bd 100644 --- a/dotcom-rendering/src/components/SupportingContent.stories.tsx +++ b/dotcom-rendering/src/components/SupportingContent.stories.tsx @@ -33,7 +33,7 @@ const basicCardProps: CardProps = { imagePositionOnDesktop: 'top', isExternalLink: false, showLivePlayable: false, - isPlayableMediaCard: true, + canPlayInline: true, imageLoading: 'eager', discussionApiUrl: 'https://discussion.theguardian.com/discussion-api', absoluteServerTimes: true, diff --git a/dotcom-rendering/src/lib/cardWrappers.tsx b/dotcom-rendering/src/lib/cardWrappers.tsx index 7f0009e12c1..20754ebd4d5 100644 --- a/dotcom-rendering/src/lib/cardWrappers.tsx +++ b/dotcom-rendering/src/lib/cardWrappers.tsx @@ -351,7 +351,7 @@ export const Card25Media25 = ({ imageLoading={imageLoading} isTagPage={isTagPage} headlineSizes={{ desktop: 'xsmall', tablet: 'xxsmall' }} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -393,7 +393,7 @@ export const Card25Media25SmallHeadline = ({ imageLoading={imageLoading} isTagPage={isTagPage} headlineSizes={{ desktop: 'xxsmall', tablet: 'xxsmall' }} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -442,7 +442,7 @@ export const Card25Media25Tall = ({ : undefined } supportingContent={trail.supportingContent?.slice(0, 2)} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -483,7 +483,7 @@ export const Card25Media25TallNoTrail = ({ isTagPage={isTagPage} headlineSizes={{ desktop: 'xsmall', tablet: 'xxsmall' }} supportingContent={trail.supportingContent?.slice(0, 2)} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -524,7 +524,7 @@ export const Card25Media25TallSmallHeadline = ({ isTagPage={isTagPage} headlineSizes={{ desktop: 'xxsmall', tablet: 'xxsmall' }} supportingContent={trail.supportingContent?.slice(0, 2)} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -805,7 +805,7 @@ export const CardDefault = ({ imageLoading={'lazy'} avatarUrl={undefined} headlineSizes={{ desktop: 'xxsmall' }} - isPlayableMediaCard={false} + canPlayInline={false} isTagPage={isTagPage} trailTextColour={palette('--card-headline')} /> @@ -844,7 +844,7 @@ export const CardDefaultMedia = ({ imageLoading={imageLoading} isTagPage={isTagPage} headlineSizes={{ desktop: 'xxsmall' }} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); @@ -882,7 +882,7 @@ export const CardDefaultMediaMobile = ({ imageLoading={imageLoading} isTagPage={isTagPage} headlineSizes={{ desktop: 'xxsmall' }} - isPlayableMediaCard={false} + canPlayInline={false} trailTextColour={palette('--card-headline')} /> ); From 37810172408c1472b766e5eb24b6ed9a0c9d088c Mon Sep 17 00:00:00 2001 From: James Mockett <1166188+jamesmockett@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:01:14 +0000 Subject: [PATCH 81/84] Media Cards: Update palette (#12980) * Update media card light and dark mode palettes * Palette entry for media card backgrounds * Palette entry for media icons * Special palette overrides for media cards * Apply extra padding to all media cards * Remove media card kicker colour variations * Update `cardHasDarkBackground` for media cards * Adjust content padding when in flexible container --- dotcom-rendering/src/components/Card/Card.tsx | 19 ++-- .../Card/components/ContentWrapper.tsx | 38 ++++++-- .../src/components/ContainerOverrides.tsx | 18 ++++ dotcom-rendering/src/components/MediaMeta.tsx | 2 +- dotcom-rendering/src/lib/cardHelpers.test.ts | 2 +- dotcom-rendering/src/lib/cardHelpers.ts | 2 +- dotcom-rendering/src/paletteDeclarations.ts | 88 ++++++++----------- 7 files changed, 103 insertions(+), 66 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 27a39eea5d3..568562a1b4b 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -447,10 +447,14 @@ export const Card = ({ isOpinion && !isOnwardContent && media?.type === 'avatar'; /** - * Some cards in standard containers have contrasting background colours. - * We need to add additional padding to these cards to keep the text readable. - */ - const hasBackgroundColour = !containerPalette && isMediaCard(format); +- * Media cards have contrasting background colours. We add additional + * padding to these cards to keep the text readable. +- */ + const hasBackgroundColour = isMediaCard(format); + + const backgroundColour = hasBackgroundColour + ? palette('--card-media-background') + : palette('--card-background'); /* Whilst we migrate to the new container types, we need to check which container we are in. */ const isFlexibleContainer = @@ -494,7 +498,7 @@ export const Card = ({ /** Determines the gap of between card components based on card properties */ const getGapSize = (): GapSize => { if (isOnwardContent) return 'none'; - if (hasBackgroundColour) return 'tiny'; + if (hasBackgroundColour && !isFlexibleContainer) return 'tiny'; if (!!isFlexSplash || (isFlexibleContainer && imageSize === 'jumbo')) { return 'small'; } @@ -580,7 +584,7 @@ export const Card = ({ css={css` padding-bottom: ${space[5]}px; `} - style={{ backgroundColor: palette('--card-background') }} + style={{ backgroundColor: backgroundColour }} > {/* This div is needed to keep the headline and trail text justified at the start */}
                  { + /** + * If we're in a flexible container there is a 20px gap between the image + * and content. We don't apply padding to the content on the same edge as + * the image so the content is aligned with the grid. + */ + if (isFlexibleContainer && imagePosition === 'left') { + return css` + padding: ${space[1]}px ${space[1]}px ${space[1]}px 0; + `; + } + + if (isFlexibleContainer && imagePosition === 'right') { + return css` + padding: ${space[1]}px 0 ${space[1]}px ${space[1]}px; + `; + } + + return css` + padding: ${space[1]}px; + `; +}; + type Props = { children: React.ReactNode; imageType?: CardImageType; @@ -68,6 +94,7 @@ type Props = { imagePositionOnDesktop: ImagePositionType; hasBackgroundColour?: boolean; isOnwardContent?: boolean; + isFlexibleContainer?: boolean; }; export const ContentWrapper = ({ @@ -77,6 +104,7 @@ export const ContentWrapper = ({ imagePositionOnDesktop, hasBackgroundColour, isOnwardContent, + isFlexibleContainer = false, }: Props) => { const isHorizontalOnDesktop = imagePositionOnDesktop === 'left' || imagePositionOnDesktop === 'right'; @@ -85,14 +113,10 @@ export const ContentWrapper = ({
                  {children} diff --git a/dotcom-rendering/src/components/ContainerOverrides.tsx b/dotcom-rendering/src/components/ContainerOverrides.tsx index 32b0517178b..2d81685ac19 100644 --- a/dotcom-rendering/src/components/ContainerOverrides.tsx +++ b/dotcom-rendering/src/components/ContainerOverrides.tsx @@ -580,6 +580,16 @@ const cardBackgroundDark: ContainerFunction = (containerPalette) => { } }; +const cardMediaBackgroundLight: ContainerFunction = (containerPalette) => + transparentColour(cardHeadlineLight(containerPalette), 0.1); +const cardMediaBackgroundDark: ContainerFunction = (containerPalette) => + transparentColour(cardHeadlineDark(containerPalette), 0.1); + +const cardMediaIconLight: ContainerFunction = (containerPalette) => + cardBackgroundLight(containerPalette); +const cardMediaIconDark: ContainerFunction = (containerPalette) => + cardBackgroundDark(containerPalette); + const sectionBackgroundLight: ContainerFunction = (containerPalette) => { switch (containerPalette) { case 'InvestigationPalette': @@ -1069,6 +1079,14 @@ const containerColours = { light: cardKickerTextLight, dark: cardKickerTextDark, }, + '--card-media-background': { + light: cardMediaBackgroundLight, + dark: cardMediaBackgroundDark, + }, + '--card-media-icon': { + light: cardMediaIconLight, + dark: cardMediaIconDark, + }, '--card-sublinks-background': { light: cardSublinksBackgroundLight, dark: cardSublinksBackgroundDark, diff --git a/dotcom-rendering/src/components/MediaMeta.tsx b/dotcom-rendering/src/components/MediaMeta.tsx index de5554f6a77..41e68063479 100644 --- a/dotcom-rendering/src/components/MediaMeta.tsx +++ b/dotcom-rendering/src/components/MediaMeta.tsx @@ -29,7 +29,7 @@ const iconWrapperStyles = (hasKicker: boolean) => css` margin-right: auto; margin-top: 2px; display: block; - fill: ${themePalette('--card-background')}; + fill: ${themePalette('--card-media-icon')}; } `; diff --git a/dotcom-rendering/src/lib/cardHelpers.test.ts b/dotcom-rendering/src/lib/cardHelpers.test.ts index c0f8339dcfb..aee50798179 100644 --- a/dotcom-rendering/src/lib/cardHelpers.test.ts +++ b/dotcom-rendering/src/lib/cardHelpers.test.ts @@ -33,7 +33,7 @@ describe('cardHasDarkBackground', () => { { format: galleryFormat, containerPalette: undefined, - expectedResult: true, + expectedResult: false, }, { format: pictureFormat, diff --git a/dotcom-rendering/src/lib/cardHelpers.ts b/dotcom-rendering/src/lib/cardHelpers.ts index 9f204ae3577..0bc491e6a29 100644 --- a/dotcom-rendering/src/lib/cardHelpers.ts +++ b/dotcom-rendering/src/lib/cardHelpers.ts @@ -40,7 +40,7 @@ export const cardHasDarkBackground = ( case 'PodcastPalette': // If no containerPalette provided, card is in a standard container case undefined: { - return isMediaCard(format); + return false; } } }; diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 5d095e4ff2e..30c15bcd8c6 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -22,7 +22,6 @@ import { type ArticleTheme, Pillar, } from './lib/articleFormat'; -import { isMediaCard } from './lib/cardHelpers'; import { transparentColour } from './lib/transparentColour'; // ----- Palette Functions ----- // @@ -2479,21 +2478,28 @@ const cardBorderSupportingLight: PaletteFunction = () => const cardBorderSupportingDark: PaletteFunction = () => sourcePalette.neutral[46]; -const cardMetaTextLight: PaletteFunction = (format) => - isMediaCard(format) ? sourcePalette.neutral[86] : sourcePalette.neutral[46]; +const cardMetaTextLight: PaletteFunction = () => sourcePalette.neutral[46]; const cardMetaTextDark: PaletteFunction = () => sourcePalette.neutral[60]; -const cardBackground: PaletteFunction = (format) => - isMediaCard(format) ? sourcePalette.neutral[20] : 'transparent'; +const cardBackgroundLight: PaletteFunction = () => 'transparent'; +const cardBackgroundDark: PaletteFunction = () => 'transparent'; -const cardHeadlineTextLight: PaletteFunction = (format) => - isMediaCard(format) ? sourcePalette.neutral[100] : sourcePalette.neutral[7]; +const cardMediaBackgroundLight: PaletteFunction = () => + sourcePalette.neutral[97]; +const cardMediaBackgroundDark: PaletteFunction = () => + sourcePalette.neutral[20]; + +const cardMediaIconLight: PaletteFunction = (format) => + cardMediaBackgroundLight(format); +const cardMediaIconDark: PaletteFunction = (format) => + cardMediaBackgroundDark(format); + +const cardHeadlineTextLight: PaletteFunction = () => sourcePalette.neutral[7]; const cardTextDark: PaletteFunction = () => sourcePalette.neutral[86]; -const cardTrailTextLight: PaletteFunction = (format) => - isMediaCard(format) ? sourcePalette.neutral[86] : sourcePalette.neutral[38]; +const cardTrailTextLight: PaletteFunction = () => sourcePalette.neutral[38]; const cardTrailTextDark: PaletteFunction = () => sourcePalette.neutral[73]; const liveKickerBackgroundLight: PaletteFunction = (format) => { @@ -2539,44 +2545,20 @@ const liveKickerPulsingDot: PaletteFunction = () => transparentColour(sourcePalette.neutral[97], 0.75); const cardKickerTextLight: PaletteFunction = (format) => { - switch (format.design) { - case ArticleDesign.Gallery: - case ArticleDesign.Audio: - case ArticleDesign.Video: - switch (format.theme) { - case Pillar.News: - return sourcePalette.news[550]; - case Pillar.Sport: - return sourcePalette.sport[600]; - case Pillar.Opinion: - return sourcePalette.opinion[550]; - case Pillar.Lifestyle: - return sourcePalette.lifestyle[500]; - case Pillar.Culture: - return sourcePalette.culture[500]; - case ArticleSpecial.Labs: - return sourcePalette.labs[400]; - case ArticleSpecial.SpecialReport: - return sourcePalette.news[400]; - case ArticleSpecial.SpecialReportAlt: - return sourcePalette.specialReportAlt[200]; - } - default: - switch (format.theme) { - case Pillar.Opinion: - return pillarPalette(format.theme, 300); - case Pillar.Sport: - case Pillar.Culture: - case Pillar.Lifestyle: - case Pillar.News: - return pillarPalette(format.theme, 400); - case ArticleSpecial.Labs: - return sourcePalette.labs[200]; - case ArticleSpecial.SpecialReport: - return sourcePalette.news[400]; - case ArticleSpecial.SpecialReportAlt: - return sourcePalette.specialReportAlt[200]; - } + switch (format.theme) { + case Pillar.Opinion: + return pillarPalette(format.theme, 300); + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: + case Pillar.News: + return pillarPalette(format.theme, 400); + case ArticleSpecial.Labs: + return sourcePalette.labs[200]; + case ArticleSpecial.SpecialReport: + return sourcePalette.news[400]; + case ArticleSpecial.SpecialReportAlt: + return sourcePalette.specialReportAlt[200]; } }; @@ -6061,8 +6043,8 @@ const paletteColours = { dark: captionTextDark, }, '--card-background': { - light: cardBackground, - dark: cardBackground, + light: cardBackgroundLight, + dark: cardBackgroundDark, }, '--card-background-hover': { light: cardBackgroundHover, @@ -6088,6 +6070,14 @@ const paletteColours = { light: cardKickerTextLight, dark: cardKickerTextDark, }, + '--card-media-background': { + light: cardMediaBackgroundLight, + dark: cardMediaBackgroundDark, + }, + '--card-media-icon': { + light: cardMediaIconLight, + dark: cardMediaIconDark, + }, '--card-sublinks-background': { light: cardSublinksBackgroundLight, dark: cardSublinksBackgroundDark, From 344d00c9422e2e36c77702022c2855e19c5f0b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLAKSHMIRPILLAI=E2=80=9D?= <“luxmi.r.pillai@gmail.com”> Date: Thu, 19 Dec 2024 12:56:58 +0000 Subject: [PATCH 82/84] Change the highlight text color and CTA hoover color --- .../src/components/UsEoy2024Wrapper.importable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx index d96f9653f87..9e10212c6c0 100644 --- a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx +++ b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx @@ -185,7 +185,7 @@ const stylesSubCampaign = { color: ${'#1A2835'}; `, highlight: css` - background-color: ${'#670055'}; + background-color: ${'#016D67'}; color: ${'#F6F6F6'}; padding-left: 2px; `, @@ -413,7 +413,7 @@ export const UsEoy2024: ReactComponent = ({ }, hover: { backgroundColour: isSubCampaign - ? '#891414' + ? '#01544F' : '#C41C1C', textColour: '#FFFFFF', }, From f43b035adaced75a744067e0cbba8b1277b47499 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 19 Dec 2024 14:48:12 +0000 Subject: [PATCH 83/84] Add 1:1 padding option for card picture (#13039) --- dotcom-rendering/src/components/CardPicture.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotcom-rendering/src/components/CardPicture.tsx b/dotcom-rendering/src/components/CardPicture.tsx index 772d9ec599f..9b251c2dc96 100644 --- a/dotcom-rendering/src/components/CardPicture.tsx +++ b/dotcom-rendering/src/components/CardPicture.tsx @@ -104,6 +104,8 @@ const getAspectRatioPadding = (aspectRatio?: AspectRatio): string => { return '80%'; case '4:5': return '125%'; + case '1:1': + return '100%'; case '5:3': default: return '60%'; From e603a00c046629a3533deda9013effa48aee111c Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 19 Dec 2024 14:48:42 +0000 Subject: [PATCH 84/84] Media card alignment (#13040) * Always position image on the top if its a media card * Always inner headline if its a media card * remove src from path * fix linting --- dotcom-rendering/src/components/Card/Card.tsx | 18 ++++++----- .../src/components/FlexibleGeneral.tsx | 21 ++++++++----- .../src/components/FlexibleSpecial.tsx | 31 +++++++++++++------ .../ScrollableMedium.importable.tsx | 8 +++-- .../src/components/StaticMediumFour.tsx | 5 ++- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 568562a1b4b..7b0679ac80b 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -7,7 +7,7 @@ import { } from '@guardian/source/foundations'; import { Hide, Link } from '@guardian/source/react-components'; import { ArticleDesign, type ArticleFormat } from '../../lib/articleFormat'; -import { isMediaCard } from '../../lib/cardHelpers'; +import { isMediaCard as isAMediaCard } from '../../lib/cardHelpers'; import { getZIndex } from '../../lib/getZIndex'; import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../../lib/useCommentCount'; import { palette } from '../../palette'; @@ -260,11 +260,14 @@ const getHeadlinePosition = ({ isFlexSplash, containerType, showLivePlayable, + isMediaCard, }: { containerType?: DCRContainerType; isFlexSplash?: boolean; showLivePlayable: boolean; + isMediaCard: boolean; }) => { + if (isMediaCard) return 'inner'; if (containerType === 'flexible/special' && isFlexSplash) { return 'outer'; } @@ -450,9 +453,9 @@ export const Card = ({ - * Media cards have contrasting background colours. We add additional * padding to these cards to keep the text readable. - */ - const hasBackgroundColour = isMediaCard(format); + const isMediaCard = isAMediaCard(format); - const backgroundColour = hasBackgroundColour + const backgroundColour = isMediaCard ? palette('--card-media-background') : palette('--card-background'); @@ -479,6 +482,7 @@ export const Card = ({ containerType, isFlexSplash, showLivePlayable, + isMediaCard, }); const hideTrailTextUntil = () => { @@ -498,7 +502,7 @@ export const Card = ({ /** Determines the gap of between card components based on card properties */ const getGapSize = (): GapSize => { if (isOnwardContent) return 'none'; - if (hasBackgroundColour && !isFlexibleContainer) return 'tiny'; + if (isMediaCard && !isFlexibleContainer) return 'tiny'; if (!!isFlexSplash || (isFlexibleContainer && imageSize === 'jumbo')) { return 'small'; } @@ -812,7 +816,7 @@ export const Card = ({ imageType={media?.type} imageSize={imageSize} imagePositionOnDesktop={imagePositionOnDesktop} - hasBackgroundColour={hasBackgroundColour} + hasBackgroundColour={isMediaCard} isOnwardContent={isOnwardContent} isFlexibleContainer={isFlexibleContainer} > @@ -943,9 +947,7 @@ export const Card = ({
                  {showLivePlayable && liveUpdatesPosition === 'outer' && ( diff --git a/dotcom-rendering/src/components/FlexibleGeneral.tsx b/dotcom-rendering/src/components/FlexibleGeneral.tsx index d296ff707cb..c13a3a5cd21 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import { palette } from '../palette'; import type { BoostLevel } from '../types/content'; import type { @@ -86,6 +87,7 @@ type BoostedSplashProperties = { const decideSplashCardProperties = ( boostLevel: BoostLevel, supportingContentLength: number, + mediaCard: boolean, ): BoostedSplashProperties => { switch (boostLevel) { // boostedfont sizing @@ -98,7 +100,7 @@ const decideSplashCardProperties = ( mobile: 'medium', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'large', supportingContentAlignment: supportingContentLength >= 4 ? 'horizontal' : 'vertical', @@ -113,7 +115,7 @@ const decideSplashCardProperties = ( mobile: 'large', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: supportingContentLength >= 4 ? 'horizontal' : 'vertical', @@ -127,8 +129,8 @@ const decideSplashCardProperties = ( tablet: 'xlarge', mobile: 'xlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -141,8 +143,8 @@ const decideSplashCardProperties = ( tablet: 'xlarge', mobile: 'xxlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -182,6 +184,7 @@ export const SplashCardLayout = ({ } = decideSplashCardProperties( card.boostLevel ?? 'default', card.supportingContent?.length ?? 0, + isMediaCard(card.format), ); return ( @@ -312,8 +315,10 @@ export const BoostedCardLayout = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} headlineSizes={headlineSizes} - imagePositionOnDesktop={'right'} - imagePositionOnMobile={'bottom'} + imagePositionOnDesktop="right" + imagePositionOnMobile={ + isMediaCard(card.format) ? 'top' : 'bottom' + } imageSize={imageSize} trailText={card.trailText} supportingContent={card.supportingContent} diff --git a/dotcom-rendering/src/components/FlexibleSpecial.tsx b/dotcom-rendering/src/components/FlexibleSpecial.tsx index ad29cdc47df..9b09bb367a5 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import type { BoostLevel } from '../types/content'; import type { AspectRatio, @@ -42,6 +43,7 @@ type BoostProperties = { const determineCardProperties = ( boostLevel: BoostLevel, supportingContentLength: number, + mediaCard: boolean, ): BoostProperties => { switch (boostLevel) { // The default boost level is equal to no boost. It is the same as the default card layout. @@ -53,7 +55,7 @@ const determineCardProperties = ( mobile: 'medium', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'large', supportingContentAlignment: supportingContentLength >= 3 ? 'horizontal' : 'vertical', @@ -69,7 +71,7 @@ const determineCardProperties = ( mobile: 'large', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: supportingContentLength >= 3 ? 'horizontal' : 'vertical', @@ -83,8 +85,8 @@ const determineCardProperties = ( tablet: 'xlarge', mobile: 'xlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -97,8 +99,8 @@ const determineCardProperties = ( tablet: 'xxlarge', mobile: 'xxlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -137,6 +139,7 @@ export const OneCardLayout = ({ } = determineCardProperties( card.boostLevel ?? 'default', card.supportingContent?.length ?? 0, + isMediaCard(card.format), ); return (
                    @@ -170,6 +173,15 @@ export const OneCardLayout = ({ ); }; +const getImagePosition = ( + hasTwoOrFewerCards: boolean, + isAMediaCard: boolean, +) => { + if (isAMediaCard && !hasTwoOrFewerCards) return 'top'; + if (hasTwoOrFewerCards) return 'left'; + return 'bottom'; +}; + const TwoCardOrFourCardLayout = ({ cards, containerPalette, @@ -208,9 +220,10 @@ const TwoCardOrFourCardLayout = ({ absoluteServerTimes={absoluteServerTimes} image={showImage ? card.image : undefined} imageLoading={imageLoading} - imagePositionOnDesktop={ - hasTwoOrFewerCards ? 'left' : 'bottom' - } + imagePositionOnDesktop={getImagePosition( + hasTwoOrFewerCards, + isMediaCard(card.format), + )} /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} imageSize={'medium'} diff --git a/dotcom-rendering/src/components/ScrollableMedium.importable.tsx b/dotcom-rendering/src/components/ScrollableMedium.importable.tsx index c1797103f4d..7045ef4a38f 100644 --- a/dotcom-rendering/src/components/ScrollableMedium.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableMedium.importable.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import type { AspectRatio, DCRContainerPalette, @@ -40,6 +41,9 @@ export const ScrollableMedium = ({ visibleCardsOnTablet={4} > {trails.map((trail) => { + const imagePosition = isMediaCard(trail.format) + ? 'top' + : 'bottom'; return (