{"componentChunkName":"component---src-templates-post-js","path":"/all-good-things/","result":{"data":{"mdx":{"id":"5281c6af-0f8a-5467-ba28-38bc8601bbbb","body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"All Good Things...\",\n  \"date\": \"2021-03-20T00:00:00.000Z\",\n  \"featuredImage\": \"testimonial-carousel.png\",\n  \"thumbnail\": \"testimonial-carousel.png\",\n  \"caption\": \"Nothing but good things to say\",\n  \"description\": \"You may think this post is about how I left an awesome team at Jefferson last month for an exciting new opportunity at the American Red Cross, and you would be partially correct. 🖖\",\n  \"published\": true\n};\n\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\n\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Eight Great Years\"), mdx(\"p\", null, \"You may think this post is about how I left an awesome team at Jefferson last month for an exciting new opportunity at the American Red Cross, and you would be partially correct. It has been an honor and privilege to work alongside some of the most amazing colleagues one could ask for. I give all the respect in the world to the front-line clinical workers who risk their own well-being for the rest of us. These people truly do improve lives. My hope is that even in some small way the work we\\u2019ve done together has improved patient access to information and appointments, indeed removing some friction from what can be a stressful ordeal.\"), mdx(\"p\", null, \"If you're a nerd of a certain caliber, you'll also recognize that I've used the same title as the final episode of \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Star Trek: The Next Generation\"), \", one of my all-time favorite shows. Captain Picard is by far the best captain of the Starship Enterprise. Don't @ me.\"), mdx(\"p\", null, \"All that being said, what this post is really about is the final project I worked on in my final sprint at Jefferson: a carousel of patient testionials that is authorable in Adobe Experience Manager and utilizes a \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://experienceleague.adobe.com/docs/experience-manager-65/assets/fragments/content-fragments-models.html\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"content fragment model\"), \" and \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://sling.apache.org/documentation/bundles/models.html\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"Sling Models\"), \".\"), mdx(\"h2\", null, \"Testimonial Carousel\"), mdx(\"p\", null, \"With the previously-built solution for displaying patient testimonials, content was added to the properties at the component instance, meaning that if authors wanted to reuse the same testimonial on multiple pages, there was a lot of copying and pasting involved. Without even considering the carousel yet, this was a very non-\", mdx(\"abbr\", {\n    title: \"Don't Repeat Yourself\"\n  }, \"DRY\"), \" way to author content, and just an annoying user experience for the authors anyway.\"), mdx(\"p\", null, \"My goal was to convert this to use a folder in the \", mdx(\"abbr\", {\n    title: \"Digital Asset Manager\"\n  }, \"DAM\"), \" as a central repository for all testimonial content that could be referenced individually or as a group. The best candidate for the job turned out to be the AEM \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://experienceleague.adobe.com/docs/experience-manager-65/assets/fragments/content-fragments.html\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"Content Fragment\"), \".\"), mdx(\"p\", null, \"The best tutorial I could find for using content fragment models that I would eventually want to iterate through turned out to be in a \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://engineering.icf.com/aem-content-fragments-in-the-wild/\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"blog post\"), \" instead of any official Adobe documentation. Much of the code for the single content fragment model borrows from their code.\"), mdx(\"h3\", null, \"Sling Model for Single Content Fragment\"), mdx(\"p\", null, \"I realize that to an experienced Java developer this probably looks like \\\"Baby's First Sling Model\\\" but it's the first time I've built something like this so it's a milestone for me. Basically, this model takes either a string of the path to a content fragment resource that uses this model, or a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ContentFragment\"), \" resource itself, as in the case of the next model for iterating through multiple testimonials for a carousel, and provides several \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"get\"), \" methods for retrieving the initials, name, location, and quote, from the model, as well as the model itself.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-java\"\n  }), \"// CFTestimonial.java\\n\\n// package information and imports...\\n\\n@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)\\npublic class CFTestimonial {\\n\\n    @Inject @Self\\n    private Resource resource;\\n    @Inject\\n    ResourceResolver resourceResolver;\\n    private Optional<ContentFragment> contentFragment;\\n\\n    @Inject \\n    private String path;\\n\\n    @PostConstruct\\n    public void init() {\\n        if(path != null) {\\n            Resource fragmentResource = resourceResolver.getResource(path);\\n            contentFragment = Optional.ofNullable(fragmentResource.adaptTo(ContentFragment.class));\\n        }\\n        else if(resource != null) {\\n            contentFragment = Optional.ofNullable(resource.adaptTo(ContentFragment.class));\\n        }\\n    }\\n\\n    public String getModel() {\\n        return contentFragment\\n            .map(ContentFragment::getTemplate)\\n            .map(FragmentTemplate::getTitle)\\n            .orElse(\\\"\\\");\\n    }\\n\\n    public String getInitials() {\\n        return contentFragment\\n            .map(cf -> cf.getElement(\\\"initials\\\"))\\n            .map(ContentElement::getContent)\\n            .orElse(\\\"\\\");\\n    }\\n\\n    public String getName() {\\n        return contentFragment\\n            .map(cf -> cf.getElement(\\\"name\\\"))\\n            .map(ContentElement::getContent)\\n            .orElse(\\\"\\\");\\n    }\\n\\n    public String getLocation() {\\n        return contentFragment\\n            .map(cf -> cf.getElement(\\\"location\\\"))\\n            .map(ContentElement::getContent)\\n            .orElse(\\\"\\\");\\n    }\\n\\n    public String getQuote() {\\n        return contentFragment\\n            .map(cf -> cf.getElement(\\\"testimonial\\\"))\\n            .map(ContentElement::getContent)\\n            .orElse(\\\"\\\");\\n    }\\n\\n}\\n\")), mdx(\"h3\", null, \"Sling Model for Iterating Content Fragments\"), mdx(\"p\", null, \"Where \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://engineering.icf.com/aem-content-fragments-in-the-wild/\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"the solution I was referencing\"), \" started to break down was when I decided to have the carousel populated by paths rather than be tag-driven like the example. As such, most of this model is my own code. The following model takes an array of paths to content fragments that use the custom testimonial model and iterates through them. Those paths are specified by the author in the properties of the testimonial carousel component.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-java\"\n  }), \"// CFTestimonialCarousel.java\\n\\n// package information and imports...\\n\\n@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)\\npublic class CFTestimonialCarousel {\\n\\n    @SlingObject\\n    private Resource currentResource;\\n\\n    @SlingObject\\n    private ResourceResolver resourceResolver;\\n\\n    @Inject\\n    private Resource paths;\\n\\n    private final List<CFTestimonial> testimonials = new ArrayList<>();\\n\\n    @PostConstruct\\n    protected void init() {\\n      if(paths != null) {\\n        Iterator<Resource> pathsIterator = paths.listChildren();\\n        while(pathsIterator.hasNext()) {\\n          Resource path             = pathsIterator.next();\\n          ValueMap pathProperties   = path.adaptTo(ValueMap.class);\\n          String cfPath             = pathProperties.get(\\\"path\\\",\\\"\\\");\\n          Resource cfResource       = resourceResolver.getResource(cfPath);\\n          ContentFragment cf        = cfResource.adaptTo(ContentFragment.class);\\n          final CFTestimonial cfTestimonial = cfResource.adaptTo(CFTestimonial.class);\\n          if (cfTestimonial != null) {\\n            testimonials.add(cfTestimonial);\\n          }\\n        }\\n      }\\n    }\\n    public List<CFTestimonial> getTestimonials() {\\n      return testimonials;\\n    }\\n}\\n\")), mdx(\"h3\", null, \"HTL\"), mdx(\"p\", null, \"With the \\\"hard part\\\" out of the way, all that's left is to write is some HTL to render the ArrayList of testimonials, and then make it come alive with some CSS and JavaScript in the component's client library.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-html\"\n  }), \"<sly data-sly-use.testimonialCarousel=\\\"tju.core.models.CFTestimonialCarousel\\\" />\\n\\n<!--/* check for testimonials */-->\\n<div data-sly-test=\\\"${!testimonialCarousel.testimonials.size}\\\" style=\\\"height: 100px; text-align: center; padding: 25px;\\\">\\n  Please choose at least one testimonial content fragment. Choose three or more for best results.\\n</div>\\n<div data-sly-test=\\\"${testimonialCarousel.testimonials.size}\\\" class=\\\"testimonial-carousel-container\\\">\\n  <div class=\\\"header-title\\\">\\n    <h2 class=\\\"title-text\\\">Testimonials</h2>\\n    <p>See what our clients have to say about us</p>\\n  </div>\\n  <div class=\\\"carousel\\\">\\n    <div class=\\\"prev\\\"></div>\\n    <!--/* iterate testimonials */-->\\n    <div data-sly-list.testimonial=\\\"${testimonialCarousel.testimonials}\\\" class=\\\"testimonials\\\">\\n      <div class=\\\"carousel-testimonial\\\">\\n        <div class=\\\"testimonial-initials\\\">${testimonial.initials}</div>\\n        <div class=\\\"testimonial-content\\\">\\n          <div class=\\\"testimonial-name-and-location\\\">\\n            <div class=\\\"testimonial-name\\\">${testimonial.name}</div>\\n            <div class=\\\"testimonial-location\\\" data-sly-test=\\\"${testimonial.location}\\\">${testimonial.location}</div>\\n          </div>\\n          <div class=\\\"testimonial-quote\\\">${testimonial.quote @ context='html'}</div>\\n        </div>\\n      </div>\\n    </div>\\n    <div class=\\\"next\\\"></div>\\n  </div>\\n  <!-- /* iterate nav */ -->\\n  <div data-sly-list.testimonial=\\\"${testimonialCarousel.testimonials}\\\" class=\\\"testimonial-nav\\\">\\n    <button \\n      class=\\\"testimonial-navitem\\\" \\n      aria-label=\\\"go to testimonial from ${testimonial.name}\\\" \\n      onclick=\\\"gotoTestimonial(${testimonialList.index @ context='html'})\\\"\\n    >\\n      <span>${testimonial.name}</span>\\n    </button>\\n  </div>\\n</div>\\n\")), mdx(\"h2\", null, \"The Final Product\"), mdx(\"p\", null, \"And finally\\u2014no really!\\u2014this is how the carousel renders on the front end. My former colleague from Jefferson, \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"http://claytonleggdesign.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"Clayton Legg\"), \", did a great job with the design and initial concept of this component, among many others. Thanks to \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://baconipsum.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"nofollow noopener noreferrer\"\n  }), \"Bacon Ipsum\"), \" for the delicious filler content. Feel free to expand the CodePen and take a look around at my HTML, CSS, and JS.\"), mdx(\"iframe\", {\n    height: \"480\",\n    style: {\n      \"width\": \"100%\"\n    },\n    scrolling: \"no\",\n    title: \"Testimonial Carousel\",\n    src: \"https://codepen.io/bhamburg/embed/eYByoWE?height=800&theme-id=dark&default-tab=result&zoom=0.5\",\n    frameBorder: \"no\",\n    loading: \"lazy\",\n    allowtransparency: \"true\",\n    allowFullScreen: \"true\"\n  }, \"See the Pen \", mdx(\"a\", {\n    href: \"https://codepen.io/bhamburg/pen/eYByoWE\"\n  }, \"Testimonial Carousel\"), \" by Brian Hamburg (\", mdx(\"a\", {\n    href: \"https://codepen.io/bhamburg\"\n  }, \"@bhamburg\"), \") on \", mdx(\"a\", {\n    href: \"https://codepen.io\"\n  }, \"CodePen\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"All Good Things...","date":"Saturday, March 20, 2021","description":"You may think this post is about how I left an awesome team at Jefferson last month for an exciting new opportunity at the American Red Cross, and you would be partially correct. 🖖","caption":"Nothing but good things to say","featuredImage":{"childImageSharp":{"sizes":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC30lEQVQoz32SbWhTZxTHH3EyN+YYE0Q/CIr4wU/iwKnoFOcnrba+1Tl11hcUX/CF1kjU+Q5Sq61Ta/3SCW3RKHSxVkXRWsvarFpbq12IoCUmNjFp3pp7c+/NTXqTnzftvgn7w8PzP89z+J9z+B+xcvIC1OqjhO7aUS44US4rKGdVYtYEXVt7+aOwnqL8UyzLK2ZpXgkFy6ws/6WKgp1NrCh7ySyHn6WrT7Dnq1U0LbyGmPntDGJVFmJPawi/foesRkmoCmpSRdVUZEVhYCCOLMkoCZW0nsJID5JSNXJovvGIVeJn/pp5guyeasR4MRH3yd0kX5XT3/uBz5BKEvzgwe9xk9EUyGaH3/+7WuoecuT7QuK7D0JtGWL2l6Np/G0dkvN3nO+7yRhZs6MY0UiEPp+fB2/8NPR4ufevlwZnH+4+P9FwmEg4MiTosv9NZ/4iuL0BqbUScebHb7BMn0fUsY9n/T10d77i3PnzdL/sovxSJbOqn3PFJWN3K8y+4eJQ2UUzp4OrVVU8bGlD63pMsnI8ibYj5K05gLD9Op+iMZPx1hTT425HTxoEAwEkSSIUCqGl0ih6GknTSQ0aRKJRdF0nPhBFTkHSWYNxdw4WyxnE/IuILT9tp3TsFOybthHq7UQ3x1BkCa/Xi6qqQ2PlhAMfPw7xdDqNz+dDjkcx9dDcN3H9uZYJa2oQG+4jvp6xi+tTCzk2qYBwx2syZtJALEYwGCSRSJhOa3g8Ht6bJxfLsjwU9wf9pHMVfA3YKw4i1t7ni53tCDHXSv4Pp9k/8Rx1tU3DXZijZUzlrGmQYXJNHV6hQcPAMD+yptPZjMlzZgc6eHTlLKO2PGVEsQsxYnEFoqCConHV3Crr+mxrNP4fclwncMfGtMMtCGsAMXK9DbHRxnfLazm5oBXrrtuU1DZzrfQtrZvdWAqtrFuSx9aSvRyvr8NW30jbEwf/NLfjaHbgaHqBVL6DU1YLojTFJ/zwvEvrID93AAAAAElFTkSuQmCC","aspectRatio":1.834862385321101,"src":"/static/dbce61f113583b4988f00f7ea07e5139/bc8e0/testimonial-carousel.png","srcSet":"/static/dbce61f113583b4988f00f7ea07e5139/8ac63/testimonial-carousel.png 200w,\n/static/dbce61f113583b4988f00f7ea07e5139/3891b/testimonial-carousel.png 400w,\n/static/dbce61f113583b4988f00f7ea07e5139/bc8e0/testimonial-carousel.png 800w,\n/static/dbce61f113583b4988f00f7ea07e5139/6050d/testimonial-carousel.png 1200w,\n/static/dbce61f113583b4988f00f7ea07e5139/5b479/testimonial-carousel.png 1320w","srcWebp":"/static/dbce61f113583b4988f00f7ea07e5139/ccdb5/testimonial-carousel.webp","srcSetWebp":"/static/dbce61f113583b4988f00f7ea07e5139/6b183/testimonial-carousel.webp 200w,\n/static/dbce61f113583b4988f00f7ea07e5139/fc32b/testimonial-carousel.webp 400w,\n/static/dbce61f113583b4988f00f7ea07e5139/ccdb5/testimonial-carousel.webp 800w,\n/static/dbce61f113583b4988f00f7ea07e5139/9000d/testimonial-carousel.webp 1200w,\n/static/dbce61f113583b4988f00f7ea07e5139/97392/testimonial-carousel.webp 1320w","sizes":"(max-width: 800px) 100vw, 800px"}}},"thumbnail":{"childImageSharp":{"sizes":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC30lEQVQoz32SbWhTZxTHH3EyN+YYE0Q/CIr4wU/iwKnoFOcnrba+1Tl11hcUX/CF1kjU+Q5Sq61Ta/3SCW3RKHSxVkXRWsvarFpbq12IoCUmNjFp3pp7c+/NTXqTnzftvgn7w8PzP89z+J9z+B+xcvIC1OqjhO7aUS44US4rKGdVYtYEXVt7+aOwnqL8UyzLK2ZpXgkFy6ws/6WKgp1NrCh7ySyHn6WrT7Dnq1U0LbyGmPntDGJVFmJPawi/foesRkmoCmpSRdVUZEVhYCCOLMkoCZW0nsJID5JSNXJovvGIVeJn/pp5guyeasR4MRH3yd0kX5XT3/uBz5BKEvzgwe9xk9EUyGaH3/+7WuoecuT7QuK7D0JtGWL2l6Np/G0dkvN3nO+7yRhZs6MY0UiEPp+fB2/8NPR4ufevlwZnH+4+P9FwmEg4MiTosv9NZ/4iuL0BqbUScebHb7BMn0fUsY9n/T10d77i3PnzdL/sovxSJbOqn3PFJWN3K8y+4eJQ2UUzp4OrVVU8bGlD63pMsnI8ibYj5K05gLD9Op+iMZPx1hTT425HTxoEAwEkSSIUCqGl0ih6GknTSQ0aRKJRdF0nPhBFTkHSWYNxdw4WyxnE/IuILT9tp3TsFOybthHq7UQ3x1BkCa/Xi6qqQ2PlhAMfPw7xdDqNz+dDjkcx9dDcN3H9uZYJa2oQG+4jvp6xi+tTCzk2qYBwx2syZtJALEYwGCSRSJhOa3g8Ht6bJxfLsjwU9wf9pHMVfA3YKw4i1t7ni53tCDHXSv4Pp9k/8Rx1tU3DXZijZUzlrGmQYXJNHV6hQcPAMD+yptPZjMlzZgc6eHTlLKO2PGVEsQsxYnEFoqCConHV3Crr+mxrNP4fclwncMfGtMMtCGsAMXK9DbHRxnfLazm5oBXrrtuU1DZzrfQtrZvdWAqtrFuSx9aSvRyvr8NW30jbEwf/NLfjaHbgaHqBVL6DU1YLojTFJ/zwvEvrID93AAAAAElFTkSuQmCC","aspectRatio":1.834862385321101,"src":"/static/dbce61f113583b4988f00f7ea07e5139/bc8e0/testimonial-carousel.png","srcSet":"/static/dbce61f113583b4988f00f7ea07e5139/8ac63/testimonial-carousel.png 200w,\n/static/dbce61f113583b4988f00f7ea07e5139/3891b/testimonial-carousel.png 400w,\n/static/dbce61f113583b4988f00f7ea07e5139/bc8e0/testimonial-carousel.png 800w,\n/static/dbce61f113583b4988f00f7ea07e5139/6050d/testimonial-carousel.png 1200w,\n/static/dbce61f113583b4988f00f7ea07e5139/5b479/testimonial-carousel.png 1320w","srcWebp":"/static/dbce61f113583b4988f00f7ea07e5139/ccdb5/testimonial-carousel.webp","srcSetWebp":"/static/dbce61f113583b4988f00f7ea07e5139/6b183/testimonial-carousel.webp 200w,\n/static/dbce61f113583b4988f00f7ea07e5139/fc32b/testimonial-carousel.webp 400w,\n/static/dbce61f113583b4988f00f7ea07e5139/ccdb5/testimonial-carousel.webp 800w,\n/static/dbce61f113583b4988f00f7ea07e5139/9000d/testimonial-carousel.webp 1200w,\n/static/dbce61f113583b4988f00f7ea07e5139/97392/testimonial-carousel.webp 1320w","sizes":"(max-width: 800px) 100vw, 800px"}}}}}},"pageContext":{"id":"5281c6af-0f8a-5467-ba28-38bc8601bbbb"}},"staticQueryHashes":["284470141","3649515864","63159454"]}