Skip to content

Commit

Permalink
Merge pull request #517 from DFE-Digital/mattb/SWCD-2429-asset-downlo…
Browse files Browse the repository at this point in the history
…ad-component

Mattb/swcd 2429 asset download component
  • Loading branch information
mattb-hippo authored Sep 30, 2024
2 parents 0789bb5 + efcd179 commit 0dbff12
Show file tree
Hide file tree
Showing 14 changed files with 544 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ public class EntityResolverTests
[Test]
[TestCase("accordion", typeof(Accordion))]
[TestCase("accordionSection", typeof(AccordionSection))]
[TestCase("areaOfPractice", typeof(AreaOfPractice))]
[TestCase("areaOfPracticeList", typeof(AreaOfPracticeList))]
[TestCase("applicationFeature", typeof(ApplicationFeature))]
[TestCase("applicationFeatures", typeof(ApplicationFeatures))]
[TestCase("areaOfPractice", typeof(AreaOfPractice))]
[TestCase("areaOfPracticeList", typeof(AreaOfPracticeList))]
[TestCase("assetDownload", typeof(AssetDownload))]
[TestCase("audioResource", typeof(AudioResource))]
[TestCase("backToTop", typeof(BackToTop))]
[TestCase("columnLayout", typeof(ColumnLayout))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public partial class PartialsFactoryTests
new object[] { new AccordionSection(), "_AccordionSection" },
new object[] { new AreaOfPractice(), "_AreaOfPractice" },
new object[] { new AreaOfPracticeList(), "_AreaOfPracticeList" },
new object[] { new AssetDownload(), "_AssetDownload" },
new object[] { new AudioResource(), "_AudioResource" },
new object[] { new BackToTop(), "_BackToTop" },
new object[] { new ColumnLayout(), "_ColumnLayout" },
Expand Down
5 changes: 3 additions & 2 deletions Childrens-Social-Care-CPD/Contentful/EntityResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ public Type Resolve(string contentTypeId)
{
"accordion" => typeof(Accordion),
"accordionSection" => typeof(AccordionSection),
"areaOfPractice" => typeof(AreaOfPractice),
"areaOfPracticeList" => typeof(AreaOfPracticeList),
"applicationFeature" => typeof(ApplicationFeature),
"applicationFeatures" => typeof(ApplicationFeatures),
"areaOfPractice" => typeof(AreaOfPractice),
"areaOfPracticeList" => typeof(AreaOfPracticeList),
"assetDownload" => typeof(AssetDownload),
"audioResource" => typeof(AudioResource),
"backToTop" => typeof(BackToTop),
"columnLayout" => typeof(ColumnLayout),
Expand Down
9 changes: 9 additions & 0 deletions Childrens-Social-Care-CPD/Contentful/Models/AssetDownload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Contentful.Core.Models;

namespace Childrens_Social_Care_CPD.Contentful.Models;

public class AssetDownload : IContent
{
public string LinkText { get; set; }
public Asset Asset { get; set; }
}
1 change: 1 addition & 0 deletions Childrens-Social-Care-CPD/Contentful/PartialsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static string GetPartialFor(IContent item)
AccordionSection => "_AccordionSection",
AreaOfPractice => "_AreaOfPractice",
AreaOfPracticeList => "_AreaOfPracticeList",
AssetDownload => "_AssetDownload",
AudioResource => "_AudioResource",
BackToTop => "_BackToTop",
ColumnLayout => "_ColumnLayout",
Expand Down
155 changes: 155 additions & 0 deletions Childrens-Social-Care-CPD/Views/Shared/_AssetDownload.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
@using Childrens_Social_Care_CPD.Contentful.Models;
@using System.Collections.Generic;
@using System.Text.RegularExpressions

@model AssetDownload

@functions {

private string FileSizeToHumanReadable (long filesize)
{
long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024;
double size = filesize;
string suffix = nameof(B);

if (filesize >= TB)
{
size = ConvertFileSize(filesize, TB);
suffix = nameof(TB);
}
else if (filesize >= GB)
{
size = ConvertFileSize(filesize, GB);
suffix = nameof(GB);
}
else if (filesize >= MB)
{
size = ConvertFileSize(filesize, MB);
suffix = nameof(MB);
}
else if (filesize >= KB)
{
size = ConvertFileSize(filesize, KB);
suffix = nameof(KB);
}

return $"{size} {suffix}";
}

private double ConvertFileSize(long filesize, long magnitude)
{
return Math.Round((double)filesize / magnitude, 2);
}

private string FileTypeToHumanReadable (string fileName, string contentType) {

var excelFileTypeString = "MS Excel Spreadsheet";
var wordFileTypeString = "MS Word Document";
var pdfFileTypeString = "<abbr title=\"Portable Document Format\" class=\"gem-c-attachment__abbr\">PDF</abbr>";
var odsFileTypeString = "ODS";

if (FileIsExcel(fileName, contentType)) return excelFileTypeString;
if (FileIsWord(fileName, contentType)) return wordFileTypeString;
if (FileIsODS(fileName, contentType)) return odsFileTypeString;
if (contentType == "application/pdf") return pdfFileTypeString;
return "Unknown file type";
}

private string FileTypeIconSVG (string fileName, string contentType)
{
var excelSVGMarkup = @"<svg class=""gem-c-attachment__thumbnail-image gem-c-attachment__thumbnail-image--spreadsheet"" version=""1.1"" viewBox=""0 0 99 140"" width=""99"" height=""140"" aria-hidden=""true"">
<path d=""M12 12h75v27H12zm0 47h18.75v63H12zm55 2v59H51V61h16m2-2H49v63h20V59z"" stroke-width=""0""></path>
<path d=""M49 61.05V120H32.8V61.05H49m2-2H30.75v63H51V59zm34 2V120H69.05V61.05H85m2-2H67v63h20V59z"" stroke-width=""0""></path>
<path d=""M30 68.5h56.5M30 77.34h56.5M30 112.7h56.5M30 95.02h56.5M30 86.18h56.5M30 103.86h56.5"" fill=""none"" stroke-miterlimit=""10"" stroke-width=""2""></path>
</svg>";

var wordSVGMarkup = @"<svg class=""gem-c-attachment__thumbnail-image gem-c-attachment__thumbnail-image--document"" version=""1.1"" viewBox=""0 0 99 140"" width=""99"" height=""140"" aria-hidden=""true"">
<path d=""M12 12h75v27H12zM12 59h9v9h-9zM12 77h9v9h-9zM12 95h9v9h-9zM12 113h9v9h-9zM30 59h57v9H30zM30 77h39v9H30zM30 95h57v9H30zM30 113h48v9H30z"" stroke-width=""0""></path>
</svg>";

var pdfSVGMarkup = @"<svg class=""gem-c-attachment__thumbnail-image gem-c-attachment__thumbnail-image--pdf"" version=""1.1"" viewBox=""0 0 99 140"">
<path d=""M12,59h9v9h-9v-9ZM30,77v9h39v-9H30ZM30,104h57v-9H30v9ZM30,122h48v-9H30v9ZM12,86h9v-9h-9v9ZM12,122h9v-9h-9v9ZM12,104h9v-9h-9v9ZM30,68h57v-9H30v9ZM35.2,21c-.3-.2-.6-.4-1-.5s-.8-.2-1.3-.2h-2.6v5.5h2.6c.5,0,1,0,1.3-.2s.7-.3,1-.5c.3-.2.5-.5.7-.8s.2-.7.2-1.1h0c0-.5,0-.9-.2-1.2-.2-.3-.4-.6-.7-.8h0ZM52.4,21.9c-.4-.5-1-.9-1.6-1.1-.7-.3-1.5-.4-2.4-.4h-2.7v11.5h2.7c.9,0,1.7-.1,2.4-.4s1.2-.7,1.6-1.1c.4-.5.7-1.1.9-1.7.2-.7.3-1.4.3-2.1v-.7c0-.8,0-1.5-.3-2.1-.2-.6-.5-1.2-.9-1.7h0ZM87,12v27H12V12h75ZM39.8,23c0-.9-.1-1.6-.4-2.4-.3-.7-.7-1.4-1.2-1.9-.5-.5-1.2-1-1.9-1.2-.8-.3-1.6-.5-2.5-.5h-7.2v17.9h3.6v-6h3.2c.9,0,1.8-.1,2.6-.5.8-.3,1.4-.7,2-1.2.5-.5,1-1.2,1.3-1.9.3-.7.5-1.5.5-2.4h0ZM57.4,26c0-1.3-.2-2.6-.5-3.7s-.9-2-1.6-2.8c-.7-.8-1.6-1.4-2.7-1.8-1.1-.4-2.3-.6-3.8-.6h-6.6v17.9h6.6c1.5,0,2.7-.2,3.8-.6,1.1-.4,2-1,2.7-1.8.7-.8,1.3-1.7,1.6-2.8.4-1.1.5-2.3.5-3.7h0ZM72.4,17.1h-12.1v17.9h3.6v-7.5h7.8v-3.3h-7.8v-3.8h8.4s0-3.3,0-3.3Z""></path>
</svg>";

if (FileIsExcel(fileName, contentType) || FileIsODS(fileName, contentType)) return excelSVGMarkup;
if (FileIsWord(fileName, contentType)) return wordSVGMarkup;
if (contentType == "application/pdf") return pdfSVGMarkup;
return "";
}
private bool FileIsExcel (string fileName, string contentType) {
var excelContentTypes = new List<string> {
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
};

if (excelContentTypes.Contains(contentType)) return true;

return MatchFileExtension(new List<string>{"xls", "xlsx"}, fileName);
}

private bool FileIsODS (string fileName, string contentType) {
var excelContentTypes = new List<string> {
"application/vnd.oasis.opendocument.spreadsheet"
};

if (excelContentTypes.Contains(contentType)) return true;

return MatchFileExtension(new List<string>{"ods"}, fileName);
}

private bool FileIsWord (string fileName, string contentType) {
var wordContentTypes = new List<string> {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
};

if (wordContentTypes.Contains(contentType)) return true;

return MatchFileExtension(new List<string>{"doc", "docx"}, fileName);
}

private bool MatchFileExtension (List<string> extensions, string fileName) {
bool match = false;
extensions.ForEach(extension => {
if (Regex.IsMatch(fileName,@"\." + extension + "$", RegexOptions.None, TimeSpan.FromMilliseconds(100))) match = true;
});
return match;
}
}

@{
bool fileIsOds = FileIsODS(Model.Asset.File.FileName, Model.Asset.File.ContentType);
}

<section class="gem-c-attachment govuk-!-margin-bottom-6" data-module="ga4-link-tracker" data-ga4-link-tracker-module-started="true">
<div class="gem-c-attachment__thumbnail govuk-!-display-none-print">
<a class="govuk-link" target="_self" tabindex="-1" aria-hidden="true" data-ga4-link="{&quot;event_name&quot;:&quot;file_download&quot;,&quot;type&quot;:&quot;attachment&quot;}" href="@Model.Asset.File.Url">
@Html.Raw(@FileTypeIconSVG(@Model.Asset.File.FileName, @Model.Asset.File.ContentType))
</a>
</div>
<div class="gem-c-attachment__details">
<h3 class="gem-c-attachment__title">
<a class="govuk-link gem-c-attachment__link" target="_self" data-ga4-link="{&quot;event_name&quot;:&quot;file_download&quot;,&quot;type&quot;:&quot;attachment&quot;}" href="@Model.Asset.File.Url">
@Model.LinkText
</a>
</h3>
<p class="gem-c-attachment__metadata">
<span class="gem-c-attachment__attribute">@Html.Raw(@FileTypeToHumanReadable(@Model.Asset.File.FileName, @Model.Asset.File.ContentType))</span>, <span class="gem-c-attachment__attribute">@FileSizeToHumanReadable(@Model.Asset.File.Details.Size)</span>
</p>

@if (fileIsOds)
{
<p class="gem-c-attachment__metadata">
This file is in an <a href="https://www.gov.uk/guidance/using-open-document-formats-odf-in-your-organisation" target="_blank" rel="noopener" class="govuk-link">OpenDocument</a> format
</p>
}

<p class="gem-c-attachment__metadata">This file may not be suitable for users of assistive technology.</p>
<details data-module="ga4-event-tracker" data-ga4-event="{&quot;event_name&quot;:&quot;select_content&quot;,&quot;type&quot;:&quot;detail&quot;,&quot;text&quot;:&quot;Request an accessible format.&quot;,&quot;section&quot;:&quot;Request an accessible format.&quot;,&quot;index_section&quot;:1,&quot;index_section_count&quot;:1}" class="gem-c-details govuk-details govuk-!-margin-bottom-3" data-ga4-event-tracker-module-started="true">
<summary class="govuk-details__summary">
<span class="govuk-details__summary-text" data-ga4-expandable="">Request an accessible format</span>
</summary>
<div class="govuk-details__text">
If you use assistive technology (such as a screen reader) and need a version of this document in a more accessible format, please email <a href="mailto:develop-child-family-social-work-career-team@digital.education.gov.uk" target="_blank" class="govuk-link">develop-child-family-social-work-career-team@digital.education.gov.uk</a>. Please tell us what format you need. It will help us if you say what assistive technology you use.
</div>
</details>
</div>
</section>
148 changes: 148 additions & 0 deletions Childrens-Social-Care-CPD/styles/scss/overrides/_dfe-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -544,4 +544,152 @@ CONTENT TITLE BANNER
top: 0;
right: 0;
bottom: 0;
}

/* Asset Downloads */
.gem-c-attachment-link__abbr {
text-decoration: none;
cursor: help;
}

.gem-c-attachment {
font-family: "GDS Transport", arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 400;
font-size: 16px;
font-size: 1rem;
line-height: 1.25;
position: relative;
}
@media (min-width: 40.0625em) {
.gem-c-attachment {
font-size: 19px;
font-size: 1.1875rem;
line-height: 1.3158;
}
}
.gem-c-attachment::after {
content: "";
display: block;
clear: both;
}
.gem-c-attachment__thumbnail {
position: relative;
width: auto;
margin-right: 25px;
margin-bottom: 15px;
padding: 5px;
float: left;
}
@media (min-width: 40.0625em) {
.gem-c-attachment__thumbnail {
position: relative;
width: 140px;
height: auto;
max-width: 140px;
margin-right: 20px;
margin-bottom: 15px;
padding: 5px;
float: left;
}
}

.gem-c-attachment__thumbnail-image {
display: block;
max-width: 280px;
width: 140px;
height: auto;
border: rgba(11, 12, 12, 0.1);
outline: 5px solid rgba(11, 12, 12, 0.1);
background: #ffffff;
box-shadow: 0 2px 2px rgba(11, 12, 12, 0.4);
fill: #b1b4b6;
stroke: #b1b4b6;
}

.gem-c-attachment__details {
padding-left: 164px;
word-break: break-word;
word-wrap: break-word;
}

.gem-c-attachment__title {
font-family: "GDS Transport", arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 400;
font-size: 18px;
font-size: 1.125rem;
line-height: 1.1111;
margin: 0 0 15px;
}
@media (min-width: 40.0625em) {
.gem-c-attachment__title {
font-size: 27px;
font-size: 1.6875rem;
line-height: 1.1111;
}
}

.gem-c-attachment__metadata {
font-family: "GDS Transport", arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 400;
font-size: 16px;
font-size: 1rem;
line-height: 1.25;
margin: 0 0 15px;
color: #505a5f;
}
@media (min-width: 40.0625em) {
.gem-c-attachment__metadata {
font-size: 19px;
font-size: 1.1875rem;
line-height: 1.3158;
}
}
.gem-c-attachment__metadata:last-of-type {
margin-bottom: 0;
}

.gem-c-attachment__metadata .gem-c-attachment__attribute {
word-wrap: break-word;
overflow-wrap: break-word;
}

.gem-c-attachment__metadata--compact {
margin-bottom: 0;
}

.gem-c-attachment__link {
line-height: 1.29;
}

.gem-c-attachment__abbr {
text-decoration: none;
cursor: help;
}

.gem-c-attachment .govuk-details {
margin: 15px 0;
}

@media print {
.gem-c-attachment {
font-family: sans-serif;
font-size: 14pt;
line-height: 1.15;
}
.gem-c-attachment__title {
font-family: sans-serif;
font-size: 18pt;
line-height: 1.15;
}
.gem-c-attachment__metadata {
font-family: sans-serif;
font-size: 14pt;
line-height: 1.15;
}
}
Loading

0 comments on commit 0dbff12

Please sign in to comment.