Skip to content

Commit

Permalink
Merge pull request #23 from ministryofjustice/story/CCMSPUI-466-creat…
Browse files Browse the repository at this point in the history
…e-details-component

CCMSPUI-466 | create details component
  • Loading branch information
arunkumar461 authored Dec 16, 2024
2 parents 8e52869 + 9171ee7 commit 0ece335
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,23 @@ This ensures that all attributes, including conditional and computed values, are
<govuk:button th:text="'Click Me!'" href="'/test'" id="'button-id'" classes="'custom-class'"/>
</body>
</html>
```
```

### Details Element Tag Processor

The `DetailsElementTagProcessor` is a custom Thymeleaf tag processor that enables the use of a `<govuk:details>` tag to generate a `<details>` HTML element styled with the GOV.UK Design System classes.

#### Features
- Generates a `<details>` element with the `govuk-details` class.
- Includes a `<summary>` element with a customizable summary text.
- Includes a `<div>` element for detailed content.

#### Usage
To use this processor, define a `govuk:details` tag in your Thymeleaf templates and provide the following attributes:

- **`summaryText`**: The text displayed in the summary section of the `<details>` element.
- **`text`**: The content displayed inside the `<div>` when the details are expanded.

#### Example
```html
<govuk:details summaryText="Click to view details" text="This is the detailed content."></govuk:details>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package uk.gov.laa.ccms.springboot.dialect;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.logging.log4j.util.Strings;
Expand All @@ -10,11 +9,9 @@
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractElementTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.templatemode.TemplateMode;


/**
* Transforms <govuk:button/> elements into standard HTML button elements.
*/
Expand All @@ -32,7 +29,7 @@ public ButtonElementTagProcessor() {
protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
IElementTagStructureHandler structureHandler) {

Map<String, String> attributes = parseAttributes(context, tag);
Map<String, String> attributes = ProcessorUtils.parseAttributes(context, tag);
String classNames = buildClassNames(attributes);
String commonAttributes = buildCommonAttributes(classNames, attributes);
String buttonAttributes = buildButtonAttributes(attributes);
Expand All @@ -43,26 +40,6 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
replaceElementWithHtml(context, structureHandler, html);
}

private Map<String, String> parseAttributes(ITemplateContext context,
IProcessableElementTag tag) {
Map<String, String> attributes = tag.getAttributeMap();
Map<String, String> resolvedAttributes = new HashMap<>();
IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(context.getConfiguration());

for (Map.Entry<String, String> entry : attributes.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.startsWith("th:")) {
IStandardExpression expression = parser.parseExpression(context, value);
resolvedAttributes.put(key.replace("th:", ""), (String) expression.execute(context));
} else {
resolvedAttributes.put(key, value);
}
}

return resolvedAttributes;
}

private String buildClassNames(Map<String, String> attributes) {
String classNames = "govuk-button";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package uk.gov.laa.ccms.springboot.dialect;

import java.util.Map;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractElementTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.templatemode.TemplateMode;

/**
* Transforms <govuk:button/> elements into standard HTML button elements.
*/
public class DetailsElementTagProcessor extends AbstractElementTagProcessor {

private static final String TAG_NAME = "details";
private static final int PRECEDENCE = 900;

public DetailsElementTagProcessor() {
super(TemplateMode.HTML, "govuk", TAG_NAME, true, null, false, PRECEDENCE);
}

@Override
protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
IElementTagStructureHandler structureHandler) {

// Parse attributes
Map<String, String> attributes = ProcessorUtils.parseAttributes(context, tag);
String summaryText = attributes.getOrDefault("summaryText", "");
String text = attributes.getOrDefault("text", "");

// Build the HTML structure
String detailsHtml = buildDetailsHtml(summaryText, text);

// Create the model and replace the tag
final IModelFactory modelFactory = context.getModelFactory();
final IModel model = modelFactory.parse(context.getTemplateData(), detailsHtml);
structureHandler.replaceWith(model, false);
}

private String buildDetailsHtml(String summaryText, String text) {
return new StringBuilder()
.append("<details class=\"").append("govuk-details").append("\">")
.append("<summary class=\"").append("govuk-details__summary").append("\">")
.append("<span class=\"").append("govuk-details__summary-text").append("\">")
.append(summaryText)
.append("</span>")
.append("</summary>")
.append("<div class=\"").append("govuk-details__text").append("\">")
.append(text)
.append("</div>")
.append("</details>")
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public GovUkDialect() {

@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
return Set.of(new ButtonElementTagProcessor());
return Set.of(new ButtonElementTagProcessor(), new DetailsElementTagProcessor());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package uk.gov.laa.ccms.springboot.dialect;

import java.util.HashMap;
import java.util.Map;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions;

/**
* ProcessorUtils for common code.
*/
public class ProcessorUtils {

private ProcessorUtils() {
}

/**
* Evaluate thymeleaf expressions.
*/
public static Map<String, String> parseAttributes(ITemplateContext context,
IProcessableElementTag tag) {
Map<String, String> attributes = tag.getAttributeMap();
Map<String, String> resolvedAttributes = new HashMap<>();
IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(context.getConfiguration());

for (Map.Entry<String, String> entry : attributes.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.startsWith("th:")) {
IStandardExpression expression = parser.parseExpression(context, value);
resolvedAttributes.put(key.replace("th:", ""), (String) expression.execute(context));
} else {
resolvedAttributes.put(key, value);
}
}

return resolvedAttributes;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package uk.gov.laa.ccms.springboot.dialect;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

@SpringBootTest(classes = ThymeleafTestConfig.class)
class DetailsElementTagProcessorTest {

@Autowired
private SpringTemplateEngine templateEngine;

@Test
void shouldRenderGovukButton() {

Context context = new Context();
String renderedHtml = templateEngine.process("test-details", context);
assertThat(renderedHtml)
.contains(
"<details class=\"govuk-details\"><summary class=\"govuk-details__summary\">" +
"<span class=\"govuk-details__summary-text\">Help with nationality</span>" +
"</summary><div class=\"govuk-details__text\">We need to know your nationality " +
"so we can work out which elections you're entitled to vote in.</div></details>");

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html xmlns:govuk="http://www.thymeleaf.org" lang="EN">
<head>
<title>Details Test</title>
</head>
<body>

<govuk:details summaryText="Help with nationality"
text="We need to know your nationality so we can work out which elections you're entitled to vote in."/>

</body>
</html>

0 comments on commit 0ece335

Please sign in to comment.